Revamp memory management of InstanceHandle (#1624)
* Revamp memory management of `InstanceHandle` This commit fixes a known but in Wasmtime where an instance could still be used after it was freed. Unfortunately the fix here is a bit of a hammer, but it's the best that we can do for now. The changes made in this commit are: * A `Store` now stores all `InstanceHandle` objects it ever creates. This keeps all instances alive unconditionally (along with all host functions and such) until the `Store` is itself dropped. Note that a `Store` is reference counted so basically everything has to be dropped to drop anything, there's no longer any partial deallocation of instances. * The `InstanceHandle` type's own reference counting has been removed. This is largely redundant with what's already happening in `Store`, so there's no need to manage two reference counts. * Each `InstanceHandle` no longer tracks its dependencies in terms of instance handles. This set was actually inaccurate due to dynamic updates to tables and such, so we needed to revamp it anyway. * Initialization of an `InstanceHandle` is now deferred until after `InstanceHandle::new`. This allows storing the `InstanceHandle` before side-effectful initialization, such as copying element segments or running the start function, to ensure that regardless of the result of instantiation the underlying `InstanceHandle` is still available to persist in storage. Overall this should fix a known possible way to safely segfault Wasmtime today (yay!) and it should also fix some flaikness I've seen on CI. Turns out one of the spec tests (bulk-memory-operations/partial-init-table-segment.wast) exercises this functionality and we were hitting sporating use-after-free, but only on Windows. * Shuffle some APIs around * Comment weak cycle
This commit is contained in:
@@ -2,14 +2,13 @@
|
||||
|
||||
use crate::resolver::Resolver;
|
||||
use more_asserts::assert_ge;
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryInto;
|
||||
use wasmtime_environ::entity::PrimaryMap;
|
||||
use wasmtime_environ::wasm::{Global, GlobalInit, Memory, Table, TableElementType};
|
||||
use wasmtime_environ::{EntityIndex, MemoryPlan, MemoryStyle, Module, TablePlan};
|
||||
use wasmtime_runtime::{
|
||||
Export, Imports, InstanceHandle, LinkError, SignatureRegistry, VMFunctionImport,
|
||||
VMGlobalImport, VMMemoryImport, VMTableImport,
|
||||
Export, Imports, LinkError, SignatureRegistry, VMFunctionImport, VMGlobalImport,
|
||||
VMMemoryImport, VMTableImport,
|
||||
};
|
||||
|
||||
/// This function allows to match all imports of a `Module` with concrete definitions provided by
|
||||
@@ -21,8 +20,6 @@ pub fn resolve_imports(
|
||||
signatures: &SignatureRegistry,
|
||||
resolver: &mut dyn Resolver,
|
||||
) -> Result<Imports, LinkError> {
|
||||
let mut dependencies = HashSet::new();
|
||||
|
||||
let mut function_imports = PrimaryMap::with_capacity(module.local.num_imported_funcs);
|
||||
let mut table_imports = PrimaryMap::with_capacity(module.local.num_imported_tables);
|
||||
let mut memory_imports = PrimaryMap::with_capacity(module.local.num_imported_memories);
|
||||
@@ -45,7 +42,6 @@ pub fn resolve_imports(
|
||||
module_name, field_name, signature, import_signature
|
||||
)));
|
||||
}
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(f.vmctx) });
|
||||
function_imports.push(VMFunctionImport {
|
||||
body: f.address,
|
||||
vmctx: f.vmctx,
|
||||
@@ -73,7 +69,6 @@ pub fn resolve_imports(
|
||||
module_name, field_name,
|
||||
)));
|
||||
}
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(t.vmctx) });
|
||||
table_imports.push(VMTableImport {
|
||||
from: t.definition,
|
||||
vmctx: t.vmctx,
|
||||
@@ -115,7 +110,6 @@ pub fn resolve_imports(
|
||||
}
|
||||
assert_ge!(m.memory.offset_guard_size, import_memory.offset_guard_size);
|
||||
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(m.vmctx) });
|
||||
memory_imports.push(VMMemoryImport {
|
||||
from: m.definition,
|
||||
vmctx: m.vmctx,
|
||||
@@ -143,7 +137,6 @@ pub fn resolve_imports(
|
||||
module_name, field_name
|
||||
)));
|
||||
}
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(g.vmctx) });
|
||||
global_imports.push(VMGlobalImport { from: g.definition });
|
||||
}
|
||||
(EntityIndex::Global(_), Some(_)) => {
|
||||
@@ -162,7 +155,6 @@ pub fn resolve_imports(
|
||||
}
|
||||
|
||||
Ok(Imports::new(
|
||||
dependencies,
|
||||
function_imports,
|
||||
table_imports,
|
||||
memory_imports,
|
||||
|
||||
@@ -204,21 +204,11 @@ impl CompiledModule {
|
||||
/// See `InstanceHandle::new`
|
||||
pub unsafe fn instantiate(
|
||||
&self,
|
||||
is_bulk_memory: bool,
|
||||
resolver: &mut dyn Resolver,
|
||||
sig_registry: &SignatureRegistry,
|
||||
mem_creator: Option<&dyn RuntimeMemoryCreator>,
|
||||
max_wasm_stack: usize,
|
||||
host_state: Box<dyn Any>,
|
||||
) -> Result<InstanceHandle, InstantiationError> {
|
||||
let data_initializers = self
|
||||
.data_initializers
|
||||
.iter()
|
||||
.map(|init| DataInitializer {
|
||||
location: init.location.clone(),
|
||||
data: &*init.data,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let imports = resolve_imports(&self.module, &sig_registry, resolver)?;
|
||||
InstanceHandle::new(
|
||||
Arc::clone(&self.module),
|
||||
@@ -226,16 +216,24 @@ impl CompiledModule {
|
||||
self.trampolines.clone(),
|
||||
imports,
|
||||
mem_creator,
|
||||
&data_initializers,
|
||||
self.signatures.clone(),
|
||||
self.dbg_jit_registration.as_ref().map(|r| Rc::clone(&r)),
|
||||
is_bulk_memory,
|
||||
host_state,
|
||||
self.interrupts.clone(),
|
||||
max_wasm_stack,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns data initializers to pass to `InstanceHandle::initialize`
|
||||
pub fn data_initializers(&self) -> Vec<DataInitializer<'_>> {
|
||||
self.data_initializers
|
||||
.iter()
|
||||
.map(|init| DataInitializer {
|
||||
location: init.location.clone(),
|
||||
data: &*init.data,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Return a reference-counting pointer to a module.
|
||||
pub fn module(&self) -> &Arc<Module> {
|
||||
&self.module
|
||||
|
||||
Reference in New Issue
Block a user