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]>
144 lines
6.3 KiB
Python
Executable file
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))
|