Skip to content

Commit f5f38fe

Browse files
authored
[37.0.x] Prevent using shared memories with Memory (#12019)
* Prevent using shared memories with `Memory` This commit fixes a few issues where it was possible to represent a wasm shared linear memory with the `wasmtime::Memory` type. This is not sound because `wasmtime::Memory` provides safe Rust access to the bytes where that is not possible with wasm shared memories. Shared memories in Rust must be represented by `SharedMemory`, not `wasmtime::Memory`. Specifically this commit prevents two vectors of this happening: 1. `Memory::new` now requires that the memory type specified is non-shared. Instead `SharedMemory::new` must be used instead. 2. Core dumps now skip over shared memories when iterating over all memories in the store. Supporting shared memories is a more invasive change and will happen on Wasmtime's `main` branch. * CI fixes
1 parent 48a4cd3 commit f5f38fe

File tree

7 files changed

+65
-4
lines changed

7 files changed

+65
-4
lines changed

RELEASES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## 37.0.3
2+
3+
Released 2025-11-11.
4+
5+
### Fixed
6+
7+
* Prevent using shared memories with `Memory`.
8+
[CVE-2025-64345](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-hc7m-r6v8-hg9q)
9+
10+
--------------------------------------------------------------------------------
11+
112
## 37.0.2
213

314
Released 2025-10-07.

crates/wasmtime/src/runtime/coredump.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ impl WasmCoreDump {
4141
pub(crate) fn new(store: &mut StoreOpaque, backtrace: WasmBacktrace) -> WasmCoreDump {
4242
let modules: Vec<_> = store.modules().all_modules().cloned().collect();
4343
let instances: Vec<Instance> = store.all_instances().collect();
44-
let store_memories: Vec<Memory> = store.all_memories().collect();
44+
let mut store_memories: Vec<Memory> = store.all_memories().collect();
45+
store_memories.retain(|m| !m.wasmtime_ty(store).shared);
4546

4647
let mut store_globals: Vec<Global> = vec![];
4748
store.for_each_global(|_store, global| store_globals.push(global));
@@ -268,7 +269,12 @@ impl WasmCoreDump {
268269
.all_memories(store.0)
269270
.collect::<Vec<_>>()
270271
.into_iter()
271-
.map(|(_i, memory)| memory_to_idx[&memory.hash_key(&store.0)])
272+
.map(|(_i, memory)| {
273+
memory_to_idx
274+
.get(&memory.hash_key(&store.0))
275+
.copied()
276+
.unwrap_or(u32::MAX)
277+
})
272278
.collect::<Vec<_>>();
273279

274280
let globals = instance

crates/wasmtime/src/runtime/memory.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,9 @@ impl Memory {
285285
limiter: Option<&mut StoreResourceLimiter<'_>>,
286286
ty: MemoryType,
287287
) -> Result<Memory> {
288+
if ty.is_shared() {
289+
bail!("shared memories must be created through `SharedMemory`")
290+
}
288291
generate_memory_export(store, limiter, &ty, None).await
289292
}
290293

crates/wast/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ workspace = true
1515

1616
[dependencies]
1717
anyhow = { workspace = true }
18-
wasmtime = { workspace = true, features = ['cranelift', 'wat', 'runtime', 'gc', 'async'] }
18+
wasmtime = { workspace = true, features = ['cranelift', 'wat', 'runtime', 'gc', 'async', 'threads'] }
1919
wast = { workspace = true, features = ['dwarf'] }
2020
log = { workspace = true }
2121
tokio = { workspace = true, features = ['rt'] }

crates/wast/src/spectest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ pub fn link_spectest<T>(
8080

8181
if config.use_shared_memory {
8282
let ty = MemoryType::shared(1, 1);
83-
let memory = Memory::new(&mut *store, ty)?;
83+
let memory = SharedMemory::new(store.engine(), ty)?;
8484
linker.define(&mut *store, "spectest", "shared_memory", memory)?;
8585
}
8686

tests/all/coredump.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,36 @@ fn multiple_globals_memories_and_instances() -> Result<()> {
258258

259259
Ok(())
260260
}
261+
262+
#[test]
263+
#[cfg_attr(miri, ignore)]
264+
fn core_dump_with_shared_memory() -> Result<()> {
265+
if super::threads::engine().is_none() {
266+
return Ok(());
267+
}
268+
let mut config = Config::new();
269+
config.coredump_on_trap(true);
270+
let engine = Engine::new(&config)?;
271+
let mut store = Store::new(&engine, ());
272+
let wat = r#"(module
273+
(memory 1 1 shared)
274+
(func (export "foo") unreachable)
275+
(data (i32.const 0) "a")
276+
)"#;
277+
let module = Module::new(&engine, wat)?;
278+
let instance = Instance::new(&mut store, &module, &[])?;
279+
let foo = instance.get_typed_func::<(), ()>(&mut store, "foo")?;
280+
let err = foo.call(&mut store, ()).unwrap_err();
281+
let coredump = err.downcast_ref::<WasmCoreDump>().unwrap();
282+
assert!(coredump.memories().is_empty());
283+
284+
let bytes = coredump.serialize(&mut store, "howdy");
285+
for payload in wasmparser::Parser::new(0).parse_all(&bytes) {
286+
let payload = payload?;
287+
if let wasmparser::Payload::DataSection(s) = payload {
288+
assert_eq!(s.count(), 0);
289+
}
290+
}
291+
292+
Ok(())
293+
}

tests/all/threads.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,11 @@ fn test_memory_size_accessibility() -> Result<()> {
295295

296296
Ok(())
297297
}
298+
299+
#[test]
300+
fn create_shared_memory_through_memory() -> Result<()> {
301+
let engine = Engine::default();
302+
let mut store = Store::new(&engine, ());
303+
assert!(Memory::new(&mut store, MemoryType::shared(1, 1)).is_err());
304+
Ok(())
305+
}

0 commit comments

Comments
 (0)