Cloudanix VulnDB

CVE-2024-47813

Low · 2.9

Wasmtime race condition could lead to WebAssembly control-flow integrity and type safety violations

CVSS

2.9

EPSS

0.2%

Published

Oct 9, 2024

CISA due date

—

Cloudanix analysis

Monitor only SSVC: Track

A rare timing problem in a WebAssembly engine used by some applications could cause crashes or let functions be called incorrectly when multiple tasks run at the same time, though most users and normal web code are not affected.

Chainable
Exploitation
Other
Attacker needs
Other
Complexity
Theoretical
Impact
Other
Component
Library
Class
Race Condition
Remediation
Upgrade
Effort
Medium
Patch risk
Low

What it actually does

Race condition in wasmtime::Engine type registry allows double-unregistration of type entries under concurrent creation and dropping of types (FuncType, ArrayType, etc.) across threads sharing an Engine, enabling registry corruption that can violate WebAssembly CFI and type safety on subsequent module loads and call_indirect operations.

Why it matters to the business

Multi-threaded applications using Wasmtime across threads risk process panics or, in rare cases, type safety violations that could allow unintended cross-module function calls; single-threaded or sequential module creation workloads are unaffected, limiting exposure to specific concurrent host-side type management patterns.

How an attack unfolds

An attacker with control over host threads must concurrently create and drop multiple type instances on the same Engine to trigger the TOCTOU double-unregistration; under specific follow-on module loads that reuse the corrupted registry slot, a funcref of mismatched type can pass a call_indirect check, violating CFI. Wasm guests cannot initiate the race, and reproduction in unmodified builds has not been achieved.

How to fix it

Upgrade bytecodealliance/wasmtime to commit 70ceccd40ecf882908571933b996690f359f0021 or later; users unable to upgrade should ensure all type creation and module instantiation occurs on a single thread or serialize operations that create and drop types.

How to confirm the fix worked

Confirm the presence of commit 70ceccd40ecf882908571933b996690f359f0021 in the Wasmtime source tree; run the project's concurrency-focused type registry tests under thread sanitizer; verify that Engine instances used across threads no longer exhibit panics or incorrect type checks after the fix.

Typical attack path

  1. Obtain concurrent access to a shared wasmtime::Engine from multiple host threads
  2. Simultaneously create and drop FuncType, ArrayType, or equivalent objects across threads until the registration count TOCTOU triggers double-unregistration
  3. Load a new module X defining a function type that reuses the freed registry slot
  4. Load a subsequent module Y defining a different function type in the same slot
  5. Create a funcref of the second type and pass it to an instance of the first module
  6. Trigger call_indirect to invoke a function with mismatched signature

How to detect it

Review application logs for Wasmtime internal panics mentioning type registry, double-unregistration, or CFI assertions; inspect thread sanitizer or data-race detector output from builds that create and drop types concurrently; no post-exploitation artifacts are expected because the flaw is a host-side race rather than a persistent compromise.

What your security team should do

  • Identify all deployments that share a single Engine across threads and prioritize upgrade to the fixed commit
  • Audit host code for concurrent type creation/dropping patterns even if Wasm guests cannot trigger the issue
  • Add runtime monitoring for Wasmtime panics that reference type registry or internal assertion failures
  • Test multi-threaded workloads with the updated version before production deployment

The record

Under certain concurrent event orderings, a `wasmtime::Engine`'s internal type registry was susceptible to double-unregistration bugs due to a race condition, leading to panics and potentially type registry corruption. That registry corruption could, following an additional and particular sequence of concurrent events, lead to violations of WebAssembly's control-flow integrity (CFI) and type safety. Users that do not use `wasmtime::Engine` across multiple threads are not affected. Users that only create new modules across threads over time are additionally not affected. Reproducing this bug requires creating and dropping multiple type instances (such as `wasmtime::FuncType` or `wasmtime::ArrayType`) concurrently on multiple threads, where all types are associated with the same `wasmtime::Engine`. **Wasm guests cannot trigger this bug.** See the "References" section below for a list of Wasmtime types-related APIs that are affected. Wasmtime maintains an internal registry of types within a `wasmtime::Engine` and an engine is shareable across threads. Types can be created and referenced through creation of a `wasmtime::Module`, creation of `wasmtime::FuncType`, or a number of other APIs where the host creates a function (see "References" below). Each of these cases interacts with an engine to deduplicate type information and manage type indices that are used to implement type checks in WebAssembly's `call_indirect` function, for example. This bug is a race condition in this management where the internal type registry could be corrupted to trigger an assert or contain invalid state. Wasmtime's internal representation of a type has individual types (e.g. one-per-host-function) maintain a registration count of how many time it's been used. Types additionally have state within an engine behind a read-write lock such as lookup/deduplication information. The race here is a time-of-check versus time-of-use (TOCTOU) bug where one thread atomically decrements a type entry's registration count, observes zero registrations, and then acquires a lock in order to unregister that entry. However, between when this first thread observed the zero-registration count and when it acquires that lock, another thread could perform the following sequence of events: re-register another copy of the type, which deduplicates to that same entry, resurrecting it and incrementing its registration count; then drop the type and decrement its registration count; observe that the registration count is now zero; acquire the type registry lock; and finally unregister the type. Now, when the original thread finally acquires the lock and unregisters the entry, it is the second time this entry has been unregistered. | Thread A | Thread B | |-----------------------------------|--------------------------------| | `acquire(type registry lock)` | | | | `decref(E) --> 0` | | | `block_on(type registry lock)` | | `register(E') == incref(E) --> 1` | | | `release(type registry lock)` | | | `decref(E) --> 0` | | | `acquire(type registry lock)` | | | `unregister(E)` | | | `release(type registry lock)` | | | | `acquire(type registry lock)` | | | `unregister(E)` | This double-unregistration could then lead to a WebAssembly CFI violation under the following conditions: a new WebAssembly module `X` was loaded into the engine before the second, buggy unregistration occurs; `X` defined a function type `F` that was allocated in the same type registry slot where the original entry was allocated; the second, buggy unregistration incorrectly unregistered `F`; another new WebAssembly module `Y` was loaded into the engine; `Y` defined a function type `G`, different from `F`, but which is also allocated in the same type registry slot; a `funcref` of type `G` is created, either by the host or by Wasm; that `funcref` is passed to a WebAssembly instance of module `X`; that instance performs a `call_indirect` to that `funcref`; the `call_indirect`'s dynamic type check, which preserves CFI, could incorrectly pass in this case, because `F` and `G` were assigned the same type registry slot. This would, ultimately, allow calling a function with too many, too few, or wrongly-typed arguments, violating CFI and type safety. We were not able to reproduce this CFI violation in a vanilla Wasmtime build, although it remains theoretically possible. However, by modifying Wasmtime's source code to make losing the races described above more likely (by disabling certain assertions, inserting panic catches, and adding retry loops in a few places if we did *not* lose the race) we were able to incorrectly get a `funcref` to pass a type check that it should have failed, which would allow the CFI violation.

Affected packages

Ecosystem Package Introduced Fixed
crates.io wasmtime 19.0.0 21.0.2
crates.io wasmtime 22.0.0 22.0.1
crates.io wasmtime 23.0.0 23.0.3
crates.io wasmtime 24.0.0 24.0.1
crates.io wasmtime 25.0.0 25.0.2
Chainguard wash 0 0.34.1-r2
Chainguard wasmcloud 0 1.3.0-r1
Chainguard wizer 0 7.0.5-r1
Chainguard zed 0 0.156.0-r1
Debian:13 rust-wasmtime 0 21.0.2+dfsg-1
Debian:14 rust-wasmtime 0 21.0.2+dfsg-1
Ubuntu:24.04:LTS rust-wasmtime 0 No fix
Ubuntu:24.10 rust-wasmtime 0 No fix
Ubuntu:25.04 rust-wasmtime 0 No fix
Ubuntu:25.10 rust-wasmtime 0 No fix
Wolfi wash 0 0.34.1-r2
Wolfi wasmcloud 0 1.3.0-r1
Wolfi wizer 0 7.0.5-r1
Wolfi zed 0 0.156.0-r1
cargo wasmtime 19.0.0 21.0.2
cargo wasmtime 22.0.0 22.0.1
cargo wasmtime 23.0.0 23.0.3
cargo wasmtime 24.0.0 24.0.1
cargo wasmtime 25.0.0 25.0.2

Related vulnerabilities