Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions crates/environ/src/string_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,20 @@ impl fmt::Debug for StringPool {

impl TryClone for StringPool {
fn try_clone(&self) -> Result<Self, OutOfMemory> {
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)
}
}

Expand Down
33 changes: 33 additions & 0 deletions tests/all/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Loading