Make module information lookup from runtime safe.

This commit uses a two-phase lookup of stack map information from modules
rather than giving back raw pointers to stack maps.

First the runtime looks up information about a module from a pc value, which
returns an `Arc` it keeps a reference on while completing the stack map lookup.

Second it then queries the module information for the stack map from a pc
value, getting a reference to the stack map (which is now safe because of the
`Arc` held by the runtime).
This commit is contained in:
Peter Huene
2021-04-16 12:05:38 -07:00
parent 6ac1321162
commit b775b68cfb
15 changed files with 116 additions and 112 deletions

View File

@@ -207,7 +207,7 @@ unsafe impl WasmTy for Option<ExternRef> {
unsafe {
store
.externref_activations_table()
.insert_with_gc(x.inner, store.stack_map_lookup());
.insert_with_gc(x.inner, store.module_info_lookup());
}
abi
} else {

View File

@@ -524,7 +524,7 @@ impl<'a> Instantiator<'a> {
externref_activations_table: self.store.externref_activations_table()
as *const VMExternRefActivationsTable
as *mut _,
stack_map_lookup: Some(self.store.stack_map_lookup()),
module_info_lookup: Some(self.store.module_info_lookup()),
})?;
// After we've created the `InstanceHandle` we still need to run

View File

@@ -6,10 +6,13 @@ use std::{
sync::{Arc, Mutex},
};
use wasmtime_environ::{
entity::EntityRef, ir, wasm::DefinedFuncIndex, FunctionAddressMap, TrapInformation,
entity::EntityRef,
ir::{self, StackMap},
wasm::DefinedFuncIndex,
FunctionAddressMap, TrapInformation,
};
use wasmtime_jit::CompiledModule;
use wasmtime_runtime::{VMCallerCheckedAnyfunc, VMTrampoline};
use wasmtime_runtime::{ModuleInfo, VMCallerCheckedAnyfunc, VMTrampoline};
lazy_static::lazy_static! {
static ref GLOBAL_MODULES: Mutex<GlobalModuleRegistry> = Default::default();
@@ -27,7 +30,7 @@ fn func_by_pc(module: &CompiledModule, pc: usize) -> Option<(DefinedFuncIndex, u
///
/// The `BTreeMap` is used to quickly locate a module based on a program counter value.
#[derive(Default)]
pub struct ModuleRegistry(BTreeMap<usize, RegisteredModule>);
pub struct ModuleRegistry(BTreeMap<usize, Arc<RegisteredModule>>);
impl ModuleRegistry {
/// Fetches frame information about a program counter in a backtrace.
@@ -48,12 +51,13 @@ impl ModuleRegistry {
self.module(pc)?.lookup_trap_info(pc)
}
/// Looks up a stack map from a program counter.
pub fn lookup_stack_map<'a>(&'a self, pc: usize) -> Option<&'a ir::StackMap> {
self.module(pc)?.lookup_stack_map(pc)
/// Fetches information about a registered module given a program counter value.
pub fn lookup_module(&self, pc: usize) -> Option<Arc<dyn ModuleInfo>> {
self.module(pc)
.map(|m| -> Arc<dyn ModuleInfo> { m.clone() })
}
fn module(&self, pc: usize) -> Option<&RegisteredModule> {
fn module(&self, pc: usize) -> Option<&Arc<RegisteredModule>> {
let (end, info) = self.0.range(pc..).next()?;
if pc < info.start || *end < pc {
return None;
@@ -94,11 +98,11 @@ impl ModuleRegistry {
let prev = self.0.insert(
end,
RegisteredModule {
Arc::new(RegisteredModule {
start,
module: compiled_module.clone(),
signatures: module.signatures().clone(),
},
}),
);
assert!(prev.is_none());
@@ -209,8 +213,29 @@ impl RegisteredModule {
Some(&info.traps[idx])
}
/// Looks up a stack map from a program counter
pub fn lookup_stack_map(&self, pc: usize) -> Option<&ir::StackMap> {
fn instr_pos(offset: u32, addr_map: &FunctionAddressMap) -> Option<usize> {
// Use our relative position from the start of the function to find the
// machine instruction that corresponds to `pc`, which then allows us to
// map that to a wasm original source location.
match addr_map
.instructions
.binary_search_by_key(&offset, |map| map.code_offset)
{
// Exact hit!
Ok(pos) => Some(pos),
// This *would* be at the first slot in the array, so no
// instructions cover `pc`.
Err(0) => None,
// This would be at the `nth` slot, so we're at the `n-1`th slot.
Err(n) => Some(n - 1),
}
}
}
impl ModuleInfo for RegisteredModule {
fn lookup_stack_map(&self, pc: usize) -> Option<&StackMap> {
let (index, offset) = func_by_pc(&self.module, pc)?;
let info = self.module.func_info(index);
@@ -275,26 +300,6 @@ impl RegisteredModule {
Some(&info.stack_maps[index].stack_map)
}
fn instr_pos(offset: u32, addr_map: &FunctionAddressMap) -> Option<usize> {
// Use our relative position from the start of the function to find the
// machine instruction that corresponds to `pc`, which then allows us to
// map that to a wasm original source location.
match addr_map
.instructions
.binary_search_by_key(&offset, |map| map.code_offset)
{
// Exact hit!
Ok(pos) => Some(pos),
// This *would* be at the first slot in the array, so no
// instructions cover `pc`.
Err(0) => None,
// This would be at the `nth` slot, so we're at the `n-1`th slot.
Err(n) => Some(n - 1),
}
}
}
// Counterpart to `RegisteredModule`, but stored in the global registry.

View File

@@ -16,9 +16,9 @@ use std::rc::Rc;
use std::sync::Arc;
use std::task::{Context, Poll};
use wasmtime_runtime::{
InstanceAllocator, InstanceHandle, OnDemandInstanceAllocator, SignalHandler, TrapInfo,
VMCallerCheckedAnyfunc, VMContext, VMExternRef, VMExternRefActivationsTable, VMInterrupts,
VMTrampoline,
InstanceAllocator, InstanceHandle, ModuleInfo, OnDemandInstanceAllocator, SignalHandler,
TrapInfo, VMCallerCheckedAnyfunc, VMContext, VMExternRef, VMExternRefActivationsTable,
VMInterrupts, VMTrampoline,
};
/// Used to associate instances with the store.
@@ -440,7 +440,7 @@ impl Store {
}
#[inline]
pub(crate) fn stack_map_lookup(&self) -> &dyn wasmtime_runtime::StackMapLookup {
pub(crate) fn module_info_lookup(&self) -> &dyn wasmtime_runtime::ModuleInfoLookup {
self.inner.as_ref()
}
@@ -910,10 +910,9 @@ impl Drop for StoreInner {
}
}
unsafe impl wasmtime_runtime::StackMapLookup for StoreInner {
fn lookup(&self, pc: usize) -> Option<*const wasmtime_environ::ir::StackMap> {
// The address of the stack map is stable for the lifetime of the store
self.modules.borrow().lookup_stack_map(pc).map(|m| m as _)
impl wasmtime_runtime::ModuleInfoLookup for StoreInner {
fn lookup(&self, pc: usize) -> Option<Arc<dyn ModuleInfo>> {
self.modules.borrow().lookup_module(pc)
}
}

View File

@@ -77,7 +77,7 @@ fn create_handle(
externref_activations_table: store.externref_activations_table()
as *const VMExternRefActivationsTable
as *mut _,
stack_map_lookup: Some(store.stack_map_lookup()),
module_info_lookup: Some(store.module_info_lookup()),
},
)?;

View File

@@ -43,7 +43,7 @@ pub(crate) fn create_handle(
externref_activations_table: store.externref_activations_table()
as *const VMExternRefActivationsTable
as *mut _,
stack_map_lookup: &store,
module_info_lookup: &store,
})?;
Ok(store.add_instance(handle, true))

View File

@@ -287,7 +287,7 @@ pub fn create_function(
host_state: Box::new(trampoline_state),
interrupts: std::ptr::null(),
externref_activations_table: std::ptr::null_mut(),
stack_map_lookup: None,
module_info_lookup: None,
})?,
trampoline,
))
@@ -319,7 +319,7 @@ pub unsafe fn create_raw_function(
host_state,
interrupts: std::ptr::null(),
externref_activations_table: std::ptr::null_mut(),
stack_map_lookup: None,
module_info_lookup: None,
})?,
)
}

View File

@@ -98,7 +98,7 @@ impl Val {
let externref_ptr = x.inner.as_raw();
store
.externref_activations_table()
.insert_with_gc(x.inner, store.stack_map_lookup());
.insert_with_gc(x.inner, store.module_info_lookup());
ptr::write(p as *mut *mut u8, externref_ptr)
}
Val::FuncRef(f) => ptr::write(