Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions frontend/src-tauri/resources/windows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ before the Tauri build. Those scripts use:
- A SHA-verified WiX CLI NuGet package, used only to extract the VC++ redist
bootstrapper payload reproducibly.

The VC++ redist contains ARM64EC payloads that report `AMD64` in the PE Machine
field. The staging script therefore rejects ARM64EC markers and only copies
native AMD64 runtime DLLs into the installer payload.

The PR build emits `target/reproducibility/desktop-pr-windows-*.sha256` proof
manifests. The signed release build emits
`target/reproducibility/desktop-release-windows-*.sha256` proof manifests after
Expand Down
102 changes: 97 additions & 5 deletions frontend/src-tauri/scripts/stage-windows-runtime-dlls.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,82 @@ function Test-PortableExecutableMachineAmd64 {
}
}

function Test-FileContainsAscii {
param(
[Parameter(Mandatory = $true)][string] $Path,
[Parameter(Mandatory = $true)][string] $Needle
)

$bytes = [IO.File]::ReadAllBytes($Path)
$needleBytes = [Text.Encoding]::ASCII.GetBytes($Needle)

if (($needleBytes.Length -eq 0) -or ($bytes.Length -lt $needleBytes.Length)) {
return $false
}

$lastStart = $bytes.Length - $needleBytes.Length
for ($i = 0; $i -le $lastStart; $i++) {
$matched = $true
for ($j = 0; $j -lt $needleBytes.Length; $j++) {
$actual = $bytes[$i + $j]
$expected = $needleBytes[$j]

if (($actual -ge 0x41) -and ($actual -le 0x5A)) {
$actual += 0x20
}

if (($expected -ge 0x41) -and ($expected -le 0x5A)) {
$expected += 0x20
}

if ($actual -ne $expected) {
$matched = $false
break
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
}

if ($matched) {
return $true
}
}

return $false
}

function Test-PortableExecutableArm64Ec {
param([Parameter(Mandatory = $true)][string] $Path)

return `
(Test-FileContainsAscii -Path $Path -Needle "arm64ec") -or `
(Test-FileContainsAscii -Path $Path -Needle "arm64ret") -or `
(Test-FileContainsAscii -Path $Path -Needle ".arm64.pdb")
}

function Test-PortableExecutableNativeAmd64 {
param([Parameter(Mandatory = $true)][string] $Path)

if (!(Test-PortableExecutableMachineAmd64 -Path $Path)) {
return $false
}

return !(Test-PortableExecutableArm64Ec -Path $Path)
}

function Assert-PortableExecutableNativeAmd64 {
param(
[Parameter(Mandatory = $true)][string] $Label,
[Parameter(Mandatory = $true)][string] $Path
)

if (!(Test-PortableExecutableMachineAmd64 -Path $Path)) {
throw "$Label is not an AMD64 portable executable: $Path"
}

if (Test-PortableExecutableArm64Ec -Path $Path) {
throw "$Label is ARM64EC, not native AMD64: $Path"
}
}

function Expand-VcRedist {
param(
[Parameter(Mandatory = $true)][string] $ExePath,
Expand Down Expand Up @@ -256,17 +332,24 @@ function Find-RequiredDll {
}

$x64Files = @($files | Where-Object { Test-PortableExecutableMachineAmd64 -Path $_.FullName })
$arm64EcFiles = @($x64Files | Where-Object { Test-PortableExecutableArm64Ec -Path $_.FullName })
$nativeAmd64Files = @($x64Files | Where-Object { !(Test-PortableExecutableArm64Ec -Path $_.FullName) })

foreach ($file in ($arm64EcFiles | Sort-Object FullName)) {
Write-Host ("skipped-windows-runtime-arm64ec {0} {1}" -f $Name, $file.FullName)
}

$match = $x64Files |
$match = $nativeAmd64Files |
Where-Object { $_.Name -ieq $Name } |
Sort-Object FullName |
Select-Object -First 1

if ($null -ne $match) {
Write-Host ("selected-windows-runtime-dll {0} {1}" -f $Name, $match.FullName)
return $match.FullName
}

foreach ($file in ($x64Files | Sort-Object FullName)) {
foreach ($file in ($nativeAmd64Files | Sort-Object FullName)) {
$versionInfo = $null
try {
$versionInfo = $file.VersionInfo
Expand Down Expand Up @@ -295,12 +378,16 @@ function Find-RequiredDll {
ForEach-Object {
$originalFilename = ""
$internalName = ""
$isAmd64 = $false
$isArm64Ec = $false
try {
$originalFilename = $_.VersionInfo.OriginalFilename
$internalName = $_.VersionInfo.InternalName
$isAmd64 = Test-PortableExecutableMachineAmd64 -Path $_.FullName
$isArm64Ec = $isAmd64 -and (Test-PortableExecutableArm64Ec -Path $_.FullName)
} catch {
}
Write-Host (" {0} len={1} original={2} internal={3}" -f $_.FullName, $_.Length, $originalFilename, $internalName)
Write-Host (" {0} len={1} amd64={2} arm64ec={3} original={4} internal={5}" -f $_.FullName, $_.Length, $isAmd64, $isArm64Ec, $originalFilename, $internalName)
}

throw "Could not find $Name in extracted VC++ Redistributable payload roots: $($Roots -join '; ')"
Expand Down Expand Up @@ -358,11 +445,16 @@ Assert-Sha256 -Label "VC++ Redistributable $vcRedistVersion" -Path $redistExe -E
$wixExe = Get-WixExe -CacheRoot $cacheDir
$payloadRoots = Expand-VcRedist -ExePath $redistExe -WixExe $wixExe -OutDir $redistDir

Copy-Item -LiteralPath $OrtDllPath -Destination (Join-Path $Destination "onnxruntime.dll") -Force
$stagedOrtDll = Join-Path $Destination "onnxruntime.dll"
Copy-Item -LiteralPath $OrtDllPath -Destination $stagedOrtDll -Force
Assert-PortableExecutableNativeAmd64 -Label "ONNX Runtime DLL" -Path $stagedOrtDll

foreach ($dllName in $dllNames) {
$source = Find-RequiredDll -Name $dllName -Roots $payloadRoots
Copy-Item -LiteralPath $source -Destination (Join-Path $Destination $dllName) -Force
Assert-PortableExecutableNativeAmd64 -Label $dllName -Path $source
$destinationDll = Join-Path $Destination $dllName
Copy-Item -LiteralPath $source -Destination $destinationDll -Force
Assert-PortableExecutableNativeAmd64 -Label $dllName -Path $destinationDll
}

Get-ChildItem -LiteralPath $Destination -Filter "*.dll" -File |
Expand Down
81 changes: 80 additions & 1 deletion scripts/ci/canonical-windows-nsis-payload-hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@


PE_CERTIFICATE_DIRECTORY_INDEX = 4
PE_MACHINE_AMD64 = 0x8664
WINDOWS_RUNTIME_PAYLOAD_FILES = (
"maple.exe",
"onnxruntime.dll",
"MSVCP140.dll",
"MSVCP140_1.dll",
"VCRUNTIME140.dll",
"VCRUNTIME140_1.dll",
)


def sha256_bytes(data):
Expand Down Expand Up @@ -44,6 +53,23 @@ def is_pe(data):
return False


def pe_machine(data, label):
if not is_pe(data):
raise ValueError(f"not a PE file: {label}")

pe_offset = read_u32(data, 0x3C)
return read_u16(data, pe_offset + 4)


def has_arm64ec_markers(data):
lowered = data.lower()
return (
b"arm64ec" in lowered
or b"arm64ret" in lowered
or b".arm64.pdb" in lowered
)


def canonical_pe_bytes(data, label):
if not is_pe(data):
return data
Expand Down Expand Up @@ -129,6 +155,45 @@ def canonical_tree_entries(root):
return entries


def payload_file(root, name):
matches = []
for current_root, _, files in os.walk(root):
for filename in files:
if filename.lower() == name.lower():
matches.append(os.path.join(current_root, filename))

if len(matches) != 1:
raise ValueError(
f"expected exactly one Windows payload file named {name}, found {len(matches)}"
)

return matches[0]
Comment thread
coderabbitai[bot] marked this conversation as resolved.


def verify_windows_runtime_payload(installer, label):
with tempfile.TemporaryDirectory() as tmp:
extract_installer(installer, tmp)

for name in WINDOWS_RUNTIME_PAYLOAD_FILES:
path = payload_file(tmp, name)
with open(path, "rb") as f:
data = f.read()

machine = pe_machine(data, name)
if machine != PE_MACHINE_AMD64:
raise ValueError(
f"Windows payload file is not native AMD64: {name} machine=0x{machine:04x}"
)

if has_arm64ec_markers(data):
raise ValueError(
f"Windows payload file is ARM64EC, not native AMD64: {name}"
)

digest = sha256_bytes(data)
print(f"verified-windows-runtime-native-amd64 {digest} {label}::{name}")


def canonical_tree_hash(installer):
with tempfile.TemporaryDirectory() as tmp:
extract_installer(installer, tmp)
Expand Down Expand Up @@ -182,11 +247,25 @@ def main():
parser = argparse.ArgumentParser(
description="Compute or compare canonical NSIS payload hashes for Windows installers."
)
parser.add_argument("--compare", action="store_true", help="compare signed and unsigned installers")
mode = parser.add_mutually_exclusive_group()
mode.add_argument("--compare", action="store_true", help="compare signed and unsigned installers")
mode.add_argument(
"--verify-runtime-dlls",
action="store_true",
help="verify installed Windows payload files are native AMD64, not ARM64EC",
)
parser.add_argument("paths", nargs="+")
args = parser.parse_args()

try:
if args.verify_runtime_dlls:
if len(args.paths) not in {1, 2}:
parser.error("--verify-runtime-dlls requires installer [label]")
installer = args.paths[0]
label = args.paths[1] if len(args.paths) == 2 else installer
verify_windows_runtime_payload(installer, label)
return 0

if args.compare:
if len(args.paths) not in {2, 4}:
parser.error("--compare requires signed unsigned [signed-label unsigned-label]")
Expand Down
24 changes: 24 additions & 0 deletions scripts/ci/verify-release-artifacts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,29 @@ verify_windows_runtime_manifest() {
fi
}

windows_setup_label_from_manifest() {
local manifest="$1"

awk '$2 ~ /_x64-setup\.exe$/ { print $2; exit }' "${manifest}"
}

verify_windows_installer_runtime_payload() {
local final_manifest="$1"
local label installer

label="$(windows_setup_label_from_manifest "${final_manifest}")"
if [ -z "${label}" ]; then
echo "Could not find Windows setup executable in ${final_manifest}." >&2
return 1
fi

installer="$(artifact_for_label "${label}")"
"$(python3_runner)" "${REPO_ROOT}/scripts/ci/canonical-windows-nsis-payload-hash.py" \
--verify-runtime-dlls \
"${installer}" \
"${label}"
}

verify_windows_canonical_manifest() {
local manifest="$1"
local kind digest label artifact actual
Expand Down Expand Up @@ -451,6 +474,7 @@ verify_windows() {
fi

verify_file_manifest "${final_manifest}"
verify_windows_installer_runtime_payload "${final_manifest}"
if [ -n "${runtime_manifest}" ]; then
verify_windows_runtime_manifest "${runtime_manifest}"
fi
Expand Down
Loading