Yaamlabs

Vulnerabilities

Two critical Wasmtime flaws let Wasm escape the sandbox

Two CVSS 9.9 Cranelift bugs let a crafted WebAssembly module run code on the Wasmtime host. Who is exposed, the fixed versions and how to check.

The Bytecode Alliance published two critical advisories for Wasmtime, its WebAssembly runtime, on October 19, 2026, and GitHub issued the CVE records on October 23. Both bugs sit in Cranelift, the compiler Wasmtime uses by default to turn WebAssembly (Wasm) into native machine code, and both let a crafted module break out of the sandbox and run code with the privileges of the host process. CVE-2026-115889 affects every architecture; CVE-2026-115893 affects ARM64 (aarch64) hosts only. Both score 9.9 on CVSS 3.1.

You are exposed if you compile Wasm that someone else wrote: a plugin system that loads customer extensions, an edge or serverless platform running tenant code, a smart contract or rules engine that accepts uploaded modules. Hosts that only run their own modules are not exposed, because the attacker has to supply the module. No exploitation in the wild had been reported as of October 26, but a public GitHub repository now claims a working exploit for the ARM64 bug.

How it works

A WebAssembly sandbox depends on the compiler. Wasmtime checks a module before compiling it, but the safety of the machine code that comes out rests on Cranelift translating every instruction correctly: memory accesses stay inside the module's linear memory, and branches land only on code the module is allowed to reach. A miscompilation breaks that promise without any bug in the host's own API, which is why the CVSS vector marks scope as changed.

CVE-2026-115889 (GHSA-897f-g5gm-fmxm). Cranelift's intermediate representation (IR) refers to values, instructions and blocks by number, and older versions packed those numbers into 24-bit fields of a 64-bit word. The only width check was a debug_assert!, which Rust removes from release builds. A function that needs more than about 16.7 million (2 to the power 24) value indices wraps round silently, so the compiler starts reading the wrong values. The advisory says a very large function body, or one built to use many SSA values (single static assignment, the compiler's numbered intermediate values), can turn this into type confusion and a sandbox escape. A cap on module file size alone does not prevent it.

The main line fixed it with no security note: a change merged on February 18, 2026 widened the fields to full 32-bit indices, with a check when that limit is hit, and shipped in Wasmtime 43.0.0 on March 20. The long-term-support 36.x line only got the fix in 36.0.19, so 36.x before 36.0.19 and 37.0.0 to 42.x are affected.

CVE-2026-115893 (GHSA-mghf-wjfp-p5xm). A br_table instruction is Wasm's jump table: one index picks one of many branch targets. On aarch64, Cranelift compiles a br_table with more than about 256,000 targets into code whose conditional branch points at the wrong place, at an offset the module controls. The advisory says every Wasmtime and Cranelift release with aarch64 support is affected, back to when that support arrived in 2020. x86-64 hosts are not affected.

The proof-of-concept repository describes the chain on Wasmtime 49.0.3 on macOS and Linux arm64: a module with no imports and no WASI (WebAssembly System Interface) access combines the oversized table with constants that land in executable memory, and the bad branch runs them as native code. The author reports that the payload replaced the Wasmtime process with a system binary, and that 49.0.4 rejected the module. We have not verified the exploit.

Who else ships the bug

Cranelift is used outside Wasmtime. Wasmer 7.5.0 pins cranelift-codegen, cranelift-frontend and cranelift-entity at exactly 0.136.0, which carries the br_table bug; the fix is in Cranelift 0.136.4, released alongside Wasmtime 49.0.4. Because of the exact pin, cargo update cannot pull it in, and issue 7117, opened on October 26, asks for a 7.5.1 release. That issue notes Wasmer 7.5.0 is not affected by CVE-2026-115889.

Spin 4.2.4, released October 21, bumps its embedded Wasmtime. Debian's tracker lists the trixie rust-wasmtime package as vulnerable. Wasmtime's 49.0.4 release also fixes three wasi-http header and body-length issues and a component model panic (GHSA-9pvw-hwfg-92h5), so the upgrade closes more than the two critical bugs.

What to do

1. Find every copy

Wasmtime is usually a library compiled into your own service, so checking the command-line tool is not enough. In each Rust project, run cargo tree -i wasmtime and cargo tree -i cranelift-codegen, and run cargo audit, which flags RUSTSEC-2026-0380 and RUSTSEC-2026-0379. On hosts, wasmtime --version covers the CLI, and uname -m returning aarch64 or arm64 tells you the second bug applies. Check Python, Go and .NET services that embed Wasmtime through a language binding, and any platform built on Spin or Wasmer.

2. Upgrade and rebuild

Move to Wasmtime 36.0.19 on the 36.x line, 48.0.7 on 48.x, 49.0.4 on 49.x, or 50.0.0, released October 20, which also contains the aarch64 fix. Version 43.0.0 alone is not enough on ARM64: it clears CVE-2026-115889 and leaves CVE-2026-115893 open until 48.0.7. Rebuild and redeploy every service that links the crate, then recompile any precompiled .cwasm artefacts, since code compiled by a vulnerable Cranelift keeps the bad branches. For Wasmer, pin the three Cranelift crates to =0.136.4 until 7.5.1 ships.

3. If you cannot upgrade today

Neither advisory lists a workaround. The safest stopgap is to stop compiling modules from outside parties on vulnerable builds until the upgrade lands. If that is not possible, these narrow the window without closing it:

  • Move untrusted compilation to x86-64 hosts. That removes CVE-2026-115893 only.
  • Before compiling, parse each module (for example with the wasmparser crate) and reject any br_table with tens of thousands of targets or any function body far larger than your legitimate plugins use. Real code rarely comes close to 256,000 targets.
  • Run the Wasmtime host as an unprivileged user in its own container or VM, with no cloud credentials or database secrets in its environment, so a successful escape lands somewhere with little to take.

4. Check whether you were hit

No indicators of compromise have been published. If you store uploaded modules, review recent ones for an unusually large br_table or a single function with a huge body. On the host, look in your process audit logs (auditd execve records on Linux, or EDR process trees) for the Wasmtime worker starting a shell or other binary, and for worker processes that crashed or disappeared without an error, which the proof-of-concept notes say is how a successful run can look. If anything fits, treat the host as compromised: rebuild it on a fixed version and rotate every secret it could read.

Sandboxes need patch owners too

Teams often treat a Wasm runtime as the boundary that makes untrusted code safe, then pin it in a lockfile and forget it. Both bugs are compiler bugs that sat in default configurations for years, one of them since 2020. If your product runs customer code inside Wasm, the runtime and its compiler belong in the same patch process as your kernel and container runtime, with an owner and a deadline.

Our red team code review looks at how your service loads and sandboxes third-party code, and cloud and Kubernetes security covers isolating the hosts that run it. Open the chat and Yaali, our AI agent, will pass your question to an engineer.


Sources: GHSA-897f-g5gm-fmxm, GHSA-mghf-wjfp-p5xm, OSV CVE-2026-115889, OSV CVE-2026-115893, RustSec RUSTSEC-2026-0379, RustSec RUSTSEC-2026-0380, Wasmtime 49.0.4 release, Wasmtime 50.0.0 release, Ostorlab analysis of CVE-2026-115889, Mallory story, The Hacker Wire CVE-2026-115893, Wasmer issue 7117, Spin releases, Debian tracker CVE-2026-115893, CVE-2026-115893 proof-of-concept repository.

Back to the blog, or read this post on the full site.