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:
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -37,6 +38,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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"))
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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: _,
|
||||||
|
|||||||
@@ -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],
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,8 +87,9 @@ 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!(
|
||||||
@@ -94,11 +97,11 @@ wig::define_add_wrappers_to_module!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user