← Back to Feed CACHED · 2026-10-26 22:40:03 · CACHE_KEY CVE-2026-115893
CVE-2026-115893 · CWE-691 · Disclosed 2026-10-23

Wasmtime Cranelift aarch64 br_table miscompilation sandbox escape via offset wrap

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

A misplaced guardrail on a 256-lane highway that sends traffic off a cliff instead of to the shoulder

CVE-2026-115893 is a compiler miscompilation bug in Cranelift, the default code-generation backend for Wasmtime, affecting only the aarch64 (ARM64) architecture. The WebAssembly br_table instruction is a multi-way branch — think of a giant switch statement. On aarch64, Cranelift used a conditional branch (b.hs) with a ±1 MiB offset range to redirect out-of-bounds indices to a default target. When a br_table contained more than ~256,000 entries, the inline jump table exceeded that range, causing the offset to *silently wrap*, sending execution to an attacker-controlled location within the compiled code region. Because the attacker fully controls the Wasm module (and thus the table size and contents), they can craft a module that deterministically escapes the Wasm sandbox and executes arbitrary host-level code. All Wasmtime versions supporting aarch64 since the initial aarch64 backend release in 2020 are affected — that is every version prior to 36.0.19, 48.0.7, and 49.0.4. Wasmer, which also uses Cranelift, released 7.5.1 with the same fix.

The vendor severity of CRITICAL 9.9 is honest and largely accurate. Wasmtime's entire value proposition is *sandboxing untrusted code*, so a sandbox escape is an existential failure of the product's security model. The CVSS vector (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H) correctly captures Scope: Changed — the attacker breaks out of the Wasm guest into the host OS. The one place the 9.9 overstates risk is the implicit assumption that all deployments are on aarch64. In reality, a significant but not universal share of production Wasmtime runs on ARM64 (AWS Graviton, Azure Cobalt, Ampere Altra on edge nodes). x86_64 deployments are entirely unaffected. This architecture gate is the only meaningful friction point; everything else — crafting a malicious br_table, submitting it to a multi-tenant runtime — is trivially within attacker capability.

"Sandbox escape in Wasmtime on ARM64 — if you run untrusted Wasm on Graviton, patch now."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Attacker obtains Wasm submission capability

The attacker needs the ability to submit a WebAssembly module for execution by a Wasmtime engine running on aarch64. In multi-tenant serverless platforms like Fastly Compute or Fermyon Cloud, this is the *normal operating model* — tenants deploy Wasm functions. In plugin ecosystems (Envoy Wasm filters, Shopify Functions), developers submit Wasm extensions. PR:L in the CVSS vector reflects this: you need an account, not admin privileges.
Conditions required:
  • Target Wasmtime engine runs on aarch64 architecture
  • Attacker has ability to submit/deploy a Wasm module (tenant account, plugin developer, etc.)
Where this breaks in practice:
  • If the Wasmtime instance only executes trusted, internally-built Wasm modules, the attacker has no submission vector
  • x86_64 deployments are entirely immune — this is aarch64-only
STEP 02

Craft malicious Wasm module with oversized br_table

The attacker constructs a valid WebAssembly module containing a br_table instruction with more than ~256,000 branch targets. This is well within the Wasm spec (which allows up to 2^32 targets). The module passes all standard Wasm validation checks. No obfuscation is needed — the oversized table is a perfectly legal Wasm construct. Tools like wasm-tools or walrus can trivially generate this.
Conditions required:
  • Knowledge of the vulnerability (the advisory and fix commit fully document the trigger condition)
Where this breaks in practice:
  • Some Wasm platforms may impose module size limits that could prevent very large br_tables, but 256k entries × 4 bytes ≈ 1 MB of table data, which is well within typical upload limits
Detection/coverage: Static analysis of submitted Wasm modules could flag br_table instructions with >200k targets as anomalous, but no mainstream WAF or Wasm validator currently checks this
STEP 03

Cranelift miscompiles the br_table on aarch64

When Cranelift compiles the module, the JTSequence macro-instruction emits a b.hs conditional branch for the default case with a ±1 MiB reach. The inline jump table of 32-bit offsets for >256k targets exceeds this range. The branch offset silently wraps, causing the default-case branch to land at an incorrect, attacker-influenceable location within the compiled code region. The attacker controls the offset by choosing the exact number of table entries.
Conditions required:
  • Wasmtime uses Cranelift backend (this is the default)
  • Target is aarch64
Where this breaks in practice:
  • If an alternative backend (e.g., Winch) were used instead of Cranelift, the bug would not trigger, but Cranelift is the default and recommended backend for production
Detection/coverage: No runtime detection mechanism exists for miscompiled branch targets; the error occurs at compile time and manifests silently at execution time
STEP 04

Out-of-bounds branch achieves sandbox escape

The incorrect branch target lands within the Wasmtime process's executable memory region but outside the intended Wasm sandbox boundaries. By carefully selecting the br_table size, the attacker can control which host-level compiled code is reached. From this point, the attacker has escaped the Wasm sandbox and is executing code with the full privileges of the Wasmtime host process — which on multi-tenant platforms typically has access to other tenants' data, network resources, and host OS capabilities.
Conditions required:
  • The Wasmtime process has meaningful privileges (network access, filesystem, other tenant data)
Where this breaks in practice:
  • If the Wasmtime process itself runs in a secondary sandbox (e.g., a minimal container with seccomp), the blast radius of the escape is reduced but not eliminated
Detection/coverage: Host-level EDR may detect anomalous syscalls from the Wasmtime process post-escape, but the initial sandbox break produces no observable signal in Wasm-level monitoring
03 · Compensating Control

1
CRITICAL 9.2→IGNORE 0.0
SEVERITY REDUCED
Upgrade Wasmtime to 36.0.19, 48.0.7, 49.0.4, or 50.0.0+ immediately — This is the only complete fix. Per the noisgate mitigation SLA for CRITICAL, deploy within 3 days. Run cargo update -p wasmtime or update your pinned version. For Wasmer users, upgrade to 7.5.1+. Verify with cargo audit using RUSTSEC-2026-0379.
2
CRITICAL 9.2→LOW 2.5
SEVERITY REDUCED
Restrict Wasm module submission to trusted sources on aarch64 engines — If you cannot patch within 3 days, disable untrusted Wasm module uploads on aarch64 Wasmtime instances. This removes the attacker's ability to deliver a malicious br_table. Only allow internally-built, reviewed Wasm modules. This is a temporary measure — not a substitute for patching within the 90-day noisgate remediation SLA.
3
CRITICAL 9.2→IGNORE 0.0
SEVERITY REDUCED
Run Wasmtime workloads on x86_64 instead of aarch64 — If your platform supports architecture selection (e.g., EC2 instance type change from Graviton to Intel/AMD), migrating Wasm workloads to x86_64 eliminates the vulnerability entirely. This is a valid interim control but has cost/performance implications. Deploy within 3 days if patching is delayed.
4
CRITICAL 9.2→HIGH 7.5
SEVERITY REDUCED
Wrap Wasmtime processes in a secondary OS-level sandbox — Run the Wasmtime process inside a minimal container with a strict seccomp profile and no network access. This does not prevent the Wasm sandbox escape but limits the blast radius of post-escape code execution. Useful as defense-in-depth but does NOT reduce the severity of the sandbox escape itself.
5
CRITICAL 9.2→MEDIUM 5.0
SEVERITY REDUCED
Validate submitted Wasm modules for oversized br_table — Implement a pre-compilation check that rejects Wasm modules containing any br_table with >200,000 targets. This can be done with wasm-tools validate plus a custom pass or a simple binary scan of the br_table immediate count. This is a fragile mitigation — the exact threshold depends on compile-time table layout — but provides a detection layer.
What doesn't work
  • WAF / network-level filtering — Wasmtime is a library, not a network service. There is no HTTP endpoint to filter. The malicious Wasm module is submitted through the application's own upload mechanism, which varies per platform.
  • Wasm validation / spec compliance checks — The oversized br_table is *valid WebAssembly*. Standard Wasm validators (wasm-tools validate, browser engines) will not flag it. The bug is in the compiler, not the Wasm module.
  • Switching to Winch compiler backend — While Winch would avoid the Cranelift-specific bug, Winch is not production-ready and generates significantly slower code. This is not a practical compensating control for production workloads.
  • ASLR / DEP on the host — The attacker's controlled branch lands within the already-mapped executable code region of the Wasmtime process. ASLR does not randomize offsets *within* a compiled code section, and DEP is irrelevant because the target is already executable code.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNot observed. Not listed on CISA KEV. No reports of active exploitation from Fastly, Fermyon, or other production operators as of 2026-10-27.
Proof-of-concept availabilityNo public PoC found. No repositories on GitHub named after CVE-2026-115893 or GHSA-mghf-wjfp-p5xm. Not indexed on pocindex.io. However, the fix commit (e61df1b) and advisory fully document the trigger condition (~256k br_table entries on aarch64), making PoC construction straightforward for a competent researcher.
EPSS0.00432 (low exploitation probability in the next 30 days). This reflects the niche runtime and lack of public exploit code, not the severity of successful exploitation.
KEV statusNot listed as of 2026-10-27.
CVSS vectorCVSS: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 metric: the attacker escapes the Wasm sandbox (the authorization scope) into the host OS (a different scope). PR:L reflects the need for a tenant/developer account to submit Wasm.
Affected versionsAll Wasmtime versions with aarch64 Cranelift support (since initial aarch64 support ~2020, before v1.0) through 36.0.18, 37.0.0–48.0.6, and 49.0.0–49.0.3. Wasmer is also affected via shared Cranelift dependency — fixed in Wasmer 7.5.1 (Cranelift 0.136.4).
Fixed versionsWasmtime 36.0.19, 48.0.7, 49.0.4, and 50.0.0+. Wasmer 7.5.1. Distro backports: NixOS tracking in #579126. RustSec advisory RUSTSEC-2026-0379 enables cargo audit detection.
Downstream consumersFastly Compute, Fermyon Cloud, Shopify Functions, wasmCloud, Envoy Wasm filters (if using Cranelift on ARM64), and any Rust crate depending on wasmtime or cranelift-codegen on aarch64.
Scanning / exposure dataWasmtime is a library/runtime, not a network service — it does not appear on Shodan/Censys/GreyNoise scans. Exposure depends entirely on the hosting platform's architecture. AWS Graviton adoption (~30%+ of new EC2) means a material fraction of cloud Wasm workloads run on aarch64.
Disclosure timelineReported by ilyas-mallah. Advisory published 2026-10-23 via GHSA-mghf-wjfp-p5xm. Patches released same day. RustSec advisory RUSTSEC-2026-0379 published concurrently.
Reporterilyas-mallah (credited in GHSA advisory)

Sources.

  1. GitHub Security Advisory (GHSA-mghf-wjfp-p5xm)
  2. Fix commit — Cranelift/aarch64: fix br_tables with very large target count
  3. RustSec Advisory RUSTSEC-2026-0379
  4. NixOS tracking issue #579126
  5. Wasmer issue #7117 — Cranelift 0.136.4 backport
  6. wasmCloud patch PR #5713
  7. VulDB entry CVE-2026-115893
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.2/10)

Why this verdict

  • Architecture gate is real but not disqualifying: The bug is aarch64-only, which removes all x86_64, s390x, and riscv64 deployments from scope. However, ARM64 adoption in cloud (AWS Graviton, Azure Cobalt, GCP Axion) is 30%+ of new compute instances, and edge/serverless Wasm platforms (Fastly Compute) preferentially deploy on ARM64 for cost efficiency. This narrows the population but does not drop below the 10% threshold for high-value-role installs.
  • Role multiplier: Wasmtime's canonical deployment role IS the security sandbox for untrusted code — analogous to a hypervisor. In multi-tenant serverless platforms (Fastly Compute serving NYT, Stripe, GitHub, Shopify; Fermyon Cloud), Wasmtime is *the* isolation boundary between tenants. A sandbox escape in this role yields tenant-to-host compromise with blast radius of *all co-located tenants' data and code*. This is fleet-scale / supply-chain-scale impact. Because the bug is IN the sandbox runtime itself, and ≥10% of production Wasmtime installs occupy this canonical high-value role on aarch64, the verdict floor is CRITICAL.
  • Trivial trigger, no PoC needed: The exploit condition (br_table with >256k entries) is a valid Wasm construct that passes all validators. The fix commit documents the exact mechanism. Any competent attacker can weaponize this without a published PoC. The EPSS of 0.004 reflects lack of a *published* exploit, not difficulty of exploitation.
  • No exploitation yet provides small downward pressure: No KEV listing, no observed in-the-wild use, no public PoC. This justifies a 0.7-point reduction from the vendor's 9.9 but does not break the CRITICAL floor given the canonical role analysis.
  • Scope: Changed is correctly applied: The Wasm sandbox and the host OS are distinct authorization scopes. The attacker crosses that boundary, which is the textbook definition of S:C in CVSS 3.1. This is not a theoretical scope change — it's the entire attack narrative.

Why not higher?

The vendor's 9.9 would be justified if all Wasmtime deployments were on aarch64 and accepting untrusted modules. The aarch64 restriction means a meaningful fraction of the install base (x86_64 deployments) is immune. Additionally, the absence of observed exploitation or public PoC provides a narrow window where defenders can patch before weaponization.

Why not lower?

This is a sandbox escape in a product whose *sole purpose* is sandboxing. The affected architecture (ARM64) is the preferred platform for the highest-value deployment pattern (multi-tenant serverless edge). The exploit trigger is trivially craftable within the Wasm spec. The attacker does not need any special access beyond what the product is designed to give them (Wasm module submission). Downgrading below CRITICAL would require evidence that <1% of production Wasmtime runs on aarch64, which contradicts Graviton/ARM64 cloud adoption data.

06 · Verification

Crowdsourced verification payload.

Run this script on each host running Wasmtime or Wasmer on aarch64. It checks both the CPU architecture and the installed wasmtime/cargo dependency version. Requires uname and either wasmtime --version or access to a Cargo.lock file. No elevated privileges needed. Example: bash check_cve_2026_115893.sh or bash check_cve_2026_115893.sh /path/to/Cargo.lock

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# CVE-2026-115893 checker — Wasmtime Cranelift aarch64 br_table sandbox escape
# Usage: bash check_cve_2026_115893.sh [path/to/Cargo.lock]
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

ARCH=$(uname -m)
STATUS="UNKNOWN"

# Only aarch64 is affected
if [[ "$ARCH" != "aarch64" && "$ARCH" != "arm64" ]]; then
  echo "PATCHED — architecture is $ARCH (only aarch64/arm64 affected)"
  exit 0
fi

echo "Architecture: $ARCH (affected platform)"

check_version() {
  local ver="$1"
  local major minor patch
  IFS='.' read -r major minor patch <<< "$ver"
  patch=${patch%%[-+]*}  # strip pre-release suffixes
  
  # Fixed in 36.0.19, 48.0.7, 49.0.4, 50.0.0+
  if (( major >= 50 )); then
    echo "PATCHED"
  elif (( major == 49 )); then
    if (( minor == 0 && patch >= 4 )); then echo "PATCHED"; else echo "VULNERABLE"; fi
  elif (( major == 48 )); then
    if (( minor == 0 && patch >= 7 )); then echo "PATCHED"; else echo "VULNERABLE"; fi
  elif (( major >= 37 && major <= 47 )); then
    echo "VULNERABLE"  # no patch for 37.x-47.x; must upgrade to 48.0.7+
  elif (( major == 36 )); then
    if (( minor == 0 && patch >= 19 )); then echo "PATCHED"; else echo "VULNERABLE"; fi
  elif (( major <= 35 )); then
    echo "VULNERABLE"  # all pre-36 versions with aarch64 support
  else
    echo "UNKNOWN"
  fi
}

# Method 1: wasmtime CLI
if command -v wasmtime &>/dev/null; then
  VER=$(wasmtime --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
  if [[ -n "$VER" ]]; then
    STATUS=$(check_version "$VER")
    echo "wasmtime CLI version: $VER → $STATUS"
  fi
fi

# Method 2: Cargo.lock (if path provided or found in cwd)
CARGO_LOCK="${1:-Cargo.lock}"
if [[ -f "$CARGO_LOCK" ]]; then
  # Extract wasmtime version from Cargo.lock
  VER=$(grep -A1 'name = "wasmtime"' "$CARGO_LOCK" | grep 'version' | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
  if [[ -n "$VER" ]]; then
    CARGO_STATUS=$(check_version "$VER")
    echo "Cargo.lock wasmtime version: $VER → $CARGO_STATUS"
    if [[ "$CARGO_STATUS" == "VULNERABLE" ]]; then
      STATUS="VULNERABLE"
    elif [[ "$STATUS" != "VULNERABLE" ]]; then
      STATUS="$CARGO_STATUS"
    fi
  fi
fi

# Method 3: cargo audit (if available)
if command -v cargo-audit &>/dev/null && [[ -f "$CARGO_LOCK" ]]; then
  if cargo audit --file "$CARGO_LOCK" 2>/dev/null | grep -q 'RUSTSEC-2026-0379'; then
    echo "cargo audit: RUSTSEC-2026-0379 detected"
    STATUS="VULNERABLE"
  fi
fi

echo ""
echo "=== RESULT: $STATUS ==="
case "$STATUS" in
  PATCHED)    exit 0 ;;
  VULNERABLE) exit 1 ;;
  *)          echo "Could not determine wasmtime version. Check manually."; exit 2 ;;
esac