Improve thread safety of core types

This commit is contained in:
Mark McCaskey
2020-06-26 15:56:19 -07:00
parent f7eef7ff0f
commit de0c548653
9 changed files with 137 additions and 60 deletions

View File

@@ -4,6 +4,7 @@ use crate::store::Store;
use crate::types::{Val, ValFuncRef}; use crate::types::{Val, ValFuncRef};
use crate::RuntimeError; use crate::RuntimeError;
use crate::TableType; use crate::TableType;
use std::sync::Arc;
use wasmer_runtime::{Export, ExportTable, Table as RuntimeTable, VMCallerCheckedAnyfunc}; use wasmer_runtime::{Export, ExportTable, Table as RuntimeTable, VMCallerCheckedAnyfunc};
/// The `Table` struct is an array-like structure representing a WebAssembly Table, /// The `Table` struct is an array-like structure representing a WebAssembly Table,
@@ -39,17 +40,18 @@ impl Table {
.create_table(table_plan) .create_table(table_plan)
.map_err(RuntimeError::new)?; .map_err(RuntimeError::new)?;
let definition = table.vmtable(); let num_elements = table.size();
for i in 0..definition.current_elements { for i in 0..num_elements {
set_table_item(table.as_ref(), i, item.clone())?; set_table_item(table.as_ref(), i, item.clone())?;
} }
let definition = table.vmtable();
Ok(Table { Ok(Table {
store: store.clone(), store: store.clone(),
owned_by_store: true, owned_by_store: true,
exported: ExportTable { exported: ExportTable {
from: table, from: table,
definition: Box::leak(Box::new(definition)), definition,
}, },
}) })
} }

View File

@@ -19,6 +19,19 @@ pub struct Instance {
/// The exports for an instance. /// The exports for an instance.
pub exports: Exports, pub exports: Exports,
} }
/*
#[cfg(test)]
mod send_test {
use super::*;
fn is_send<T: Send>() -> bool {
true
}
#[test]
fn instance_is_send() {
assert!(is_send::<Instance>());
}
}*/
impl Instance { impl Instance {
/// Creates a new `Instance` from a WebAssembly [`Module`] and a /// Creates a new `Instance` from a WebAssembly [`Module`] and a

View File

@@ -1,13 +1,14 @@
use crate::{Bytes, Pages}; use crate::{Bytes, Pages};
use more_asserts::{assert_ge, assert_le}; use more_asserts::{assert_ge, assert_le};
use std::cell::RefCell; use std::borrow::{Borrow, BorrowMut};
use std::sync::{Arc, Mutex};
use wasmer_runtime::{Memory, MemoryError, MemoryPlan, MemoryStyle, Mmap, VMMemoryDefinition}; use wasmer_runtime::{Memory, MemoryError, MemoryPlan, MemoryStyle, Mmap, VMMemoryDefinition};
/// A linear memory instance. /// A linear memory instance.
#[derive(Debug)] #[derive(Debug)]
pub struct LinearMemory { pub struct LinearMemory {
// The underlying allocation. // The underlying allocation.
mmap: RefCell<WasmMmap>, mmap: Arc<Mutex<WasmMmap>>,
// The optional maximum size in wasm pages of this linear memory. // The optional maximum size in wasm pages of this linear memory.
maximum: Option<Pages>, maximum: Option<Pages>,
@@ -81,7 +82,7 @@ impl LinearMemory {
}; };
Ok(Self { Ok(Self {
mmap: mmap.into(), mmap: Arc::new(Mutex::new(mmap)),
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,
@@ -98,7 +99,8 @@ impl Memory for LinearMemory {
/// Returns the number of allocated wasm pages. /// Returns the number of allocated wasm pages.
fn size(&self) -> Pages { fn size(&self) -> Pages {
self.mmap.borrow().size let mmap_guard = self.mmap.lock().unwrap();
mmap_guard.borrow().size
} }
/// Grow memory by the specified amount of wasm pages. /// Grow memory by the specified amount of wasm pages.
@@ -106,8 +108,9 @@ impl Memory for LinearMemory {
/// 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.
fn grow(&self, delta: Pages) -> Result<Pages, MemoryError> { fn grow(&self, delta: Pages) -> Result<Pages, MemoryError> {
let mut mmap_guard = self.mmap.lock().unwrap();
let mmap = mmap_guard.borrow_mut();
// Optimization of memory.grow 0 calls. // Optimization of memory.grow 0 calls.
let mut mmap = self.mmap.borrow_mut();
if delta.0 == 0 { if delta.0 == 0 {
return Ok(mmap.size); return Ok(mmap.size);
} }
@@ -178,7 +181,8 @@ impl Memory for LinearMemory {
/// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
fn vmmemory(&self) -> VMMemoryDefinition { fn vmmemory(&self) -> VMMemoryDefinition {
let mut mmap = self.mmap.borrow_mut(); let mut mmap_guard = self.mmap.lock().unwrap();
let mmap = mmap_guard.borrow_mut();
VMMemoryDefinition { VMMemoryDefinition {
base: mmap.alloc.as_mut_ptr(), base: mmap.alloc.as_mut_ptr(),
current_length: mmap.size.bytes().0, current_length: mmap.size.bytes().0,

View File

@@ -1,6 +1,9 @@
use crate::ValType; use crate::ValType;
use std::cell::RefCell; use std::borrow::{Borrow, BorrowMut};
use std::cell::UnsafeCell;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::ptr::NonNull;
use std::sync::Mutex;
use wasmer_runtime::{ use wasmer_runtime::{
Table, TablePlan, TableStyle, Trap, TrapCode, VMCallerCheckedAnyfunc, VMTableDefinition, Table, TablePlan, TableStyle, Trap, TrapCode, VMCallerCheckedAnyfunc, VMTableDefinition,
}; };
@@ -8,11 +11,18 @@ use wasmer_runtime::{
/// A table instance. /// A table instance.
#[derive(Debug)] #[derive(Debug)]
pub struct LinearTable { pub struct LinearTable {
vec: RefCell<Vec<VMCallerCheckedAnyfunc>>, // TODO: we can remove the mutex by using atomic swaps and preallocating the max table size
vec: Mutex<Vec<VMCallerCheckedAnyfunc>>,
maximum: Option<u32>, maximum: Option<u32>,
plan: TablePlan, plan: TablePlan,
vm_table_definition: Box<UnsafeCell<VMTableDefinition>>,
} }
/// This is correct because there is no thread-specific data tied to this type.
unsafe impl Send for LinearTable {}
/// This is correct because all internal mutability is protected by a mutex.
unsafe impl Sync for LinearTable {}
impl LinearTable { impl LinearTable {
/// Create a new table instance with specified minimum and maximum number of elements. /// Create a new table instance with specified minimum and maximum number of elements.
pub fn new(plan: &TablePlan) -> Result<Self, String> { pub fn new(plan: &TablePlan) -> Result<Self, String> {
@@ -28,16 +38,19 @@ impl LinearTable {
)); ));
} }
} }
let table_minimum = usize::try_from(plan.table.minimum)
.map_err(|_| "Table minimum is bigger than usize".to_string())?;
let mut vec = vec![VMCallerCheckedAnyfunc::default(); table_minimum];
let base = vec.as_mut_ptr();
match plan.style { match plan.style {
TableStyle::CallerChecksSignature => Ok(Self { TableStyle::CallerChecksSignature => Ok(Self {
vec: RefCell::new(vec![ vec: Mutex::new(vec),
VMCallerCheckedAnyfunc::default();
usize::try_from(plan.table.minimum).map_err(|_| {
"Table minimum is bigger than usize".to_string()
})?
]),
maximum: plan.table.maximum, maximum: plan.table.maximum,
plan: plan.clone(), plan: plan.clone(),
vm_table_definition: Box::new(UnsafeCell::new(VMTableDefinition {
base: base as _,
current_elements: table_minimum as _,
})),
}), }),
} }
} }
@@ -51,7 +64,8 @@ impl Table for LinearTable {
/// Returns the number of allocated elements. /// Returns the number of allocated elements.
fn size(&self) -> u32 { fn size(&self) -> u32 {
self.vec.borrow().len().try_into().unwrap() let vec_guard = self.vec.lock().unwrap();
vec_guard.borrow().len().try_into().unwrap()
} }
/// Grow table by the specified amount of elements. /// Grow table by the specified amount of elements.
@@ -59,15 +73,23 @@ impl Table for LinearTable {
/// 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, otherwise returns the previous size of the table. /// of elements, otherwise returns the previous size of the table.
fn grow(&self, delta: u32) -> Option<u32> { fn grow(&self, delta: u32) -> Option<u32> {
let mut vec_guard = self.vec.lock().unwrap();
let vec = vec_guard.borrow_mut();
let size = self.size(); let size = self.size();
let new_len = size.checked_add(delta)?; let new_len = size.checked_add(delta)?;
if self.maximum.map_or(false, |max| new_len > max) { if self.maximum.map_or(false, |max| new_len > max) {
return None; return None;
} }
self.vec.borrow_mut().resize( vec.resize(
usize::try_from(new_len).unwrap(), usize::try_from(new_len).unwrap(),
VMCallerCheckedAnyfunc::default(), VMCallerCheckedAnyfunc::default(),
); );
// update table definition
unsafe {
let td = &mut *self.vm_table_definition.get();
td.current_elements = new_len;
td.base = vec.as_mut_ptr() as _;
}
Some(size) Some(size)
} }
@@ -75,7 +97,8 @@ impl Table for LinearTable {
/// ///
/// Returns `None` if the index is out of bounds. /// Returns `None` if the index is out of bounds.
fn get(&self, index: u32) -> Option<VMCallerCheckedAnyfunc> { fn get(&self, index: u32) -> Option<VMCallerCheckedAnyfunc> {
self.vec.borrow().get(index as usize).cloned() let vec_guard = self.vec.lock().unwrap();
vec_guard.borrow().get(index as usize).cloned()
} }
/// Set reference to the specified element. /// Set reference to the specified element.
@@ -84,7 +107,9 @@ impl Table for LinearTable {
/// ///
/// Returns an error if the index is out of bounds. /// Returns an error if the index is out of bounds.
fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), Trap> { fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), Trap> {
match self.vec.borrow_mut().get_mut(index as usize) { let mut vec_guard = self.vec.lock().unwrap();
let vec = vec_guard.borrow_mut();
match vec.get_mut(index as usize) {
Some(slot) => { Some(slot) => {
*slot = func; *slot = func;
Ok(()) Ok(())
@@ -94,11 +119,10 @@ impl Table for LinearTable {
} }
/// Return a `VMTableDefinition` for exposing the table to compiled wasm code. /// Return a `VMTableDefinition` for exposing the table to compiled wasm code.
fn vmtable(&self) -> VMTableDefinition { fn vmtable(&self) -> NonNull<VMTableDefinition> {
let mut vec = self.vec.borrow_mut(); let _vec_guard = self.vec.lock().unwrap();
VMTableDefinition { let ptr = self.vm_table_definition.as_ref() as *const UnsafeCell<VMTableDefinition>
base: vec.as_mut_ptr() as *mut u8, as *const VMTableDefinition as *mut VMTableDefinition;
current_elements: vec.len().try_into().unwrap(), unsafe { NonNull::new_unchecked(ptr) }
}
} }
} }

View File

@@ -7,6 +7,7 @@ use crate::vmcontext::{
VMContext, VMFunctionBody, VMFunctionKind, VMGlobalDefinition, VMMemoryDefinition, VMContext, VMFunctionBody, VMFunctionKind, VMGlobalDefinition, VMMemoryDefinition,
VMTableDefinition, VMTableDefinition,
}; };
use std::ptr::NonNull;
use std::sync::Arc; use std::sync::Arc;
use wasm_common::{FunctionType, GlobalType}; use wasm_common::{FunctionType, GlobalType};
@@ -49,7 +50,7 @@ impl From<ExportFunction> for Export {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ExportTable { pub struct ExportTable {
/// The address of the table descriptor. /// The address of the table descriptor.
pub definition: *mut VMTableDefinition, pub definition: NonNull<VMTableDefinition>,
/// Pointer to the containing `Table`. /// Pointer to the containing `Table`.
pub from: Arc<dyn Table>, pub from: Arc<dyn Table>,
} }

View File

@@ -23,6 +23,7 @@ use std::any::Any;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ptr::NonNull;
use std::sync::Arc; use std::sync::Arc;
use std::{mem, ptr, slice}; use std::{mem, ptr, slice};
use wasm_common::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap}; use wasm_common::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap};
@@ -178,20 +179,20 @@ impl Instance {
/// Return the indexed `VMTableDefinition`. /// Return the indexed `VMTableDefinition`.
#[allow(dead_code)] #[allow(dead_code)]
fn table(&self, index: LocalTableIndex) -> VMTableDefinition { fn table(&self, index: LocalTableIndex) -> VMTableDefinition {
unsafe { *self.table_ptr(index) } unsafe { self.table_ptr(index).as_ref().clone() }
} }
/// Updates the value for a defined table to `VMTableDefinition`. /// Updates the value for a defined table to `VMTableDefinition`.
fn set_table(&self, index: LocalTableIndex, table: VMTableDefinition) { fn set_table(&self, index: LocalTableIndex, table: &VMTableDefinition) {
unsafe { unsafe {
*self.table_ptr(index) = table; *self.table_ptr(index).as_ptr() = table.clone();
} }
} }
/// Return the indexed `VMTableDefinition`. /// Return the indexed `VMTableDefinition`.
fn table_ptr(&self, index: LocalTableIndex) -> *mut VMTableDefinition { fn table_ptr(&self, index: LocalTableIndex) -> NonNull<VMTableDefinition> {
let index = usize::try_from(index.as_u32()).unwrap(); let index = usize::try_from(index.as_u32()).unwrap();
unsafe { self.tables_ptr().add(index) } NonNull::new(unsafe { self.tables_ptr().add(index) }).unwrap()
} }
/// Return a pointer to the `VMTableDefinition`s. /// Return a pointer to the `VMTableDefinition`s.
@@ -502,7 +503,9 @@ impl Instance {
.grow(delta); .grow(delta);
// Keep current the VMContext pointers used by compiled wasm code. // Keep current the VMContext pointers used by compiled wasm code.
self.set_table(table_index, self.tables[table_index].vmtable()); let table_ptr = self.tables[table_index].vmtable();
let vmtable = unsafe { table_ptr.as_ref() };
self.set_table(table_index, vmtable);
result result
} }
@@ -631,7 +634,8 @@ impl Instance {
// https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
let memory = self.memory(memory_index); let memory = self.memory(memory_index);
memory.memory_copy(dst, src, len) // The following memory copy is not synchronized and is not atomic:
unsafe { memory.memory_copy(dst, src, len) }
} }
/// Perform a `memory.copy` on an imported memory. /// Perform a `memory.copy` on an imported memory.
@@ -644,7 +648,8 @@ impl Instance {
) -> Result<(), Trap> { ) -> Result<(), Trap> {
let import = self.imported_memory(memory_index); let import = self.imported_memory(memory_index);
let memory = unsafe { &*import.definition }; let memory = unsafe { &*import.definition };
memory.memory_copy(dst, src, len) // The following memory copy is not synchronized and is not atomic:
unsafe { memory.memory_copy(dst, src, len) }
} }
/// Perform the `memory.fill` operation on a locally defined memory. /// Perform the `memory.fill` operation on a locally defined memory.
@@ -660,7 +665,8 @@ impl Instance {
len: u32, len: u32,
) -> Result<(), Trap> { ) -> Result<(), Trap> {
let memory = self.memory(memory_index); let memory = self.memory(memory_index);
memory.memory_fill(dst, val, len) // The following memory fill is not synchronized and is not atomic:
unsafe { memory.memory_fill(dst, val, len) }
} }
/// Perform the `memory.fill` operation on an imported memory. /// Perform the `memory.fill` operation on an imported memory.
@@ -677,7 +683,8 @@ impl Instance {
) -> Result<(), Trap> { ) -> Result<(), Trap> {
let import = self.imported_memory(memory_index); let import = self.imported_memory(memory_index);
let memory = unsafe { &*import.definition }; let memory = unsafe { &*import.definition };
memory.memory_fill(dst, val, len) // The following memory fill is not synchronized and is not atomic:
unsafe { memory.memory_fill(dst, val, len) }
} }
/// Performs the `memory.init` operation. /// Performs the `memory.init` operation.
@@ -785,9 +792,15 @@ impl InstanceHandle {
vmshared_signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>, vmshared_signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
host_state: Box<dyn Any>, host_state: Box<dyn Any>,
) -> Result<Self, Trap> { ) -> Result<Self, Trap> {
// TODO: investigate `vmctx_tables` and `vmctx_memories`: both of these
// appear to be dropped in this function which may cause memory problems
// depending on the ownership of the types in the `PrimaryMap`.
let vmctx_tables = finished_tables let vmctx_tables = finished_tables
.values() .values()
.map(|t| t.vmtable()) .map(|t| {
let vmtable_ptr = t.vmtable();
vmtable_ptr.as_ref().clone()
})
.collect::<PrimaryMap<LocalTableIndex, _>>() .collect::<PrimaryMap<LocalTableIndex, _>>()
.into_boxed_slice(); .into_boxed_slice();

View File

@@ -62,7 +62,7 @@ pub struct MemoryPlan {
} }
/// Trait for implementing Wasm Memory used by Wasmer. /// Trait for implementing Wasm Memory used by Wasmer.
pub trait Memory: fmt::Debug { pub trait Memory: fmt::Debug + Send + Sync {
/// Returns the memory plan for this memory. /// Returns the memory plan for this memory.
fn plan(&self) -> &MemoryPlan; fn plan(&self) -> &MemoryPlan;
@@ -72,6 +72,8 @@ pub trait Memory: fmt::Debug {
/// Grow memory by the specified amount of wasm pages. /// Grow memory by the specified amount of wasm pages.
fn grow(&self, delta: Pages) -> Result<Pages, MemoryError>; fn grow(&self, delta: Pages) -> Result<Pages, MemoryError>;
/// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. /// Return a [`VMMemoryDefinition`] for exposing the memory to compiled wasm code.
///
/// The pointer returned in [`VMMemoryDefinition`] must be valid for the lifetime of this memory.
fn vmmemory(&self) -> VMMemoryDefinition; fn vmmemory(&self) -> VMMemoryDefinition;
} }

View File

@@ -9,6 +9,7 @@ use crate::trap::{Trap, TrapCode};
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition}; use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::ptr::NonNull;
use wasm_common::{FunctionIndex, GlobalIndex, TableIndex, TableType}; use wasm_common::{FunctionIndex, GlobalIndex, TableIndex, TableType};
/// A WebAssembly table initializer. /// A WebAssembly table initializer.
@@ -42,7 +43,7 @@ pub struct TablePlan {
} }
/// Trait for implementing the interface of a Wasm table. /// Trait for implementing the interface of a Wasm table.
pub trait Table: fmt::Debug { pub trait Table: fmt::Debug + Send + Sync {
/// Returns the table plan for this Table. /// Returns the table plan for this Table.
fn plan(&self) -> &TablePlan; fn plan(&self) -> &TablePlan;
@@ -68,7 +69,7 @@ pub trait Table: fmt::Debug {
fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), Trap>; fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), Trap>;
/// Return a `VMTableDefinition` for exposing the table to compiled wasm code. /// Return a `VMTableDefinition` for exposing the table to compiled wasm code.
fn vmtable(&self) -> VMTableDefinition; fn vmtable(&self) -> NonNull<VMTableDefinition>;
/// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`. /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`.
/// ///

View File

@@ -10,8 +10,9 @@ use crate::table::Table;
use crate::trap::{Trap, TrapCode}; use crate::trap::{Trap, TrapCode};
use std::any::Any; use std::any::Any;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ptr::{self, NonNull};
use std::sync::Arc; use std::sync::Arc;
use std::{ptr, u32}; use std::u32;
/// An imported function. /// An imported function.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -140,7 +141,7 @@ pub enum VMFunctionKind {
#[repr(C)] #[repr(C)]
pub struct VMTableImport { pub struct VMTableImport {
/// A pointer to the imported table description. /// A pointer to the imported table description.
pub definition: *mut VMTableDefinition, pub definition: NonNull<VMTableDefinition>,
/// A pointer to the `Table` that owns the table description. /// A pointer to the `Table` that owns the table description.
pub from: Arc<dyn Table>, pub from: Arc<dyn Table>,
@@ -247,21 +248,36 @@ mod test_vmglobal_import {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)] #[repr(C)]
pub struct VMMemoryDefinition { pub struct VMMemoryDefinition {
/// The start address. /// The start address which is always valid, even if the memory grows.
pub base: *mut u8, pub base: *mut u8,
/// The current logical size of this linear memory in bytes. /// The current logical size of this linear memory in bytes.
pub current_length: usize, pub current_length: usize,
} }
/// # Safety
/// This data is safe to share between threads because it's plain data that
/// is the user's responsibility to synchronize.
unsafe impl Send for VMMemoryDefinition {}
/// # Safety
/// This data is safe to share between threads because it's plain data that
/// is the user's responsibility to synchronize. And it's `Copy` so there's
/// really no difference between passing it by reference or by value as far as
/// correctness in a multi-threaded context is concerned.
unsafe impl Sync for VMMemoryDefinition {}
impl VMMemoryDefinition { impl VMMemoryDefinition {
/// Do a `memory.copy` for the memory. /// Do an unsynchronized, non-atomic `memory.copy` for the memory.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns a `Trap` error when the source or destination ranges are out of /// Returns a `Trap` error when the source or destination ranges are out of
/// bounds. /// bounds.
pub(crate) fn memory_copy(&self, dst: u32, src: u32, len: u32) -> Result<(), Trap> { ///
/// # Safety
/// The memory is not copied atomically and is not synchronized: it's the
/// caller's responsibility to synchronize.
pub(crate) unsafe fn memory_copy(&self, dst: u32, src: u32, len: u32) -> Result<(), Trap> {
// https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
if src if src
.checked_add(len) .checked_add(len)
@@ -278,21 +294,24 @@ impl VMMemoryDefinition {
// Bounds and casts are checked above, by this point we know that // Bounds and casts are checked above, by this point we know that
// everything is safe. // everything is safe.
unsafe { let dst = self.base.add(dst);
let dst = self.base.add(dst); let src = self.base.add(src);
let src = self.base.add(src); ptr::copy(src, dst, len as usize);
ptr::copy(src, dst, len as usize);
}
Ok(()) Ok(())
} }
/// Perform the `memory.fill` operation for the memory. /// Perform the `memory.fill` operation for the memory in an unsynchronized,
/// non-atomic way.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns a `Trap` error if the memory range is out of bounds. /// Returns a `Trap` error if the memory range is out of bounds.
pub(crate) fn memory_fill(&self, dst: u32, val: u32, len: u32) -> Result<(), Trap> { ///
/// # Safety
/// The memory is not filled atomically and is not synchronized: it's the
/// caller's responsibility to synchronize.
pub(crate) unsafe fn memory_fill(&self, dst: u32, val: u32, len: u32) -> Result<(), Trap> {
if dst if dst
.checked_add(len) .checked_add(len)
.map_or(true, |m| m as usize > self.current_length) .map_or(true, |m| m as usize > self.current_length)
@@ -305,10 +324,8 @@ impl VMMemoryDefinition {
// Bounds and casts are checked above, by this point we know that // Bounds and casts are checked above, by this point we know that
// everything is safe. // everything is safe.
unsafe { let dst = self.base.offset(dst);
let dst = self.base.offset(dst); ptr::write_bytes(dst, val, len as usize);
ptr::write_bytes(dst, val, len as usize);
}
Ok(()) Ok(())
} }
@@ -348,7 +365,7 @@ mod test_vmmemory_definition {
/// The fields compiled code needs to access to utilize a WebAssembly table /// The fields compiled code needs to access to utilize a WebAssembly table
/// defined within the instance. /// defined within the instance.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Clone)]
#[repr(C)] #[repr(C)]
pub struct VMTableDefinition { pub struct VMTableDefinition {
/// Pointer to the table data. /// Pointer to the table data.