Skip to content

Commit 64c3cd7

Browse files
docs(adr): ADR 0006 — SNIF host runtime: pinned, patched, no-WASI wasmtime (#51)
The **new-and-improved SNIF host-runtime specification** you asked for. The current host wasmtime (bundled by precompiled wasmex 0.14) sits in the vulnerable range of **two critical aarch64 sandbox-escape CVEs** (Winch GHSA-xx5w-cvp6-jv83 + Cranelift GHSA-jhxm-h53p-jm7w; patched 42.0.2) — the exact failure a SNIF exists to prevent — and **upstream wasmex has no patched release**, so a wasmex bump is not a fix. ADR 0006 makes SNIF **own its host runtime**: patched wasmtime (≥42.0.2, pin 43.x) built from source via a SNIF wasmex fork, **Cranelift-only (Winch off)**, **no WASI** (guests are freestanding per ADR 0005), behind a **CI gate** that fails if the resolved host wasmtime is vulnerable. Scope honesty: this PR is the **specification**. The implementation (fork wasmex → bump wasmtime → fix 41→43 Rust API → build/test → wire the gate) is real Rust work I haven't built/verified here, tracked as a follow-up. The Zenodo/CITATION bump waits until that fix actually lands — bumping now would overclaim the CVEs as resolved. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e5768b4 commit 64c3cd7

1 file changed

Lines changed: 95 additions & 0 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
3+
= ADR 0006 — SNIF host runtime: pinned, patched, no-WASI wasmtime
4+
5+
Date: 2026-06-25
6+
7+
== Status
8+
9+
Accepted.
10+
11+
== Context
12+
13+
A SNIF's whole value proposition is *sandbox integrity*: a guest fault must be
14+
trapped, never an escape into the BEAM's address space (ADR 0005). That guarantee
15+
is only as strong as the host wasmtime the sandbox runs in.
16+
17+
Two **critical** wasmtime advisories break exactly that guarantee:
18+
19+
* **GHSA-xx5w-cvp6-jv83** — Winch compiler backend on aarch64: a sandbox-escaping
20+
memory access. Patched in `42.0.2` (for the `>= 37.0.0, < 42.0.2` line).
21+
* **GHSA-jhxm-h53p-jm7w** — miscompiled guest heap access on aarch64 Cranelift:
22+
sandbox escape. Patched in `42.0.2`.
23+
24+
Both are *sandbox escapes* — the one failure mode a SNIF exists to prevent — on
25+
aarch64 (Apple Silicon, ARM servers/CI).
26+
27+
The host wasmtime is supplied transitively by `wasmex`. The current pin
28+
(`{:wasmex, "~> 0.14"}`) resolves to wasmex `0.14.0`, whose bundled wasmtime is in
29+
the **vulnerable** range, and which ships as a **precompiled** NIF
30+
(`rustler_precompiled`) — so the vulnerable runtime is baked into a downloaded
31+
binary, not even visible at the demo's build. Critically, **upstream wasmex has
32+
no release with a patched wasmtime** (latest `0.14.0` bundles wasmtime `39.0.1`),
33+
so "bump wasmex" is not a fix. wasmex also pulls in `wasmtime-wasi` /
34+
`wasi-common`, which SNIF guests never use — they are `wasm32-freestanding`, no
35+
WASI (ADR 0005) — so that surface is pure, unused attack surface.
36+
37+
== Decision
38+
39+
SNIF owns its host runtime version and configuration rather than inheriting
40+
whatever wasmex happens to bundle. The SNIF host runtime is:
41+
42+
. **Patched wasmtime — pinned by SNIF, ≥ `42.0.2`** (current pin: the latest
43+
stable patched line, `43.0.1`+). Achieved by building wasmex **from source**
44+
(`force_build: true`) against a SNIF-maintained wasmex pin
45+
(`hyperpolymath/wasmex`, branch `snif-wasmtime-patched`) whose
46+
`native/wasmex/Cargo.toml` bumps `wasmtime`/`wasmtime-wasi`/`wasmtime-wasi-http`
47+
to the patched line and is kept current by Dependabot + the estate dep-security
48+
sweep. No precompiled NIFs for the host.
49+
. **Cranelift only — Winch disabled.** Winch is not used; removing it closes
50+
GHSA-xx5w-cvp6-jv83 by construction and shrinks the codegen surface.
51+
. **No WASI.** `wasmtime-wasi` and `wasi-common` are dropped from the host build
52+
(default features off; only the `cranelift` feature on). Guests are
53+
freestanding (ADR 0005); WASI is dead weight and attack surface.
54+
. **Fuel-metered, store-capped** as today (ADR 0004) — unchanged; this ADR is
55+
about *which* wasmtime and *how configured*, not the liveness guards.
56+
57+
The pinned-and-patched property is a **gate**: CI fails if the resolved host
58+
wasmtime is in any advisory's vulnerable range (a `cargo audit` / advisory check
59+
over the built `Cargo.lock`).
60+
61+
== Consequences
62+
63+
=== Positive
64+
- The two critical sandbox-escape CVEs are removed; the SNIF integrity claim holds
65+
on aarch64 as well as x86_64.
66+
- SNIF's sandbox integrity no longer depends on upstream wasmex's release cadence
67+
or its choice of wasmtime version — SNIF pins and patches it directly.
68+
- Smaller host: no Winch, no WASI → less codegen and less unused surface.
69+
70+
=== Negative / Neutral
71+
- The host NIF must be **built from source** (no precompiled fast path); CI and
72+
contributor setup need a Rust toolchain (already required for the guest).
73+
- SNIF carries a thin wasmex fork pin (`hyperpolymath/wasmex@snif-wasmtime-patched`)
74+
to bump wasmtime ahead of upstream. It tracks upstream wasmex otherwise.
75+
- Until the fork pin lands + CI verifies the build, the demo's `mix.exs` still
76+
references upstream wasmex; this ADR is the spec, and the migration steps below
77+
are the implementation.
78+
79+
== Implementation steps
80+
81+
. Fork wasmex → `hyperpolymath/wasmex`, branch `snif-wasmtime-patched`: bump
82+
`wasmtime*` to ≥ `42.0.2` in `native/wasmex/Cargo.toml`, set
83+
`default-features = false, features = ["cranelift"]`, drop the WASI deps, and
84+
fix any wasmtime 41→43 API breakage in wasmex's Rust.
85+
. `demo/mix.exs`: `{:wasmex, github: "hyperpolymath/wasmex", branch:
86+
"snif-wasmtime-patched", force_build: true}`.
87+
. Add a CI gate: advisory scan of the built `Cargo.lock` must show no wasmtime in
88+
a vulnerable range (fail the build otherwise).
89+
. Re-run `demo` tests + the metamorphic/pool suites against the new host.
90+
91+
== References
92+
93+
- ADR 0005 — Rust wasm32 guest + verifier (freestanding, no-WASI guest).
94+
- ADR 0004 — fuel-metered liveness guard.
95+
- GHSA-xx5w-cvp6-jv83, GHSA-jhxm-h53p-jm7w (wasmtime aarch64 sandbox escapes).

0 commit comments

Comments
 (0)