From ad3d7de47723c40d91f5d8fb00feb2c7411d3b05 Mon Sep 17 00:00:00 2001 From: Ayush Yadav Date: Sat, 24 Oct 2026 02:00:15 -0400 Subject: [PATCH] Don't panic on an out-of-bounds fault in an imported shared memory `Store::wasm_fault` asserted that only one instance claims the faulting address. Importing a shared memory through `SharedMemory::vmimport` creates a single-memory instance that defines the same memory, so a fault in it was claimed twice and hit the assert instead of trapping. Only panic now when the address belongs to two different memories. Fixes #14584 --- crates/wasmtime/src/runtime/store.rs | 15 ++++++++++++--- .../threads/shared-memory-imported-oob.wast | 10 ++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 tests/misc_testsuite/threads/shared-memory-imported-oob.wast diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs index 1041750ada77..fca2e3a652c4 100644 --- a/crates/wasmtime/src/runtime/store.rs +++ b/crates/wasmtime/src/runtime/store.rs @@ -2055,11 +2055,20 @@ impl StoreOpaque { // If this loop becomes hot in the future, however, it should be // possible to precompute maps about linear memories in a store and have // a quicker lookup. - let mut fault = None; + // + // A shared memory can be defined by more than one instance in a store, + // for example when it's imported through the single-memory instance + // that `SharedMemory::vmimport` creates. Each of those instances + // reports the same fault, so only assert that the faulting address + // doesn't belong to two different memories. The memory size isn't + // compared because a shared memory can grow concurrently. + let mut fault: Option = None; for (_, instance) in self.instances.iter() { if let Some(f) = instance.handle.get().wasm_fault(addr) { - assert!(fault.is_none()); - fault = Some(f); + match &fault { + Some(prev) => assert_eq!(prev.wasm_address, f.wasm_address), + None => fault = Some(f), + } } } if fault.is_some() { diff --git a/tests/misc_testsuite/threads/shared-memory-imported-oob.wast b/tests/misc_testsuite/threads/shared-memory-imported-oob.wast new file mode 100644 index 000000000000..9119bfcb4ec1 --- /dev/null +++ b/tests/misc_testsuite/threads/shared-memory-imported-oob.wast @@ -0,0 +1,10 @@ +;;! threads = true + +;; A shared memory defined in one instance and imported into another must +;; still report an out-of-bounds access as a trap. +(module $a + (memory (export "m") 1 1 shared) + (func (export "ld") (param i32) (result i32) (i32.load (local.get 0)))) +(register "a" $a) +(module $b (import "a" "m" (memory 1 1 shared))) +(assert_trap (invoke $a "ld" (i32.const 0x10000)) "out of bounds memory access")