diff --git a/crates/environ/src/string_pool.rs b/crates/environ/src/string_pool.rs index f29a90edfd5e..f72a1060bdf7 100644 --- a/crates/environ/src/string_pool.rs +++ b/crates/environ/src/string_pool.rs @@ -73,10 +73,20 @@ impl fmt::Debug for StringPool { impl TryClone for StringPool { fn try_clone(&self) -> Result { - Ok(StringPool { - map: self.map.try_clone()?, - strings: self.strings.try_clone()?, - }) + let mut new_pool = StringPool::new(); + // Re-intern strings in index order so that each Atom value is + // identical in the clone — callers that hold Atoms from the original + // can use them interchangeably with the clone. + // + // Directly cloning `self.map` would copy &'static str keys that point + // into the *original* pool's `strings` allocation. Those pointers + // become dangling once the original is dropped, leading to UB on any + // subsequent lookup. Re-interning ensures the cloned map's keys point + // into the clone's own `strings`. + for s in self.strings.iter() { + new_pool.insert(s)?; + } + Ok(new_pool) } } diff --git a/tests/all/linker.rs b/tests/all/linker.rs index 0cb65f167cfb..39fb3773ebf2 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -774,3 +774,36 @@ fn linker_defines_table_subtype_err() -> Result<()> { Ok(()) } + +// Regression test for a soundness bug in StringPool::try_clone() introduced +// in wasmtime 43.0.0: cloning a StringPool (and thus a Linker) copied the +// map's &'static str keys that pointed into the *original* pool's string +// storage. After the original was dropped those keys became dangling, causing +// get_atom() on the clone to silently fail to find registered imports. +#[test] +fn linker_clone_drop_original_then_instantiate() -> Result<()> { + let engine = Engine::default(); + let wat = r#"(module + (import "env" "answer" (func (result i32))) + (func (export "run") (result i32) + call 0 + ) + )"#; + let module = Module::new(&engine, wat)?; + + let original = { + let mut l: Linker<()> = Linker::new(&engine); + l.func_wrap("env", "answer", || -> i32 { 42 })?; + l + }; + + let clone = original.clone(); + // Dropping the original must not invalidate the clone's string pool. + drop(original); + + let mut store = Store::new(&engine, ()); + let instance = clone.instantiate(&mut store, &module)?; + let f = instance.get_typed_func::<(), i32>(&mut store, "run")?; + assert_eq!(f.call(&mut store, ())?, 42); + Ok(()) +}