Reel in unsafety around InstanceHandle (#856)

* Reel in unsafety around `InstanceHandle`

This commit is an attempt, or at least is targeted at being a start, at
reeling in the unsafety around the `InstanceHandle` type. Currently this
type represents a sort of moral `Rc<Instance>` but is a bit more
specialized since the underlying memory is allocated through mmap.

Additionally, though, `InstanceHandle` exposes a fundamental flaw in its
safety by safetly allowing mutable access so long as you have `&mut
InstanceHandle`. This type, however, is trivially created by simply
cloning a `InstanceHandle` to get an owned reference. This means that
`&mut InstanceHandle` does not actually provide any guarantees about
uniqueness, so there's no more safety than `&InstanceHandle` itself.

This commit removes all `&mut self` APIs from `InstanceHandle`,
additionally removing some where `&self` was `unsafe` and `&mut self`
was safe (since it was trivial to subvert this "safety"). In doing so
interior mutability patterns are now used much more extensively through
structures such as `Table` and `Memory`. Additionally a number of
methods were refactored to be a bit clearer and use helper functions
where possible.

This is a relatively large commit unfortunately, but it snowballed very
quickly into touching quite a few places. My hope though is that this
will prevent developers working on wasmtime internals as well as
developers still yet to migrate to the `wasmtime` crate from falling
into trivial unsafe traps by accidentally using `&mut` when they can't.
All existing users relying on `&mut` will need to migrate to some form
of interior mutability, such as using `RefCell` or `Cell`.

This commit also additionally marks `InstanceHandle::new` as an `unsafe`
function. The rationale for this is that the `&mut`-safety is only the
beginning for the safety of `InstanceHandle`. In general the wasmtime
internals are extremely unsafe and haven't been audited for appropriate
usage of `unsafe`. Until that's done it's hoped that we can warn users
with this `unsafe` constructor and otherwise push users to the
`wasmtime` crate which we know is safe.

* Fix windows build

* Wrap up mutable memory state in one structure

Rather than having separate fields

* Use `Cell::set`, not `Cell::replace`, where possible

* Add a helper function for offsets from VMContext

* Fix a typo from merging

* rustfmt

* Use try_from, not as

* Tweak style of some setters
This commit is contained in:
Alex Crichton
2020-01-24 14:20:35 -06:00
committed by GitHub
parent 3db1074c15
commit 47d6db0be8
18 changed files with 490 additions and 692 deletions

View File

@@ -420,12 +420,9 @@ fn set_table_item(
item_index: u32, item_index: u32,
item: wasmtime_runtime::VMCallerCheckedAnyfunc, item: wasmtime_runtime::VMCallerCheckedAnyfunc,
) -> Result<()> { ) -> Result<()> {
if let Some(item_ref) = handle.table_get_mut(table_index, item_index) { handle
*item_ref = item; .table_set(table_index, item_index, item)
Ok(()) .map_err(|()| anyhow!("table element index out of bounds"))
} else {
bail!("table element index out of bounds")
}
} }
impl Table { impl Table {

View File

@@ -25,6 +25,7 @@ fn instantiate(
imports: &[Extern], imports: &[Extern],
) -> Result<InstanceHandle, Error> { ) -> Result<InstanceHandle, Error> {
let mut resolver = SimpleResolver { imports }; let mut resolver = SimpleResolver { imports };
unsafe {
let instance = compiled_module let instance = compiled_module
.instantiate(&mut resolver) .instantiate(&mut resolver)
.map_err(|e| -> Error { .map_err(|e| -> Error {
@@ -38,6 +39,7 @@ fn instantiate(
})?; })?;
Ok(instance) Ok(instance)
} }
}
/// An instantiated WebAssembly module. /// An instantiated WebAssembly module.
/// ///
@@ -108,7 +110,7 @@ impl Instance {
/// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727 /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727
pub fn new(module: &Module, imports: &[Extern]) -> Result<Instance, Error> { pub fn new(module: &Module, imports: &[Extern]) -> Result<Instance, Error> {
let store = module.store(); let store = module.store();
let mut instance_handle = instantiate(module.compiled_module(), imports)?; let instance_handle = instantiate(module.compiled_module(), imports)?;
let exports = { let exports = {
let mut exports = Vec::with_capacity(module.exports().len()); let mut exports = Vec::with_capacity(module.exports().len());
@@ -179,9 +181,8 @@ impl Instance {
pub fn from_handle(store: &Store, instance_handle: InstanceHandle) -> Instance { pub fn from_handle(store: &Store, instance_handle: InstanceHandle) -> Instance {
let mut exports = Vec::new(); let mut exports = Vec::new();
let mut exports_types = Vec::new(); let mut exports_types = Vec::new();
let mut mutable = instance_handle.clone(); for (name, _) in instance_handle.exports() {
for (name, _) in instance_handle.clone().exports() { let export = instance_handle.lookup(name).expect("export");
let export = mutable.lookup(name).expect("export");
if let wasmtime_runtime::Export::Function { signature, .. } = &export { if let wasmtime_runtime::Export::Function { signature, .. } = &export {
// HACK ensure all handles, instantiated outside Store, present in // HACK ensure all handles, instantiated outside Store, present in
// the store's SignatureRegistry, e.g. WASI instances that are // the store's SignatureRegistry, e.g. WASI instances that are
@@ -220,7 +221,6 @@ impl Instance {
#[doc(hidden)] #[doc(hidden)]
pub fn get_wasmtime_memory(&self) -> Option<wasmtime_runtime::Export> { pub fn get_wasmtime_memory(&self) -> Option<wasmtime_runtime::Export> {
let mut instance_handle = self.instance_handle.clone(); self.instance_handle.lookup("memory")
instance_handle.lookup("memory")
} }
} }

View File

@@ -36,6 +36,7 @@ pub(crate) fn create_handle(
}) })
.unwrap_or_else(PrimaryMap::new); .unwrap_or_else(PrimaryMap::new);
unsafe {
Ok(InstanceHandle::new( Ok(InstanceHandle::new(
Arc::new(module), Arc::new(module),
finished_functions.into_boxed_slice(), finished_functions.into_boxed_slice(),
@@ -44,6 +45,6 @@ pub(crate) fn create_handle(
signatures.into_boxed_slice(), signatures.into_boxed_slice(),
None, None,
state, state,
) )?)
.expect("instance")) }
} }

View File

@@ -70,7 +70,7 @@ unsafe extern "C" fn stub_fn(
call_id: u32, call_id: u32,
values_vec: *mut i128, values_vec: *mut i128,
) -> u32 { ) -> u32 {
let mut instance = InstanceHandle::from_vmctx(vmctx); let instance = InstanceHandle::from_vmctx(vmctx);
let (args, returns_len) = { let (args, returns_len) = {
let module = instance.module_ref(); let module = instance.module_ref();
@@ -89,7 +89,7 @@ unsafe extern "C" fn stub_fn(
let mut returns = vec![Val::null(); returns_len]; let mut returns = vec![Val::null(); returns_len];
let func = &instance let func = &instance
.host_state() .host_state()
.downcast_mut::<TrampolineState>() .downcast_ref::<TrampolineState>()
.expect("state") .expect("state")
.func; .func;

View File

@@ -34,12 +34,12 @@ pub fn create_global(gt: &GlobalType, val: Val) -> Result<(wasmtime_runtime::Exp
}, },
initializer: wasm::GlobalInit::Import, // TODO is it right? initializer: wasm::GlobalInit::Import, // TODO is it right?
}; };
let mut handle = let handle =
create_handle(Module::new(), None, PrimaryMap::new(), Box::new(())).expect("handle"); create_handle(Module::new(), None, PrimaryMap::new(), Box::new(())).expect("handle");
Ok(( Ok((
wasmtime_runtime::Export::Global { wasmtime_runtime::Export::Global {
definition: definition.as_mut(), definition: definition.as_mut(),
vmctx: handle.vmctx_mut_ptr(), vmctx: handle.vmctx_ptr(),
global, global,
}, },
GlobalState { definition, handle }, GlobalState { definition, handle },

View File

@@ -23,7 +23,7 @@ pub fn generate_func_export(
func: &Rc<dyn Callable + 'static>, func: &Rc<dyn Callable + 'static>,
store: &Store, store: &Store,
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> { ) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
let mut instance = create_handle_with_function(ft, func, store)?; let instance = create_handle_with_function(ft, func, store)?;
let export = instance.lookup("trampoline").expect("trampoline export"); let export = instance.lookup("trampoline").expect("trampoline export");
Ok((instance, export)) Ok((instance, export))
} }
@@ -38,7 +38,7 @@ pub fn generate_global_export(
pub fn generate_memory_export( pub fn generate_memory_export(
m: &MemoryType, m: &MemoryType,
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> { ) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
let mut instance = create_handle_with_memory(m)?; let instance = create_handle_with_memory(m)?;
let export = instance.lookup("memory").expect("memory export"); let export = instance.lookup("memory").expect("memory export");
Ok((instance, export)) Ok((instance, export))
} }
@@ -46,7 +46,7 @@ pub fn generate_memory_export(
pub fn generate_table_export( pub fn generate_table_export(
t: &TableType, t: &TableType,
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> { ) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
let mut instance = create_handle_with_table(t)?; let instance = create_handle_with_table(t)?;
let export = instance.lookup("table").expect("table export"); let export = instance.lookup("table").expect("table export");
Ok((instance, export)) Ok((instance, export))
} }

View File

@@ -217,7 +217,7 @@ pub(crate) fn into_checked_anyfunc(
} }
pub(crate) fn from_checked_anyfunc( pub(crate) fn from_checked_anyfunc(
item: &wasmtime_runtime::VMCallerCheckedAnyfunc, item: wasmtime_runtime::VMCallerCheckedAnyfunc,
store: &Store, store: &Store,
) -> Val { ) -> Val {
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() { if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {

View File

@@ -231,7 +231,7 @@ pub fn inspect_memory<'instance>(
start: usize, start: usize,
len: usize, len: usize,
) -> Result<&'instance [u8], ActionError> { ) -> Result<&'instance [u8], ActionError> {
let definition = match unsafe { instance.lookup_immutable(memory_name) } { let definition = match instance.lookup(memory_name) {
Some(Export::Memory { Some(Export::Memory {
definition, definition,
memory: _memory, memory: _memory,
@@ -259,7 +259,7 @@ pub fn inspect_memory<'instance>(
/// Read a global in the given instance identified by an export name. /// Read a global in the given instance identified by an export name.
pub fn get(instance: &InstanceHandle, global_name: &str) -> Result<RuntimeValue, ActionError> { pub fn get(instance: &InstanceHandle, global_name: &str) -> Result<RuntimeValue, ActionError> {
let (definition, global) = match unsafe { instance.lookup_immutable(global_name) } { let (definition, global) = match instance.lookup(global_name) {
Some(Export::Global { Some(Export::Global {
definition, definition,
vmctx: _, vmctx: _,

View File

@@ -105,7 +105,7 @@ impl Context {
.map_err(|e| format!("module did not validate: {}", e.to_string())) .map_err(|e| format!("module did not validate: {}", e.to_string()))
} }
fn instantiate(&mut self, data: &[u8]) -> Result<InstanceHandle, SetupError> { unsafe fn instantiate(&mut self, data: &[u8]) -> Result<InstanceHandle, SetupError> {
self.validate(&data).map_err(SetupError::Validate)?; self.validate(&data).map_err(SetupError::Validate)?;
let debug_info = self.debug_info(); let debug_info = self.debug_info();
@@ -125,7 +125,11 @@ impl Context {
} }
/// Instantiate a module instance and register the instance. /// Instantiate a module instance and register the instance.
pub fn instantiate_module( ///
/// # Unsafety
///
/// See `InstanceHandle::new`
pub unsafe fn instantiate_module(
&mut self, &mut self,
instance_name: Option<String>, instance_name: Option<String>,
data: &[u8], data: &[u8],

View File

@@ -184,7 +184,11 @@ impl CompiledModule {
/// Note that if only one instance of this module is needed, it may be more /// Note that if only one instance of this module is needed, it may be more
/// efficient to call the top-level `instantiate`, since that avoids copying /// efficient to call the top-level `instantiate`, since that avoids copying
/// the data initializers. /// the data initializers.
pub fn instantiate( ///
/// # Unsafety
///
/// See `InstanceHandle::new`
pub unsafe fn instantiate(
&self, &self,
resolver: &mut dyn Resolver, resolver: &mut dyn Resolver,
) -> Result<InstanceHandle, InstantiationError> { ) -> Result<InstanceHandle, InstantiationError> {
@@ -242,8 +246,12 @@ impl OwnedDataInitializer {
/// ///
/// This is equivalent to createing a `CompiledModule` and calling `instantiate()` on it, /// This is equivalent to createing a `CompiledModule` and calling `instantiate()` on it,
/// but avoids creating an intermediate copy of the data initializers. /// but avoids creating an intermediate copy of the data initializers.
///
/// # Unsafety
///
/// See `InstanceHandle::new`
#[allow(clippy::implicit_hasher)] #[allow(clippy::implicit_hasher)]
pub fn instantiate( pub unsafe fn instantiate(
compiler: &mut Compiler, compiler: &mut Compiler,
data: &[u8], data: &[u8],
resolver: &mut dyn Resolver, resolver: &mut dyn Resolver,

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
use crate::mmap::Mmap; use crate::mmap::Mmap;
use crate::vmcontext::VMMemoryDefinition; use crate::vmcontext::VMMemoryDefinition;
use more_asserts::{assert_ge, assert_le}; use more_asserts::{assert_ge, assert_le};
use std::cell::RefCell;
use std::convert::TryFrom; use std::convert::TryFrom;
use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE}; use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE};
@@ -12,10 +13,7 @@ use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE};
#[derive(Debug)] #[derive(Debug)]
pub struct LinearMemory { pub struct LinearMemory {
// The underlying allocation. // The underlying allocation.
mmap: Mmap, mmap: RefCell<WasmMmap>,
// The current logical size in wasm pages of this linear memory.
current: u32,
// The optional maximum size in wasm pages of this linear memory. // The optional maximum size in wasm pages of this linear memory.
maximum: Option<u32>, maximum: Option<u32>,
@@ -29,6 +27,14 @@ pub struct LinearMemory {
pub(crate) needs_signal_handlers: bool, pub(crate) needs_signal_handlers: bool,
} }
#[derive(Debug)]
struct WasmMmap {
// Our OS allocation of mmap'd memory.
alloc: Mmap,
// The current logical size in wasm pages of this linear memory.
size: u32,
}
impl LinearMemory { impl LinearMemory {
/// Create a new linear memory instance with specified minimum and maximum number of wasm pages. /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
pub fn new(plan: &MemoryPlan) -> Result<Self, String> { pub fn new(plan: &MemoryPlan) -> Result<Self, String> {
@@ -59,11 +65,13 @@ impl LinearMemory {
let mapped_pages = plan.memory.minimum as usize; let mapped_pages = plan.memory.minimum as usize;
let mapped_bytes = mapped_pages * WASM_PAGE_SIZE as usize; let mapped_bytes = mapped_pages * WASM_PAGE_SIZE as usize;
let mmap = Mmap::accessible_reserved(mapped_bytes, request_bytes)?; let mmap = WasmMmap {
alloc: Mmap::accessible_reserved(mapped_bytes, request_bytes)?,
size: plan.memory.minimum,
};
Ok(Self { Ok(Self {
mmap, mmap: mmap.into(),
current: plan.memory.minimum,
maximum: plan.memory.maximum, maximum: plan.memory.maximum,
offset_guard_size: offset_guard_bytes, offset_guard_size: offset_guard_bytes,
needs_signal_handlers, needs_signal_handlers,
@@ -72,25 +80,26 @@ impl LinearMemory {
/// Returns the number of allocated wasm pages. /// Returns the number of allocated wasm pages.
pub fn size(&self) -> u32 { pub fn size(&self) -> u32 {
self.current self.mmap.borrow().size
} }
/// Grow memory by the specified amount of wasm pages. /// Grow memory by the specified amount of wasm pages.
/// ///
/// Returns `None` if memory can't be grown by the specified amount /// Returns `None` if memory can't be grown by the specified amount
/// of wasm pages. /// of wasm pages.
pub fn grow(&mut self, delta: u32) -> Option<u32> { pub fn grow(&self, delta: u32) -> Option<u32> {
// Optimization of memory.grow 0 calls. // Optimization of memory.grow 0 calls.
let mut mmap = self.mmap.borrow_mut();
if delta == 0 { if delta == 0 {
return Some(self.current); return Some(mmap.size);
} }
let new_pages = match self.current.checked_add(delta) { let new_pages = match mmap.size.checked_add(delta) {
Some(new_pages) => new_pages, Some(new_pages) => new_pages,
// Linear memory size overflow. // Linear memory size overflow.
None => return None, None => return None,
}; };
let prev_pages = self.current; let prev_pages = mmap.size;
if let Some(maximum) = self.maximum { if let Some(maximum) = self.maximum {
if new_pages > maximum { if new_pages > maximum {
@@ -111,7 +120,7 @@ impl LinearMemory {
let prev_bytes = usize::try_from(prev_pages).unwrap() * WASM_PAGE_SIZE as usize; let prev_bytes = usize::try_from(prev_pages).unwrap() * WASM_PAGE_SIZE as usize;
let new_bytes = usize::try_from(new_pages).unwrap() * WASM_PAGE_SIZE as usize; let new_bytes = usize::try_from(new_pages).unwrap() * WASM_PAGE_SIZE as usize;
if new_bytes > self.mmap.len() - self.offset_guard_size { if new_bytes > mmap.alloc.len() - self.offset_guard_size {
// If the new size is within the declared maximum, but needs more memory than we // If the new size is within the declared maximum, but needs more memory than we
// have on hand, it's a dynamic heap and it can move. // have on hand, it's a dynamic heap and it can move.
let guard_bytes = self.offset_guard_size; let guard_bytes = self.offset_guard_size;
@@ -119,25 +128,26 @@ impl LinearMemory {
let mut new_mmap = Mmap::accessible_reserved(new_bytes, request_bytes).ok()?; let mut new_mmap = Mmap::accessible_reserved(new_bytes, request_bytes).ok()?;
let copy_len = self.mmap.len() - self.offset_guard_size; let copy_len = mmap.alloc.len() - self.offset_guard_size;
new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&self.mmap.as_slice()[..copy_len]); new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&mmap.alloc.as_slice()[..copy_len]);
self.mmap = new_mmap; mmap.alloc = new_mmap;
} else if delta_bytes > 0 { } else if delta_bytes > 0 {
// Make the newly allocated pages accessible. // Make the newly allocated pages accessible.
self.mmap.make_accessible(prev_bytes, delta_bytes).ok()?; mmap.alloc.make_accessible(prev_bytes, delta_bytes).ok()?;
} }
self.current = new_pages; mmap.size = new_pages;
Some(prev_pages) Some(prev_pages)
} }
/// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
pub fn vmmemory(&mut self) -> VMMemoryDefinition { pub fn vmmemory(&self) -> VMMemoryDefinition {
let mut mmap = self.mmap.borrow_mut();
VMMemoryDefinition { VMMemoryDefinition {
base: self.mmap.as_mut_ptr(), base: mmap.alloc.as_mut_ptr(),
current_length: self.current as usize * WASM_PAGE_SIZE as usize, current_length: mmap.size as usize * WASM_PAGE_SIZE as usize,
} }
} }
} }

View File

@@ -3,6 +3,7 @@
//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition}; use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
use std::cell::RefCell;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use wasmtime_environ::wasm::TableElementType; use wasmtime_environ::wasm::TableElementType;
use wasmtime_environ::{TablePlan, TableStyle}; use wasmtime_environ::{TablePlan, TableStyle};
@@ -10,7 +11,7 @@ use wasmtime_environ::{TablePlan, TableStyle};
/// A table instance. /// A table instance.
#[derive(Debug)] #[derive(Debug)]
pub struct Table { pub struct Table {
vec: Vec<VMCallerCheckedAnyfunc>, vec: RefCell<Vec<VMCallerCheckedAnyfunc>>,
maximum: Option<u32>, maximum: Option<u32>,
} }
@@ -25,10 +26,10 @@ impl Table {
}; };
match plan.style { match plan.style {
TableStyle::CallerChecksSignature => Self { TableStyle::CallerChecksSignature => Self {
vec: vec![ vec: RefCell::new(vec![
VMCallerCheckedAnyfunc::default(); VMCallerCheckedAnyfunc::default();
usize::try_from(plan.table.minimum).unwrap() usize::try_from(plan.table.minimum).unwrap()
], ]),
maximum: plan.table.maximum, maximum: plan.table.maximum,
}, },
} }
@@ -36,14 +37,14 @@ impl Table {
/// Returns the number of allocated elements. /// Returns the number of allocated elements.
pub fn size(&self) -> u32 { pub fn size(&self) -> u32 {
self.vec.len().try_into().unwrap() self.vec.borrow().len().try_into().unwrap()
} }
/// Grow table by the specified amount of elements. /// Grow table by the specified amount of elements.
/// ///
/// Returns `None` if table can't be grown by the specified amount /// Returns `None` if table can't be grown by the specified amount
/// of elements. /// of elements.
pub fn grow(&mut self, delta: u32) -> Option<u32> { pub fn grow(&self, delta: u32) -> Option<u32> {
let new_len = match self.size().checked_add(delta) { let new_len = match self.size().checked_add(delta) {
Some(len) => { Some(len) => {
if let Some(max) = self.maximum { if let Some(max) = self.maximum {
@@ -57,7 +58,7 @@ impl Table {
return None; return None;
} }
}; };
self.vec.resize( self.vec.borrow_mut().resize(
usize::try_from(new_len).unwrap(), usize::try_from(new_len).unwrap(),
VMCallerCheckedAnyfunc::default(), VMCallerCheckedAnyfunc::default(),
); );
@@ -67,34 +68,31 @@ impl Table {
/// Get reference to the specified element. /// Get reference to the specified element.
/// ///
/// Returns `None` if the index is out of bounds. /// Returns `None` if the index is out of bounds.
pub fn get(&self, index: u32) -> Option<&VMCallerCheckedAnyfunc> { pub fn get(&self, index: u32) -> Option<VMCallerCheckedAnyfunc> {
self.vec.get(index as usize) self.vec.borrow().get(index as usize).cloned()
} }
/// Get mutable reference to the specified element. /// Set reference to the specified element.
/// ///
/// Returns `None` if the index is out of bounds. /// # Panics
pub fn get_mut(&mut self, index: u32) -> Option<&mut VMCallerCheckedAnyfunc> { ///
self.vec.get_mut(index as usize) /// Panics if `index` is out of bounds.
pub fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), ()> {
match self.vec.borrow_mut().get_mut(index as usize) {
Some(slot) => {
*slot = func;
Ok(())
}
None => Err(()),
}
} }
/// Return a `VMTableDefinition` for exposing the table to compiled wasm code. /// Return a `VMTableDefinition` for exposing the table to compiled wasm code.
pub fn vmtable(&mut self) -> VMTableDefinition { pub fn vmtable(&self) -> VMTableDefinition {
let mut vec = self.vec.borrow_mut();
VMTableDefinition { VMTableDefinition {
base: self.vec.as_mut_ptr() as *mut u8, base: vec.as_mut_ptr() as *mut u8,
current_elements: self.vec.len().try_into().unwrap(), current_elements: vec.len().try_into().unwrap(),
} }
} }
} }
impl AsRef<[VMCallerCheckedAnyfunc]> for Table {
fn as_ref(&self) -> &[VMCallerCheckedAnyfunc] {
self.vec.as_slice()
}
}
impl AsMut<[VMCallerCheckedAnyfunc]> for Table {
fn as_mut(&mut self) -> &mut [VMCallerCheckedAnyfunc] {
self.vec.as_mut_slice()
}
}

View File

@@ -605,16 +605,16 @@ impl VMContext {
/// This is unsafe because it doesn't work on just any `VMContext`, it must /// This is unsafe because it doesn't work on just any `VMContext`, it must
/// be a `VMContext` allocated as part of an `Instance`. /// be a `VMContext` allocated as part of an `Instance`.
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
pub(crate) unsafe fn instance(&mut self) -> &mut Instance { pub(crate) unsafe fn instance(&self) -> &Instance {
&mut *((self as *mut Self as *mut u8).offset(-Instance::vmctx_offset()) as *mut Instance) &*((self as *const Self as *mut u8).offset(-Instance::vmctx_offset()) as *const Instance)
} }
/// Return a mutable reference to the host state associated with this `Instance`. /// Return a reference to the host state associated with this `Instance`.
/// ///
/// # Safety /// # Safety
/// This is unsafe because it doesn't work on just any `VMContext`, it must /// This is unsafe because it doesn't work on just any `VMContext`, it must
/// be a `VMContext` allocated as part of an `Instance`. /// be a `VMContext` allocated as part of an `Instance`.
pub unsafe fn host_state(&mut self) -> &mut dyn Any { pub unsafe fn host_state(&self) -> &dyn Any {
self.instance().host_state() self.instance().host_state()
} }
} }

View File

@@ -201,8 +201,8 @@ pub fn add_wrappers_to_module(args: TokenStream) -> TokenStream {
#format_str, #format_str,
#(#format_args),* #(#format_args),*
); );
let wasi_ctx = match get_wasi_ctx(&mut *ctx) { let mut wasi_ctx = match get_wasi_ctx(&mut *ctx) {
Ok(e) => e, Ok(e) => e.borrow_mut(),
Err(e) => #handle_early_error, Err(e) => #handle_early_error,
}; };
let memory = match get_memory(&mut *caller_ctx) { let memory = match get_memory(&mut *caller_ctx) {
@@ -210,7 +210,7 @@ pub fn add_wrappers_to_module(args: TokenStream) -> TokenStream {
Err(e) => #handle_early_error, Err(e) => #handle_early_error,
}; };
hostcalls::#name_ident( hostcalls::#name_ident(
wasi_ctx, &mut *wasi_ctx,
memory, memory,
#(#hostcall_args),* #(#hostcall_args),*
) #cvt_ret ) #cvt_ret

View File

@@ -2,6 +2,7 @@ use cranelift_codegen::ir::types;
use cranelift_codegen::{ir, isa}; use cranelift_codegen::{ir, isa};
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::DefinedFuncIndex; use cranelift_wasm::DefinedFuncIndex;
use std::cell::RefCell;
use std::fs::File; use std::fs::File;
use std::sync::Arc; use std::sync::Arc;
use target_lexicon::HOST; use target_lexicon::HOST;
@@ -78,6 +79,7 @@ pub fn instantiate_wasi_with_context(
let data_initializers = Vec::new(); let data_initializers = Vec::new();
let signatures = PrimaryMap::new(); let signatures = PrimaryMap::new();
unsafe {
InstanceHandle::new( InstanceHandle::new(
Arc::new(module), Arc::new(module),
finished_functions.into_boxed_slice(), finished_functions.into_boxed_slice(),
@@ -85,20 +87,21 @@ pub fn instantiate_wasi_with_context(
&data_initializers, &data_initializers,
signatures.into_boxed_slice(), signatures.into_boxed_slice(),
None, None,
Box::new(wasi_ctx), Box::new(RefCell::new(wasi_ctx)),
) )
} }
}
wig::define_add_wrappers_to_module!( wig::define_add_wrappers_to_module!(
"snapshot" "wasi_snapshot_preview1" "snapshot" "wasi_snapshot_preview1"
); );
// Used by `add_wrappers_to_module` defined in the macro above // Used by `add_wrappers_to_module` defined in the macro above
fn get_wasi_ctx(vmctx: &mut VMContext) -> Result<&mut WasiCtx, wasi::__wasi_errno_t> { fn get_wasi_ctx(vmctx: &mut VMContext) -> Result<&RefCell<WasiCtx>, wasi::__wasi_errno_t> {
unsafe { unsafe {
vmctx vmctx
.host_state() .host_state()
.downcast_mut::<WasiCtx>() .downcast_ref()
.ok_or_else(|| panic!("no host state named WasiCtx available")) .ok_or_else(|| panic!("no host state named WasiCtx available"))
} }
} }

View File

@@ -2,6 +2,7 @@ use cranelift_codegen::ir::types;
use cranelift_codegen::{ir, isa}; use cranelift_codegen::{ir, isa};
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::DefinedFuncIndex; use cranelift_wasm::DefinedFuncIndex;
use std::cell::RefCell;
use std::fs::File; use std::fs::File;
use std::sync::Arc; use std::sync::Arc;
use target_lexicon::HOST; use target_lexicon::HOST;
@@ -78,6 +79,7 @@ pub fn instantiate_wasi_with_context(
let data_initializers = Vec::new(); let data_initializers = Vec::new();
let signatures = PrimaryMap::new(); let signatures = PrimaryMap::new();
unsafe {
InstanceHandle::new( InstanceHandle::new(
Arc::new(module), Arc::new(module),
finished_functions.into_boxed_slice(), finished_functions.into_boxed_slice(),
@@ -85,16 +87,17 @@ pub fn instantiate_wasi_with_context(
&data_initializers, &data_initializers,
signatures.into_boxed_slice(), signatures.into_boxed_slice(),
None, None,
Box::new(wasi_ctx), Box::new(RefCell::new(wasi_ctx)),
) )
} }
}
// Used by `add_wrappers_to_module` defined in the macro above // Used by `add_wrappers_to_module` defined in the macro above
fn get_wasi_ctx(vmctx: &mut VMContext) -> Result<&mut WasiCtx, wasi::__wasi_errno_t> { fn get_wasi_ctx(vmctx: &mut VMContext) -> Result<&RefCell<WasiCtx>, wasi::__wasi_errno_t> {
unsafe { unsafe {
vmctx vmctx
.host_state() .host_state()
.downcast_mut::<WasiCtx>() .downcast_ref()
.ok_or_else(|| panic!("no host state named WasiCtx available")) .ok_or_else(|| panic!("no host state named WasiCtx available"))
} }
} }

View File

@@ -21,6 +21,8 @@ fn test_environ_translate() {
let mut resolver = NullResolver {}; let mut resolver = NullResolver {};
let mut compiler = Compiler::new(isa, CompilationStrategy::Auto); let mut compiler = Compiler::new(isa, CompilationStrategy::Auto);
unsafe {
let instance = instantiate(&mut compiler, &data, &mut resolver, false); let instance = instantiate(&mut compiler, &data, &mut resolver, false);
assert!(instance.is_ok()); assert!(instance.is_ok());
} }
}