Fix a use-after-free of trampoline code
This commit fixes an issue with wasmtime where it was possible for a trampoline from one module to get used for another module after it was freed. This issue arises because we register a module's native trampolines *before* it's fully instantiated, which is a fallible process. Some fallibility is predictable, such as import type mismatches, but other fallibility is less predictable, such as failure to allocate a linear memory. The problem happened when a module was registered with a `Store`, retaining information about its trampolines, but then instantiation failed and the module's code was never persisted within the `Store`. Unlike as documented in #2374 the `Module` inside an `Instance` is not the primary way to hold on to a module's code, but rather the `Arc<ModuleCode>` is persisted within the global frame information off on the side. This persistence only made its way into the store through the `Box<Any>` field of `InstanceHandle`, but that's never made if instantiation fails during import matching. The fix here is to build on the refactoring of #2407 to not store module code in frame information but rather explicitly in the `Store`. Registration is now deferred until just-before an instance handle is created, and during module registration we insert the `Arc<ModuleCode>` into a set stored within the `Store`.
This commit is contained in:
@@ -522,3 +522,30 @@ fn externref_signature_no_reference_types() -> anyhow::Result<()> {
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trampolines_always_valid() -> anyhow::Result<()> {
|
||||
let func = {
|
||||
// Compile two modules up front
|
||||
let store = Store::default();
|
||||
let module1 = Module::new(store.engine(), "(module (import \"\" \"\" (func)))")?;
|
||||
let module2 = Module::new(store.engine(), "(module (func (export \"\")))")?;
|
||||
// Start instantiating the first module, but this will fail.
|
||||
// Historically this registered the module's trampolines with `Store`
|
||||
// before the failure, but then after the failure the `Store` didn't
|
||||
// hold onto the trampoline.
|
||||
drop(Instance::new(&store, &module1, &[]));
|
||||
drop(module1);
|
||||
|
||||
// Then instantiate another module which has the same function type (no
|
||||
// parameters or results) which tries to use the trampoline defined in
|
||||
// the previous module. Then we extract the function and, in another
|
||||
// scope where everything is dropped, we call the func.
|
||||
let i = Instance::new(&store, &module2, &[])?;
|
||||
i.get_func("").unwrap()
|
||||
};
|
||||
|
||||
// ... and no segfaults! right? right? ...
|
||||
func.call(&[])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user