322 lines
9.5 KiB
PowerShell
322 lines
9.5 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[switch]$ExemptMiri,
|
|
[string]$MiriExemptionReason,
|
|
[switch]$ExemptFuzz,
|
|
[string]$FuzzExemptionReason,
|
|
[switch]$ExemptSanitizers,
|
|
[string]$SanitizersExemptionReason,
|
|
[switch]$ExemptNoPanic,
|
|
[string]$NoPanicExemptionReason,
|
|
[switch]$ExemptLoom,
|
|
[string]$LoomExemptionReason,
|
|
[string[]]$MiriPackages = @('common'),
|
|
[string[]]$SanitizerPackages = @('common'),
|
|
[ValidateSet('All', 'Miri', 'Fuzz', 'Sanitizers', 'NoPanic', 'Loom')]
|
|
[string]$Only = 'All',
|
|
[string]$NightlyToolchain = 'nightly',
|
|
[ValidateRange(1, 3600)]
|
|
[int]$FuzzSeconds = 10
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-StrictMode -Version Latest
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
|
|
$workspaceRoot = Split-Path -Parent $PSScriptRoot
|
|
$fuzzManifest = Join-Path $workspaceRoot 'fuzz/Cargo.toml'
|
|
$fuzzArtifactRoot = Join-Path $workspaceRoot 'target/jade-fuzz/artifacts'
|
|
Push-Location -LiteralPath $workspaceRoot
|
|
|
|
$script:StepResults = [System.Collections.Generic.List[object]]::new()
|
|
|
|
function Add-StepResult {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name,
|
|
[Parameter(Mandatory = $true)]
|
|
[double]$DurationSeconds,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Mode
|
|
)
|
|
|
|
$script:StepResults.Add([pscustomobject]@{
|
|
Step = $Name
|
|
Seconds = [math]::Round($DurationSeconds, 2)
|
|
Mode = $Mode
|
|
})
|
|
}
|
|
|
|
function Test-NativeCommand {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name
|
|
)
|
|
|
|
return $null -ne (Get-Command -Name $Name -ErrorAction SilentlyContinue)
|
|
}
|
|
|
|
function Resolve-AsanRuntimeDirectory {
|
|
if (-not $IsWindows) {
|
|
return $null
|
|
}
|
|
|
|
$targetLibdir = & cargo "+$NightlyToolchain" rustc --print target-libdir 2>$null
|
|
if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($targetLibdir)) {
|
|
foreach ($name in @('clang_rt.asan_dynamic-x86_64.dll', 'libclang_rt.asan_dynamic-x86_64.dll')) {
|
|
$candidate = Join-Path $targetLibdir $name
|
|
if (Test-Path -LiteralPath $candidate) {
|
|
return (Split-Path -Parent $candidate)
|
|
}
|
|
}
|
|
}
|
|
|
|
$roots = @(
|
|
${env:ProgramFiles(x86)},
|
|
$env:ProgramFiles,
|
|
(Join-Path $env:USERPROFILE 'scoop'),
|
|
(Join-Path $env:USERPROFILE '.rustup')
|
|
) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) -and (Test-Path -LiteralPath $_) }
|
|
|
|
foreach ($root in $roots) {
|
|
$candidate = Get-ChildItem -LiteralPath $root -Recurse -File -Filter 'clang_rt.asan_dynamic-x86_64.dll' -ErrorAction SilentlyContinue |
|
|
Select-Object -First 1
|
|
if ($null -ne $candidate) {
|
|
return $candidate.DirectoryName
|
|
}
|
|
|
|
$mingwCandidate = Get-ChildItem -LiteralPath $root -Recurse -File -Filter 'libclang_rt.asan_dynamic-x86_64.dll' -ErrorAction SilentlyContinue |
|
|
Select-Object -First 1
|
|
if ($null -ne $mingwCandidate) {
|
|
return $mingwCandidate.DirectoryName
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Add-AsanRuntimeToPath {
|
|
if (-not $IsWindows) {
|
|
return
|
|
}
|
|
|
|
$runtimeDirectory = Resolve-AsanRuntimeDirectory
|
|
if ([string]::IsNullOrWhiteSpace($runtimeDirectory)) {
|
|
Deny-MissingGate -Name 'Sanitizers' -Reason 'Windows ASan runtime DLL was not found in the active nightly target-libdir, PATH, or known compiler install roots'
|
|
}
|
|
|
|
$pathParts = $env:PATH -split ';'
|
|
if ($pathParts -notcontains $runtimeDirectory) {
|
|
$env:PATH = "${runtimeDirectory};$env:PATH"
|
|
}
|
|
}
|
|
|
|
function Confirm-Exemption {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name,
|
|
[Parameter(Mandatory = $true)]
|
|
[bool]$Requested,
|
|
[string]$Reason
|
|
)
|
|
|
|
if (-not $Requested) {
|
|
return $false
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Reason)) {
|
|
throw "${Name} exemption requires a non-empty reason."
|
|
}
|
|
|
|
Write-Warning "${Name} explicitly exempted: ${Reason}"
|
|
Add-StepResult -Name $Name -DurationSeconds 0 -Mode 'exempted'
|
|
return $true
|
|
}
|
|
|
|
function Invoke-TimedNative {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$FilePath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$ArgumentList
|
|
)
|
|
|
|
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
|
|
& $FilePath @ArgumentList
|
|
$exitCode = $LASTEXITCODE
|
|
$stopwatch.Stop()
|
|
Add-StepResult -Name $Name -DurationSeconds $stopwatch.Elapsed.TotalSeconds -Mode 'hardening'
|
|
|
|
if ($exitCode -ne 0) {
|
|
throw "Command failed with exit code ${exitCode}: $FilePath $($ArgumentList -join ' ')"
|
|
}
|
|
}
|
|
|
|
function Invoke-TimedNativeNoStdout {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$FilePath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$ArgumentList
|
|
)
|
|
|
|
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
|
|
& $FilePath @ArgumentList > $null
|
|
$exitCode = $LASTEXITCODE
|
|
$stopwatch.Stop()
|
|
Add-StepResult -Name $Name -DurationSeconds $stopwatch.Elapsed.TotalSeconds -Mode 'hardening'
|
|
|
|
if ($exitCode -ne 0) {
|
|
throw "Command failed with exit code ${exitCode}: $FilePath $($ArgumentList -join ' ')"
|
|
}
|
|
}
|
|
|
|
function Deny-MissingGate {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Reason
|
|
)
|
|
|
|
throw "${Name} gate is required by Jade and did not run: ${Reason}. First run 'just jade-tools' or 'pwsh -NoProfile -File .\scripts\install-jade-tooling.ps1' and install the missing platform prerequisite. Use -Exempt${Name} with a reason only for a reviewed, narrow exemption."
|
|
}
|
|
|
|
function Invoke-MiriGate {
|
|
if (Confirm-Exemption -Name 'Miri' -Requested $ExemptMiri.IsPresent -Reason $MiriExemptionReason) {
|
|
return
|
|
}
|
|
if (-not (Test-NativeCommand -Name 'cargo-miri')) {
|
|
Deny-MissingGate -Name 'Miri' -Reason 'cargo-miri is not installed for the active toolchain'
|
|
return
|
|
}
|
|
|
|
Invoke-TimedNative -Name 'cargo miri setup' -FilePath 'cargo' -ArgumentList @("+$NightlyToolchain", 'miri', 'setup')
|
|
foreach ($package in $MiriPackages) {
|
|
Invoke-TimedNative -Name "cargo miri ($package)" -FilePath 'cargo' -ArgumentList @(
|
|
"+$NightlyToolchain",
|
|
'miri',
|
|
'test',
|
|
'-p',
|
|
$package,
|
|
'--test',
|
|
'miri_json_family'
|
|
)
|
|
}
|
|
}
|
|
|
|
function Invoke-FuzzGate {
|
|
if (Confirm-Exemption -Name 'Fuzz' -Requested $ExemptFuzz.IsPresent -Reason $FuzzExemptionReason) {
|
|
return
|
|
}
|
|
if (-not (Test-NativeCommand -Name 'cargo-fuzz')) {
|
|
Deny-MissingGate -Name 'Fuzz' -Reason 'cargo-fuzz is not installed'
|
|
return
|
|
}
|
|
if (-not (Test-Path -LiteralPath $fuzzManifest)) {
|
|
Deny-MissingGate -Name 'Fuzz' -Reason 'fuzz harness manifest is missing'
|
|
return
|
|
}
|
|
Invoke-TimedNativeNoStdout -Name 'cargo fuzz metadata lock' -FilePath 'cargo' -ArgumentList @(
|
|
"+$NightlyToolchain",
|
|
'metadata',
|
|
'--manifest-path',
|
|
$fuzzManifest,
|
|
'--locked',
|
|
'--format-version',
|
|
'1'
|
|
)
|
|
Add-AsanRuntimeToPath
|
|
New-Item -ItemType Directory -Force -Path $fuzzArtifactRoot | Out-Null
|
|
|
|
Invoke-TimedNative -Name 'cargo fuzz json_family_decode' -FilePath 'cargo' -ArgumentList @(
|
|
"+$NightlyToolchain",
|
|
'fuzz',
|
|
'run',
|
|
'json_family_decode',
|
|
'--',
|
|
"-artifact_prefix=$fuzzArtifactRoot/",
|
|
"-max_total_time=$FuzzSeconds"
|
|
)
|
|
}
|
|
|
|
function Invoke-SanitizerGate {
|
|
if (Confirm-Exemption -Name 'Sanitizers' -Requested $ExemptSanitizers.IsPresent -Reason $SanitizersExemptionReason) {
|
|
return
|
|
}
|
|
|
|
$previousRustFlags = $env:RUSTFLAGS
|
|
try {
|
|
Add-AsanRuntimeToPath
|
|
$env:RUSTFLAGS = '-Zsanitizer=address'
|
|
foreach ($package in $SanitizerPackages) {
|
|
Invoke-TimedNative -Name "address sanitizer ($package)" -FilePath 'cargo' -ArgumentList @(
|
|
"+$NightlyToolchain",
|
|
'test',
|
|
'-p',
|
|
$package,
|
|
'--test',
|
|
'miri_json_family'
|
|
)
|
|
}
|
|
}
|
|
finally {
|
|
$env:RUSTFLAGS = $previousRustFlags
|
|
}
|
|
}
|
|
|
|
function Invoke-NoPanicGate {
|
|
if (Confirm-Exemption -Name 'NoPanic' -Requested $ExemptNoPanic.IsPresent -Reason $NoPanicExemptionReason) {
|
|
return
|
|
}
|
|
|
|
Invoke-TimedNative -Name 'no-panic source scan' -FilePath 'pwsh' -ArgumentList @(
|
|
'-NoProfile',
|
|
'-File',
|
|
(Join-Path $PSScriptRoot 'check-no-panic.ps1')
|
|
)
|
|
}
|
|
|
|
function Invoke-LoomGate {
|
|
if (Confirm-Exemption -Name 'Loom' -Requested $ExemptLoom.IsPresent -Reason $LoomExemptionReason) {
|
|
return
|
|
}
|
|
|
|
Invoke-TimedNative -Name 'loom runtime capture model' -FilePath 'cargo' -ArgumentList @(
|
|
'test',
|
|
'-p',
|
|
'runtimekit',
|
|
'--test',
|
|
'loom_capture'
|
|
)
|
|
}
|
|
|
|
try {
|
|
switch ($Only) {
|
|
'All' {
|
|
Invoke-NoPanicGate
|
|
Invoke-MiriGate
|
|
Invoke-LoomGate
|
|
Invoke-FuzzGate
|
|
Invoke-SanitizerGate
|
|
}
|
|
'Miri' { Invoke-MiriGate }
|
|
'Fuzz' { Invoke-FuzzGate }
|
|
'Sanitizers' { Invoke-SanitizerGate }
|
|
'NoPanic' { Invoke-NoPanicGate }
|
|
'Loom' { Invoke-LoomGate }
|
|
}
|
|
|
|
Write-Host ''
|
|
$script:StepResults |
|
|
Sort-Object Step |
|
|
Format-Table -AutoSize
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|