CVE-2026-115893: Patch Wasmtime Sandbox Escape on Arm64

WebAssembly control-flow branch intercepted at a patched sandbox boundary protecting an Arm64 host

CVE-2026-115893 is a Wasmtime sandbox-escape vulnerability that affects Arm64 systems using the default Cranelift compiler backend. A user-controlled WebAssembly module can abuse an unusually large br_table instruction so the generated native code branches to the wrong target, potentially crossing the boundary between an isolated guest and its host process.

The Bytecode Alliance published the underlying security advisory on October 19, 2026. The CVE record followed on October 23, and the OSV entry was modified on October 28. Operators should treat this as an architecture-specific runtime issue: the highest priority is any Arm64 or aarch64 service that accepts WebAssembly from tenants, plugins, users, build jobs, or automation pipelines.

What goes wrong inside Cranelift

WebAssembly’s br_table instruction represents a multi-way branch. A runtime compiler must translate its target table into safe native machine code while preserving WebAssembly’s control-flow rules. According to the maintainer advisory, an Arm64 build with more than roughly 256,000 targets in one table can be lowered with an incorrect conditional branch target.

The bad branch can jump to other code at an offset influenced by the module. That breaks a central Wasmtime security assumption: untrusted guest code should remain constrained by the runtime’s sandbox. The advisory describes arbitrary sandbox escape as the possible outcome and assigns a critical severity rating. This article uses the vendor’s published score and does not invent a separate one.

The flaw is not a general WebAssembly language weakness. It is a code-generation error in the Cranelift backend on aarch64. That distinction matters for triage because architecture, compiler backend, runtime version, and module trust boundary all change practical exposure.

Affected versions and fixed branches

Wasmtime lineAffected rangeFixed destination
36.x maintenance lineEarlier than 36.0.1936.0.19 or later in the line
37.x through 48.x37.0.0 through versions earlier than 48.0.748.0.7 or later
49.x49.0.0 through 49.0.349.0.4 or later
Older releases with Arm64 supportAffected according to the maintainerMove to a supported fixed release

Wasmtime 36.0.19, 48.0.7, and 49.0.4 were released with the fix. The project also lists version 50.0.0 after those security releases. Select a supported branch that fits your application’s compatibility requirements rather than jumping between major versions without testing. Distribution packages may use backported patches, so compare the package advisory and build revision instead of relying only on the upstream semantic version.

Decide whether a workload is exposed

Confirm the host architecture

The published vulnerable path is specific to aarch64, commonly reported by Linux tools as aarch64 or arm64. Check the node that performs compilation and execution, not only the developer workstation or CI controller.

uname -m
dpkg --print-architecture 2>/dev/null
rpm --eval '%{_arch}' 2>/dev/null
docker image inspect --format '{{.Architecture}}' IMAGE:TAG

Find the real Wasmtime version

Wasmtime may appear as a command-line binary, a Rust crate, a vendored component, a language binding, or part of a larger platform. Inventory the deployed artifact and its lockfile or software bill of materials. A safe host binary does not prove that every container or statically linked service uses the same runtime.

wasmtime --version
cargo tree -i wasmtime
cargo metadata --format-version 1 --locked > cargo-metadata.json
rg 'name = "wasmtime"|version = ' Cargo.lock

# For a running container
docker exec CONTAINER wasmtime --version
docker inspect --format '{{.Image}} {{.Config.Image}}' CONTAINER

Map the trust boundary

Prioritize services that compile or run modules supplied outside the runtime’s administrative trust domain. Examples include plugin platforms, edge-compute workers, multi-tenant function services, policy engines, package analysis systems, CI jobs, and developer tools that preview uploaded modules. A service that executes only pinned, reviewed modules has a lower immediate exposure, but it still needs the fixed runtime because module provenance and deployment behavior can change.

  • Record who can submit or replace a module.
  • Identify whether modules arrive over a network, registry, repository, or shared storage path.
  • Confirm whether the engine uses Cranelift or a product-specific backend.
  • Document the host architecture for every replica and autoscaling pool.
  • Capture the deployed image digest, package revision, and runtime configuration.

Mitigation and patch sequence

  1. Pause or restrict untrusted module submission on affected Arm64 pools.
  2. Preserve current image digests, runtime versions, lockfiles, and configuration before changing the environment.
  3. Upgrade to 36.0.19, 48.0.7, 49.0.4, or a later supported release containing the fix.
  4. Rebuild statically linked applications and container images so the corrected crate is actually deployed.
  5. Roll out through a canary pool, exercise representative modules, and verify isolation controls.
  6. Replace every affected replica and confirm that autoscaling templates no longer reference an old image.

Temporarily moving untrusted workloads to a fixed x86_64 pool, disabling module uploads, or accepting only signed and approved modules can reduce exposure while patching proceeds. These are containment measures, not repairs. They also require careful checks for queue backlogs, tenant isolation, and accidental fallback to an affected Arm64 node.

For Kubernetes deployments, inspect node architecture labels, affinity rules, runtime images, and DaemonSet or operator-managed components. The site’s Kubernetes node-audit checklist provides a reusable pattern for validating node attributes before a fleet change. If guest modules can access shared host paths, also review the shared-filesystem isolation analysis so a runtime patch is not mistaken for complete workload isolation.

Hands-on lab: triage a mixed Wasmtime inventory

This lab is deliberately non-exploitative. It reads a local CSV and classifies systems by architecture, backend, module trust, and published fixed-version floor. It does not download, compile, or execute WebAssembly.

Prerequisites and isolated environment

  • Python 3.9 or later.
  • An empty directory on an analyst workstation or disposable VM.
  • An inventory containing no credentials, tokens, or tenant data.

Create inventory.csv:

asset,architecture,backend,accepts_untrusted_wasm,version
edge-arm,arm64,cranelift,yes,49.0.3
worker-arm,aarch64,cranelift,no,48.0.6
build-x86,x86_64,cranelift,yes,49.0.3
arm-alt,arm64,winch,yes,49.0.3
fixed-arm,arm64,cranelift,yes,49.0.4
old-arm,aarch64,cranelift,yes,36.0.18

Save the following as wasmtime_inventory.py:

#!/usr/bin/env python3
import csv, sys

def vt(value):
    try: return tuple(int(x) for x in value.lstrip("v").split(".")[:3])
    except ValueError: return None

def version_state(value):
    v = vt(value)
    if not v or len(v) != 3: return "MANUAL_REVIEW", "unparseable version"
    major = v[0]
    if major < 36: return "UPDATE", "all earlier Arm64 releases are affected"
    if major == 36: floor = (36, 0, 19)
    elif 37 <= major < 48: return "UPDATE", "37.x through 47.x are affected"
    elif major == 48: floor = (48, 0, 7)
    elif major == 49: floor = (49, 0, 4)
    else: return "CURRENT", "newer than published fixed branches"
    return (("UPDATE", "fixed floor is " + ".".join(map(str, floor)))
            if v < floor else
            ("CURRENT", "meets fixed floor " + ".".join(map(str, floor))))

def assess(row):
    arch = row["architecture"].lower()
    if arch not in {"arm64", "aarch64"}:
        return "NOT_MATCHING_ARCH", "advisory scope is Arm64/aarch64"
    if row["backend"].lower() != "cranelift":
        return "MANUAL_REVIEW", "confirm backend and product-specific exposure"
    state, detail = version_state(row["version"])
    if state == "UPDATE" and row["accepts_untrusted_wasm"].lower() == "yes":
        return "UPDATE_HIGH", detail
    if state == "UPDATE": return "UPDATE_LOWER_EXPOSURE", detail
    return state, detail

print("asset,version,status,detail")
with open(sys.argv[1], newline="", encoding="utf-8") as handle:
    for row in csv.DictReader(handle):
        status, detail = assess(row)
        print(f'{row["asset"]},{row["version"]},{status},{detail}')

Run the audit:

python3 wasmtime_inventory.py inventory.csv

Expected output

asset,version,status,detail
edge-arm,49.0.3,UPDATE_HIGH,fixed floor is 49.0.4
worker-arm,48.0.6,UPDATE_LOWER_EXPOSURE,fixed floor is 48.0.7
build-x86,49.0.3,NOT_MATCHING_ARCH,advisory scope is Arm64/aarch64
arm-alt,49.0.3,MANUAL_REVIEW,confirm backend and product-specific exposure
fixed-arm,49.0.4,CURRENT,meets fixed floor 49.0.4
old-arm,36.0.18,UPDATE_HIGH,fixed floor is 36.0.19

A NOT_MATCHING_ARCH result applies only to this specific advisory and is not a general security verdict. MANUAL_REVIEW exists because embedded products and alternate backends may carry different risk or vendor guidance.

Troubleshooting and cleanup

  • If the script reports missing columns, confirm the CSV header exactly matches the example.
  • If a distribution version includes suffixes, record its package release and consult the distribution security tracker.
  • If Wasmtime is statically linked, use the build lockfile, SBOM, or binary provenance rather than a host package query.
  • If the backend is unknown, do not assume Cranelift or declare the service safe.
rm -f inventory.csv wasmtime_inventory.py cargo-metadata.json

Detection and post-upgrade validation

The maintainer advisory does not provide network indicators or a malicious module hash. Monitor for unexpected Wasmtime host crashes, illegal-instruction or segmentation-fault events, abnormal JIT compilation failures, sandbox policy alerts, and unusual large modules submitted to exposed services. None of these signals proves exploitation, so correlate them with module provenance, authentication records, request timing, runtime version, architecture, and crash telemetry.

If suspicious activity predates the upgrade, preserve the module bytes and hash without executing them, capture the affected image digest and process metadata, and follow the product’s incident-response procedure. A sandbox escape can affect the host process, so investigation should extend beyond the guest’s expected files and logs.

  • Confirm every Arm64 replica reports a fixed build.
  • Verify the new container image digest is present in deployment templates and registries.
  • Run known-good module regression tests through the same compiler path.
  • Test guest resource limits, filesystem permissions, network policies, and syscall controls.
  • Confirm rollback images do not reintroduce the vulnerable runtime.

Conclusion

CVE-2026-115893 shows why a sandbox is only as strong as the native code generated beneath it. The practical response is to find Arm64 Wasmtime deployments, identify Cranelift-backed services that accept untrusted modules, move them to a fixed branch, rebuild every embedded artifact, and validate both runtime functionality and surrounding host controls. Architecture-aware inventory is the difference between a precise response and a noisy fleet-wide guess.

Primary sources

Similar Posts