open-standards/scripts/check-wasmtime-pin.py
Transaction Science 888cc3ba20 CI: a check holds every workflow to the one pinned wasmtime and both its settings
The install steps' comment said every WAI workflow that runs wasmtime pins one
version and passes both determinism settings, but nothing enforced it: a step
that ran a stock or differently pinned wasmtime would have merged green.
scripts/check-wasmtime-pin.py now fails, in the repository checks, any workflow
that

1. downloads a wasmtime release other than 50.0.1, or checks it against any
   SHA-256 but that archive's;
2. installs wasmtime another way (cargo install, the install script, an action);
3. runs wasmtime (run, serve, compile, or a module given to it) or names it as
   a CARGO_TARGET_*_RUNNER without -W nan-canonicalization=y and
   -W relaxed-simd-deterministic=y.

It also fails when it finds no download or no run, so a check that looked in
the wrong place cannot pass. On this tree it finds the downloads and runs it
should. Its self-test (scripts/check-wasmtime-pin-selftest.sh) passes a scratch
workflow shaped like these and fails each way of breaking it that the
self-test lists, matching the reason in the output.

The comment's relaxed_madd claim was measured on arm64 only. It now rests on
both architectures, measured on an arm64 macOS host and an x86_64 Linux host
with a small probe (wasmtime 50.0.1's release for the host, checked against
the digest the release lists):

    host Darwin arm64; wasmtime 50.0.1 (c307ec81d 2026-10-29)
    f32.add(bits 0x7fa00001, 1.0)                     [no flags]: 0x7fe00001
    f32x4.relaxed_madd(1+2^-12, 1+2^-12, -(1+2^-11)) [no flags]: 0x33800000
    f32.add(bits 0x7fa00001, 1.0)                     [both flags]: 0x7fc00000
    f32x4.relaxed_madd(1+2^-12, 1+2^-12, -(1+2^-11)) [both flags]: 0x33800000
    an unknown -W option is refused: error: failed to parse -W / --wasm option:
      unknown option: no-such-option

    host Linux x86_64; wasmtime 50.0.1 (c307ec81d 2026-10-29)
    (the same five lines, bit for bit)

0x33800000 is 2^-24, the fused result; the unfused fadd(fmul) gives 0. So the
NaN setting takes effect on both, and relaxed_madd is fused on both with and
without the relaxed setting. That matches 50.0.1's source (the
wasmtime-internal-cranelift crate, read from the registry): the translator
lowers F32x4/F64x2RelaxedMadd through fma_f32x4/fma_f64x2
(src/translate/code_translator.rs L2530-2539), which emit a native fma on a
host that has one, a fused library call under relaxed-simd-deterministic on a
host that has none, and the unfused fmul and fadd only on a host with no FMA
and the setting off (src/func_environ.rs L5907-5925). The setting is what
makes the result fused on every host. And since an unknown -W option stops
wasmtime, a run that exits 0 with the two flags has parsed them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 12:28:47 -05:00

144 lines
6.3 KiB
Python
Executable file

#!/usr/bin/env python3
"""check-wasmtime-pin.py: every workflow that runs wasmtime runs the one pinned release,
with both of its determinism settings.
WAI's WebAssembly legs hold a module's output to pinned bits. A stock wasmtime leaves NaN
canonicalisation off and lets each relaxed SIMD instruction take the host's behaviour, and
two versions of it can lower one instruction two ways, so a leg's bits would be the
engine's choice. The install steps' comment says every workflow pins one version and
passes both settings; this makes that true of every workflow, not only of the ones that
comment sits in.
In every `.github/workflows/*.yml`, with continued lines (`\\` at the end) joined, this
requires that
1. every download of a wasmtime release names version PIN (the `v=` the step sets, or
the version in the URL), and the SHA-256 it checks is the digest of that archive in
DIGESTS;
2. wasmtime is installed no other way (`cargo install wasmtime-cli`, the install
script, or an action);
3. every `wasmtime run`, `wasmtime serve` or `wasmtime compile`, every `wasmtime` given
a module path, and every `CARGO_TARGET_<triple>_RUNNER` that is wasmtime, passes
`-W nan-canonicalization=y` and `-W relaxed-simd-deterministic=y`;
4. at least one download and one run were found, so a check that looked in the wrong
place fails rather than passing on nothing.
check-wasmtime-pin.py [--root DIR]
Exit 0 when all four hold, 1 with one line a finding otherwise. Standard library only.
"""
import os
import re
import sys
PIN = "50.0.1"
# The release archives a workflow may download, by name, with the SHA-256 the release lists.
DIGESTS = {
"wasmtime-v50.0.1-x86_64-linux.tar.xz": "685466455dd42a14774a86c36c8f2e45cbb9bd6d92a850139315df60a75498a5",
}
FLAGS = ("nan-canonicalization=y", "relaxed-simd-deterministic=y")
DOWNLOAD = re.compile(r"bytecodealliance/wasmtime/releases/download/")
URL_VERSION = re.compile(r"releases/download/v(\d[^/\"'\s]*)/")
SET_VERSION = re.compile(r"^\s*v=[\"']?([^\"'\s;]+)")
ARCHIVE = re.compile(r"wasmtime-v(\$v|\$\{v\}|\d[^-\"'\s]*)-([a-z0-9_]+-[a-z]+)\.tar\.xz")
DIGEST = re.compile(r"\b([0-9a-f]{64})\b.*sha256sum -c")
OTHER_INSTALL = re.compile(
r"cargo\s+(?:\S+\s+)*install\s+(?:\S+\s+)*wasmtime-cli|wasmtime\.dev/install|uses:\s*\S*wasmtime"
)
# wasmtime as a command: run, serve or compile, or given a module path (`wasmtime x.wasm`).
INVOKE = re.compile(
r"(?:^|[\s;&|(`/\"'=])wasmtime\s+(?:(?:run|serve|compile)\b|[^\s-]\S*\.(?:c?wasm|wat)\b)"
)
RUNNER = re.compile(r"CARGO_TARGET_\w+_RUNNER\s*[:=]\s*[\"']?wasmtime\b")
def logical_lines(text):
"""(first line number, text) with backslash-continued lines joined."""
out, buf, start = [], "", 0
for n, line in enumerate(text.splitlines(), 1):
if not buf:
start = n
if line.rstrip().endswith("\\"):
buf += line.rstrip()[:-1] + " "
continue
out.append((start, buf + line))
buf = ""
if buf:
out.append((start, buf))
return out
def check_file(path, rel, findings):
lines = logical_lines(open(path, encoding="utf-8").read())
downloads = runs = 0
for i, (n, line) in enumerate(lines):
code = line.lstrip()
if code.startswith("#"):
continue
if OTHER_INSTALL.search(line):
findings.append(f"{rel}:{n}: installs wasmtime some other way; download the pinned release ({PIN}) by version and SHA-256")
if DOWNLOAD.search(line):
downloads += 1
m = URL_VERSION.search(line)
ver = m.group(1) if m else None
if ver is None: # the URL takes the version from the step's `v=`
for _, prev in reversed(lines[max(0, i - 12) : i]):
s = SET_VERSION.match(prev)
if s:
ver = s.group(1)
break
if ver != PIN:
findings.append(f"{rel}:{n}: downloads wasmtime {ver or '(version not found)'}; every workflow pins {PIN}")
near = lines[max(0, i - 12) : i + 5]
arch = None
for _, other in near:
a = ARCHIVE.search(other)
if a:
arch = a.group(2)
break
name = f"wasmtime-v{ver}-{arch}.tar.xz" if arch else None
digests = [d.group(1) for _, other in lines[i : i + 5] for d in [DIGEST.search(other)] if d]
if name not in DIGESTS:
findings.append(f"{rel}:{n}: downloads {name or 'an archive whose name was not found'}, which has no pinned digest here")
elif not digests:
findings.append(f"{rel}:{n}: downloads {name} and checks no SHA-256 within four lines")
elif digests[0] != DIGESTS[name]:
findings.append(f"{rel}:{n}: checks {name} against {digests[0][:12]}..., not its pinned digest {DIGESTS[name][:12]}...")
for rx in (INVOKE, RUNNER):
m = rx.search(line)
if m:
runs += 1
rest = line[m.start() :]
missing = [f for f in FLAGS if f not in rest]
if missing:
findings.append(f"{rel}:{n}: runs wasmtime without {' and '.join('-W ' + f for f in missing)}")
break
return downloads, runs
def main(argv):
root = "."
if len(argv) == 3 and argv[1] == "--root":
root = argv[2]
elif len(argv) != 1:
sys.exit("usage: check-wasmtime-pin.py [--root DIR]")
wf = os.path.join(root, ".github", "workflows")
findings, downloads, runs = [], 0, 0
for name in sorted(os.listdir(wf)) if os.path.isdir(wf) else []:
if name.endswith((".yml", ".yaml")):
d, r = check_file(os.path.join(wf, name), f".github/workflows/{name}", findings)
downloads += d
runs += r
if downloads == 0 or runs == 0:
findings.append(f"found {downloads} wasmtime download(s) and {runs} run(s) under {wf}: this check looked in the wrong place")
for f in findings:
print(f"::error::{f}")
if findings:
return 1
print(f"check-wasmtime-pin: {downloads} downloads of wasmtime {PIN}, each checked against its digest; {runs} runs, each with both settings")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))