A typo in the ruler's measuring tape lets every prisoner walk through the walls
CVE-2026-115889 is a numeric truncation error (CWE-197) in the Cranelift compiler backend used by Wasmtime, the Bytecode Alliance's reference WebAssembly runtime. Cranelift bit-packs SSA (Static Single Assignment) value indices into a fixed-width field — reportedly 24 bits in earlier internal representations. In release builds, Rust's default integer-overflow behavior is wrapping (not panicking), so when a WebAssembly function contains enough SSA values to exceed the bit-packed capacity, the index silently wraps around. The result: Cranelift references the *wrong* IR value during code generation, producing arbitrary miscompilation. Because the miscompiled code runs inside the Wasm sandbox engine itself, an attacker who controls the Wasm module can leverage the resulting type confusion (CWE-843) to read/write outside the sandbox's linear memory, achieving full host-process compromise. All architectures using the default Cranelift backend are affected across Wasmtime versions < 36.0.19 and 37.0.0 through 42.x. Fixes ship in 36.0.19 (LTS backport) and 43.0.0, which upgrade the index to a full u32 with overflow checks.
The vendor's CRITICAL 9.9 rating is aggressive but defensible for Wasmtime's canonical deployment role. Wasmtime exists *specifically* to sandbox untrusted WebAssembly — it is the security boundary, analogous to a hypervisor or container runtime. The CVSS vector AV:N/AC:L/PR:L/UI:N/S:C captures this well: the attacker submits a crafted module over the network with low privileges (a platform tenant account), no user interaction, and the impact crosses from the Wasm guest into the host process (Scope: Changed). Where we trim from 9.9 to 9.4 is on practical grounds: there is no public proof-of-concept, no observed in-the-wild exploitation, the EPSS probability sits at a low 0.45%, and crafting the trigger module requires nontrivial understanding of Cranelift IR internals. These temporal factors reduce immediate operational risk but do not change the fundamental severity class — this remains a CRITICAL sandbox-escape primitive.
5 steps from start to impact.
Craft oversized-SSA Wasm module
- Attacker can write arbitrary valid WebAssembly bytecode
- Knowledge of Cranelift SSA index bit-width and overflow behavior
- No public PoC or tooling automates this today
- Requires reverse-engineering Cranelift IR internals to reliably trigger overflow at a chosen value
Submit module to Wasmtime-powered service
- Target service accepts attacker-supplied Wasm modules
- Service uses Wasmtime with Cranelift backend (the default)
- Attacker holds a valid tenant/user account (PR:L)
- Services that pre-validate or size-limit Wasm functions may reject the oversized module
- Some platforms use alternative backends (e.g., Winch) or runtimes (V8, Wasmer) not affected
Cranelift compiles with truncated SSA indices
- Wasmtime built in release mode (standard for production)
- Cranelift version < 43.0.0 using bit-packed indices
- Debug builds with overflow checks would panic instead of wrapping — but production deployments universally use release builds
Type confusion enables out-of-bounds access
- The specific SSA alias produces an exploitable type confusion (attacker-influenced by module design)
- Achieving a *specific* useful confusion requires careful alignment of the overflow — trial and error may be needed
- ASLR on the host adds a probabilistic barrier to reliable exploitation
Sandbox escape to host-process RCE
- Successful OOB read/write from step 4
- Host process runs with meaningful privileges (network access, filesystem, secrets)
- CFI/CET on modern CPUs adds friction to control-flow hijacking
- Seccomp/AppArmor/SELinux policies may limit post-escape lateral movement
cargo update -p wasmtime) followed by rebuild and redeployment. For platforms like Fastly, check that the platform vendor has updated their runtime.Config API allows setting max_function_size or similar resource limits. This prevents the SSA index from reaching overflow. Deploy within 3 days as a stopgap if patching requires more time.- Network-level WAF rules — The Wasm module is opaque binary data, typically uploaded via platform-specific APIs. A WAF cannot inspect or understand Wasm bytecode structure to detect the malicious pattern.
- Switching to debug builds — While debug builds would panic on integer overflow, the 10-100x performance penalty makes this completely impractical for production workloads.
- ASLR alone — ASLR adds entropy to the host address space but does not prevent the sandbox escape itself. An attacker with arbitrary read can defeat ASLR by leaking pointers before pivoting to write.
The supporting signals.
| In-the-Wild Exploitation | None observed. Not listed in CISA KEV. No reports of active campaigns. The Bytecode Alliance stated no attacks were known at disclosure time. |
|---|---|
| Proof-of-Concept | No public PoC. No exploit code found on GitHub, ExploitDB, or pocindex.io. No nuclei template. Crafting a trigger module requires Cranelift-internal knowledge of SSA index bit-packing. Exploitation difficulty is moderate-to-hard without a PoC. |
| EPSS | 0.00454 (0.45%) — bottom quartile. The model rates exploitation probability as low, consistent with the niche product and absence of public exploit tooling. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H — 9.9. Scope:Changed is the key driver: the vuln crosses the Wasm-to-host boundary. PR:L reflects that uploading a Wasm module requires a tenant account, not anonymous access. |
| Affected Versions | Wasmtime < 36.0.19 (all early versions) and 37.0.0 through 42.x (all mainline releases between LTS and v43). This spans roughly two years of releases. |
| Fixed Versions | 36.0.19 (LTS backport) and 43.0.0 (mainline). Fix replaces bit-packed indices with full u32 plus overflow checks. See PR #12613 and #14818. |
| Disclosure Timeline | Reported 2026-10-19, advisory issued 2026-10-20, CVE published 2026-10-23. Turnaround was fast — 4 days from report to public CVE. |
| Reporter | Credited to jaydns per the GHSA advisory. |
| Companion CVE | Disclosed alongside CVE-2026-115893 (Cranelift br_table with >256K targets causes incorrect branch on aarch64). Same disclosure batch, same fix versions. Check both. |
| Scanning / Exposure | Wasmtime is an embedded library/CLI, not a network service with a fingerprint. No Shodan/Censys/GreyNoise tag exists. Exposure assessment requires software inventory (SBOM, cargo audit, package manager queries). Fastly Compute, Shopify, and similar platforms are the highest-profile consumers. |
Sources.
Why this verdict
- Canonical sandbox runtime — floor is CRITICAL. Wasmtime's entire purpose is executing *untrusted* WebAssembly in a sandboxed environment. By definition, the vast majority (>80%) of production Wasmtime deployments are in the high-value role — they ARE the security boundary between untrusted tenant code and the host. A sandbox escape in a sandboxing runtime is analogous to a hypervisor escape in ESXi: the component's reason for existence is the boundary being broken.
- Role multiplier — edge/serverless platforms (Fastly Compute, Shopify plugins): The documented chain succeeds fully in these deployments. A tenant uploads a crafted module → Cranelift compiles it → sandbox escape → host-process RCE with access to other tenants' data and secrets. Blast radius is fleet-scale / multi-tenant isolation breach. These platforms represent a significant share of Wasmtime's installed base.
- Role multiplier — CI/CD and plugin sandboxing: Organizations embedding Wasmtime to sandbox build plugins or user-supplied extensions face supply-chain-scale blast radius if the sandbox is escaped. The chain succeeds identically.
- Scope:Changed is accurate and devastating. The CVSS S:C flag correctly captures that the vulnerability crosses from the Wasm guest context into the host process — this is a privilege boundary violation, not just a local escalation.
- Downward adjustment from 9.9 to 9.4: No public PoC exists, EPSS is 0.45%, no in-the-wild exploitation observed, and crafting the trigger module requires nontrivial Cranelift-internal knowledge. These temporal factors reduce immediate risk but do not change the severity class.
Why not higher?
The vendor's 9.9 is near the CVSS ceiling. We trim to 9.4 because the absence of any public exploit code, the zero in-the-wild exploitation, and the requirement for Cranelift-specific knowledge to craft a trigger module all reduce the *immediacy* of the threat. A 10.0 or 9.9 would imply trivially exploitable with public tooling, which is not the case today.
Why not lower?
Dropping below CRITICAL would require evidence that few Wasmtime deployments occupy the high-value role — but the opposite is true: Wasmtime exists to sandbox untrusted code, making nearly all deployments high-value by definition. The sandbox escape primitive gives C:H/I:H/A:H with Scope:Changed on a multi-tenant boundary. Even without a public PoC, a motivated attacker (nation-state, ransomware group targeting cloud infrastructure) can develop one. The niche deployment base reduces *breadth* of exposure but not *depth* of impact where Wasmtime is present.
Crowdsourced verification payload.
Run on any host where Wasmtime is installed as a CLI binary or embedded as a Cargo dependency. Execute from the project root (for Cargo.lock checks) or any shell where wasmtime is in PATH. No special privileges required. Example: bash check_cve_2026_115889.sh
#!/usr/bin/env bash
# CVE-2026-115889 — Wasmtime Cranelift SSA bit-packing overflow checker
# Checks installed wasmtime version against patched versions.
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
echo "=== CVE-2026-115889 Wasmtime Version Check ==="
VER=""
SOURCE=""
# Method 1: wasmtime CLI binary
if command -v wasmtime &>/dev/null; then
VER=$(wasmtime --version 2>/dev/null | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
SOURCE="wasmtime CLI"
fi
# Method 2: Cargo.lock dependency
if [ -z "$VER" ] && [ -f "Cargo.lock" ]; then
VER=$(grep -A1 'name = "wasmtime"' Cargo.lock 2>/dev/null | grep 'version' | head -1 | sed 's/.*"\([0-9.]*\)".*/\1/')
SOURCE="Cargo.lock"
fi
# Method 3: cargo tree
if [ -z "$VER" ] && command -v cargo &>/dev/null && [ -f "Cargo.toml" ]; then
VER=$(cargo tree -p wasmtime 2>/dev/null | head -1 | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' || true)
SOURCE="cargo tree"
fi
if [ -z "$VER" ]; then
echo "UNKNOWN — wasmtime not found (not in PATH, Cargo.lock, or Cargo.toml)"
exit 2
fi
echo "Detected wasmtime $VER via $SOURCE"
IFS='.' read -r MAJOR MINOR PATCH <<< "$VER"
# Patched: (>= 43.0.0) OR (>= 36.0.19 AND < 37.0.0)
# Vulnerable: (< 36.0.19) OR (>= 37.0.0 AND < 43.0.0)
if [ "$MAJOR" -ge 43 ]; then
echo "PATCHED — $VER >= 43.0.0"
exit 0
elif [ "$MAJOR" -eq 36 ] && [ "$MINOR" -eq 0 ] && [ "$PATCH" -ge 19 ]; then
echo "PATCHED — $VER >= 36.0.19 (LTS backport)"
exit 0
elif [ "$MAJOR" -ge 37 ] && [ "$MAJOR" -lt 43 ]; then
echo "VULNERABLE — $VER is in range 37.x-42.x; upgrade to >= 43.0.0"
exit 1
elif [ "$MAJOR" -eq 36 ]; then
if [ "$MINOR" -gt 0 ] || [ "$PATCH" -lt 19 ]; then
echo "VULNERABLE — $VER < 36.0.19; upgrade to >= 36.0.19 or >= 43.0.0"
exit 1
fi
elif [ "$MAJOR" -lt 36 ]; then
echo "VULNERABLE — $VER < 36.0.19; upgrade to >= 36.0.19 or >= 43.0.0"
exit 1
fi
echo "UNKNOWN — could not classify version $VER"
exit 2