//! Memory management for linear memories. //! //! `LinearMemory` is to WebAssembly linear memories what `Table` is to WebAssembly tables. use crate::mmap::Mmap; use crate::vmcontext::VMMemoryDefinition; use std::string::String; use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE}; /// A linear memory instance. #[derive(Debug)] pub struct LinearMemory { // The underlying allocation. mmap: Mmap, // The current logical size in wasm pages of this linear memory. current: u32, // The optional maximum size in wasm pages of this linear memory. maximum: Option, // Size in bytes of extra guard pages after the end to optimize loads and stores with // constant offsets. offset_guard_size: usize, // Records whether we're using a bounds-checking strategy which requires // handlers to catch trapping accesses. pub(crate) needs_signal_handlers: bool, } impl LinearMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. pub fn new(plan: &MemoryPlan) -> Result { // `maximum` cannot be set to more than `65536` pages. assert!(plan.memory.minimum <= WASM_MAX_PAGES); assert!(plan.memory.maximum.is_none() || plan.memory.maximum.unwrap() <= WASM_MAX_PAGES); let offset_guard_bytes = plan.offset_guard_size as usize; // If we have an offset guard, or if we're doing the static memory // allocation strategy, we need signal handlers to catch out of bounds // acceses. let needs_signal_handlers = offset_guard_bytes > 0 || match plan.style { MemoryStyle::Dynamic => false, MemoryStyle::Static { .. } => true, }; let minimum_pages = match plan.style { MemoryStyle::Dynamic => plan.memory.minimum, MemoryStyle::Static { bound } => { assert!(bound >= plan.memory.minimum); bound } } as usize; let minimum_bytes = minimum_pages.checked_mul(WASM_PAGE_SIZE as usize).unwrap(); let request_bytes = minimum_bytes.checked_add(offset_guard_bytes).unwrap(); let mapped_pages = plan.memory.minimum as usize; let mapped_bytes = mapped_pages * WASM_PAGE_SIZE as usize; let mmap = Mmap::accessible_reserved(mapped_bytes, request_bytes)?; Ok(Self { mmap, current: plan.memory.minimum, maximum: plan.memory.maximum, offset_guard_size: offset_guard_bytes, needs_signal_handlers, }) } /// Returns the number of allocated wasm pages. pub fn size(&self) -> u32 { self.current } /// Grow memory by the specified amount of wasm pages. /// /// Returns `None` if memory can't be grown by the specified amount /// of wasm pages. pub fn grow(&mut self, delta: u32) -> Option { let new_pages = match self.current.checked_add(delta) { Some(new_pages) => new_pages, // Linear memory size overflow. None => return None, }; let prev_pages = self.current; if let Some(maximum) = self.maximum { if new_pages > maximum { // Linear memory size would exceed the declared maximum. return None; } } // Wasm linear memories are never allowed to grow beyond what is // indexable. If the memory has no maximum, enforce the greatest // limit here. if new_pages >= WASM_MAX_PAGES { // Linear memory size would exceed the index range. return None; } let delta_bytes = cast::usize(delta) * WASM_PAGE_SIZE as usize; let prev_bytes = cast::usize(prev_pages) * WASM_PAGE_SIZE as usize; let new_bytes = cast::usize(new_pages) * WASM_PAGE_SIZE as usize; if new_bytes > self.mmap.len() - self.offset_guard_size { // 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. let guard_bytes = self.offset_guard_size; let request_bytes = new_bytes.checked_add(guard_bytes)?; let mut new_mmap = Mmap::accessible_reserved(new_bytes, request_bytes).ok()?; let copy_len = self.mmap.len() - self.offset_guard_size; new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&self.mmap.as_slice()[..copy_len]); self.mmap = new_mmap; } else if delta_bytes > 0 { // Make the newly allocated pages accessible. self.mmap.make_accessible(prev_bytes, delta_bytes).ok()?; } self.current = new_pages; Some(prev_pages) } /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. pub fn vmmemory(&mut self) -> VMMemoryDefinition { VMMemoryDefinition { base: self.mmap.as_mut_ptr(), current_length: self.current as usize * WASM_PAGE_SIZE as usize, } } }