use crate::sys::exports::{ExportError, Exportable}; use crate::sys::externals::Extern; use crate::sys::store::{AsStoreMut, AsStoreRef}; use crate::sys::MemoryType; use crate::MemoryAccessError; use std::convert::TryInto; use std::marker::PhantomData; use std::mem; use std::mem::MaybeUninit; use std::slice; #[cfg(feature = "tracing")] use tracing::warn; use wasmer_types::Pages; use wasmer_vm::{InternalStoreHandle, LinearMemory, MemoryError, StoreHandle, VMExtern, VMMemory}; use super::MemoryView; /// A WebAssembly `memory` instance. /// /// A memory instance is the runtime representation of a linear memory. /// It consists of a vector of bytes and an optional maximum size. /// /// The length of the vector always is a multiple of the WebAssembly /// page size, which is defined to be the constant 65536 – abbreviated 64Ki. /// Like in a memory type, the maximum size in a memory instance is /// given in units of this page size. /// /// A memory created by the host or in WebAssembly code will be accessible and /// mutable from both host and WebAssembly. /// /// Spec: #[derive(Debug, Clone)] pub struct Memory { pub(crate) handle: StoreHandle, } impl Memory { #[cfg(feature = "compiler")] /// Creates a new host `Memory` from the provided [`MemoryType`]. /// /// This function will construct the `Memory` using the store /// [`BaseTunables`][crate::sys::BaseTunables]. /// /// # Example /// /// ``` /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; /// # let mut store = Store::default(); /// # /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap(); /// ``` pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { let mut store = store.as_store_mut(); let tunables = store.tunables(); let style = tunables.memory_style(&ty); let memory = tunables.create_host_memory(&ty, &style)?; Ok(Self { handle: StoreHandle::new(store.objects_mut(), memory), }) } /// Create a memory object from an existing memory and attaches it to the store pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { let handle = StoreHandle::new(new_store.objects_mut(), memory); Self::from_vm_extern(new_store, handle.internal_handle()) } /// Returns the [`MemoryType`] of the `Memory`. /// /// # Example /// /// ``` /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; /// # let mut store = Store::default(); /// # /// let mt = MemoryType::new(1, None, false); /// let m = Memory::new(&mut store, mt).unwrap(); /// /// assert_eq!(m.ty(&mut store), mt); /// ``` pub fn ty(&self, store: &impl AsStoreRef) -> MemoryType { self.handle.get(store.as_store_ref().objects()).ty() } /// Creates a view into the memory that then allows for /// read and write pub fn view<'a>(&'a self, store: &impl AsStoreRef) -> MemoryView<'a> { MemoryView::new(self, store) } /// Grow memory by the specified amount of WebAssembly [`Pages`] and return /// the previous memory size. /// /// # Example /// /// ``` /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; /// # let mut store = Store::default(); /// # /// let m = Memory::new(&mut store, MemoryType::new(1, Some(3), false)).unwrap(); /// let p = m.grow(&mut store, 2).unwrap(); /// /// assert_eq!(p, Pages(1)); /// assert_eq!(m.view(&mut store).size(), Pages(3)); /// ``` /// /// # Errors /// /// Returns an error if memory can't be grown by the specified amount /// of pages. /// /// ```should_panic /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; /// # use wasmer::FunctionEnv; /// # let mut store = Store::default(); /// # let env = FunctionEnv::new(&mut store, ()); /// # /// let m = Memory::new(&mut store, MemoryType::new(1, Some(1), false)).unwrap(); /// /// // This results in an error: `MemoryError::CouldNotGrow`. /// let s = m.grow(&mut store, 1).unwrap(); /// ``` pub fn grow( &self, store: &mut impl AsStoreMut, delta: IntoPages, ) -> Result where IntoPages: Into, { self.handle.get_mut(store.objects_mut()).grow(delta.into()) } pub(crate) fn from_vm_extern( store: &impl AsStoreRef, internal: InternalStoreHandle, ) -> Self { Self { handle: unsafe { StoreHandle::from_internal(store.as_store_ref().objects().id(), internal) }, } } /// Checks whether this `Memory` can be used with the given context. pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() } /// Attempts to clone this memory (if its clonable) pub fn try_clone(&self, store: &impl AsStoreRef) -> Option { let mem = self.handle.get(store.as_store_ref().objects()); mem.try_clone().map(|mem| mem.into()) } pub(crate) fn to_vm_extern(&self) -> VMExtern { VMExtern::Memory(self.handle.internal_handle()) } } impl std::cmp::PartialEq for Memory { fn eq(&self, other: &Self) -> bool { self.handle == other.handle } } impl std::cmp::Eq for Memory {} impl<'a> Exportable<'a> for Memory { fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { match _extern { Extern::Memory(memory) => Ok(memory), _ => Err(ExportError::IncompatibleType), } } } /// Underlying buffer for a memory. #[derive(Debug, Copy, Clone)] pub(crate) struct MemoryBuffer<'a> { pub(crate) base: *mut u8, pub(crate) len: usize, pub(crate) marker: PhantomData<&'a MemoryView<'a>>, } impl<'a> MemoryBuffer<'a> { pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { let end = offset .checked_add(buf.len() as u64) .ok_or(MemoryAccessError::Overflow)?; if end > self.len.try_into().unwrap() { #[cfg(feature = "tracing")] warn!( "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", buf.len(), end, self.len ); return Err(MemoryAccessError::HeapOutOfBounds); } unsafe { volatile_memcpy_read(self.base.add(offset as usize), buf.as_mut_ptr(), buf.len()); } Ok(()) } pub(crate) fn read_uninit<'b>( &self, offset: u64, buf: &'b mut [MaybeUninit], ) -> Result<&'b mut [u8], MemoryAccessError> { let end = offset .checked_add(buf.len() as u64) .ok_or(MemoryAccessError::Overflow)?; if end > self.len.try_into().unwrap() { #[cfg(feature = "tracing")] warn!( "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", buf.len(), end, self.len ); return Err(MemoryAccessError::HeapOutOfBounds); } let buf_ptr = buf.as_mut_ptr() as *mut u8; unsafe { volatile_memcpy_read(self.base.add(offset as usize), buf_ptr, buf.len()); } Ok(unsafe { slice::from_raw_parts_mut(buf_ptr, buf.len()) }) } pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { let end = offset .checked_add(data.len() as u64) .ok_or(MemoryAccessError::Overflow)?; if end > self.len.try_into().unwrap() { #[cfg(feature = "tracing")] warn!( "attempted to write ({} bytes) beyond the bounds of the memory view ({} > {})", data.len(), end, self.len ); return Err(MemoryAccessError::HeapOutOfBounds); } unsafe { volatile_memcpy_write(data.as_ptr(), self.base.add(offset as usize), data.len()); } Ok(()) } } // We can't use a normal memcpy here because it has undefined behavior if the // memory is being concurrently modified. So we need to write our own memcpy // implementation which uses volatile operations. // // The implementation of these functions can optimize very well when inlined // with a fixed length: they should compile down to a single load/store // instruction for small (8/16/32/64-bit) copies. #[inline] unsafe fn volatile_memcpy_read(mut src: *const u8, mut dst: *mut u8, mut len: usize) { #[inline] unsafe fn copy_one(src: &mut *const u8, dst: &mut *mut u8, len: &mut usize) { #[repr(packed)] struct Unaligned(T); let val = (*src as *const Unaligned).read_volatile(); (*dst as *mut Unaligned).write(val); *src = src.add(mem::size_of::()); *dst = dst.add(mem::size_of::()); *len -= mem::size_of::(); } while len >= 8 { copy_one::(&mut src, &mut dst, &mut len); } if len >= 4 { copy_one::(&mut src, &mut dst, &mut len); } if len >= 2 { copy_one::(&mut src, &mut dst, &mut len); } if len >= 1 { copy_one::(&mut src, &mut dst, &mut len); } } #[inline] unsafe fn volatile_memcpy_write(mut src: *const u8, mut dst: *mut u8, mut len: usize) { #[inline] unsafe fn copy_one(src: &mut *const u8, dst: &mut *mut u8, len: &mut usize) { #[repr(packed)] struct Unaligned(T); let val = (*src as *const Unaligned).read(); (*dst as *mut Unaligned).write_volatile(val); *src = src.add(mem::size_of::()); *dst = dst.add(mem::size_of::()); *len -= mem::size_of::(); } while len >= 8 { copy_one::(&mut src, &mut dst, &mut len); } if len >= 4 { copy_one::(&mut src, &mut dst, &mut len); } if len >= 2 { copy_one::(&mut src, &mut dst, &mut len); } if len >= 1 { copy_one::(&mut src, &mut dst, &mut len); } }