2019-01-08 12:09:47 -05:00
|
|
|
//! The webassembly::Memory() constructor creates a new Memory object which is
|
|
|
|
//! a structure that holds the raw bytes of memory accessed by a
|
|
|
|
//! webassembly::Instance.
|
|
|
|
//! A memory created by Rust or in WebAssembly code will be accessible and
|
|
|
|
//! mutable from both Rust and WebAssembly.
|
|
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
|
2019-01-08 21:57:28 -05:00
|
|
|
use crate::{
|
|
|
|
mmap::{Mmap, Protect},
|
|
|
|
types::Memory,
|
|
|
|
vm::LocalMemory,
|
|
|
|
};
|
2019-01-08 12:09:47 -05:00
|
|
|
|
|
|
|
/// A linear memory instance.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct LinearMemory {
|
|
|
|
/// The actual memory allocation.
|
|
|
|
mmap: Mmap,
|
|
|
|
|
|
|
|
/// The current number of wasm pages.
|
|
|
|
current: u32,
|
|
|
|
|
|
|
|
// The maximum size the WebAssembly Memory is allowed to grow
|
|
|
|
// to, in units of WebAssembly pages. When present, the maximum
|
|
|
|
// parameter acts as a hint to the engine to reserve memory up
|
|
|
|
// front. However, the engine may ignore or clamp this reservation
|
|
|
|
// request. In general, most WebAssembly modules shouldn't need
|
|
|
|
// to set a maximum.
|
|
|
|
max: Option<u32>,
|
|
|
|
|
|
|
|
// The size of the extra guard pages after the end.
|
|
|
|
// Is used to optimize loads and stores with constant offsets.
|
|
|
|
offset_guard_size: usize,
|
|
|
|
|
|
|
|
/// Requires exception catching to handle out-of-bounds accesses.
|
|
|
|
requires_signal_catch: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// It holds the raw bytes of memory accessed by a WebAssembly Instance
|
|
|
|
impl LinearMemory {
|
|
|
|
pub const PAGE_SIZE: u32 = 65_536;
|
|
|
|
pub const MAX_PAGES: u32 = 65_536;
|
|
|
|
pub const DEFAULT_HEAP_SIZE: usize = 1 << 32; // 4 GiB
|
|
|
|
pub const DEFAULT_GUARD_SIZE: usize = 1 << 31; // 2 GiB
|
|
|
|
pub const DEFAULT_SIZE: usize = Self::DEFAULT_HEAP_SIZE + Self::DEFAULT_GUARD_SIZE; // 6 GiB
|
|
|
|
|
|
|
|
/// Create a new linear memory instance with specified initial and maximum number of pages.
|
|
|
|
///
|
|
|
|
/// `maximum` cannot be set to more than `65536` pages.
|
|
|
|
pub fn new(mem: &Memory) -> Self {
|
|
|
|
assert!(mem.min <= Self::MAX_PAGES);
|
|
|
|
assert!(mem.max.is_none() || mem.max.unwrap() <= Self::MAX_PAGES);
|
|
|
|
debug!("Instantiate LinearMemory(mem: {:?})", mem);
|
|
|
|
|
2019-01-10 22:59:57 -05:00
|
|
|
let (mmap_size, initial_pages, offset_guard_size, requires_signal_catch) = if
|
|
|
|
/*mem.is_static_heap()*/
|
|
|
|
true {
|
|
|
|
(Self::DEFAULT_SIZE, mem.min, Self::DEFAULT_GUARD_SIZE, true)
|
|
|
|
// This is a static heap
|
|
|
|
} else {
|
|
|
|
// this is a dynamic heap
|
|
|
|
assert!(!mem.shared, "shared memories must have a maximum size.");
|
2019-01-08 12:09:47 -05:00
|
|
|
|
2019-01-10 22:59:57 -05:00
|
|
|
(
|
|
|
|
mem.min as usize * Self::PAGE_SIZE as usize,
|
|
|
|
mem.min,
|
|
|
|
0,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
};
|
2019-01-08 12:09:47 -05:00
|
|
|
|
|
|
|
let mut mmap = Mmap::with_size(mmap_size).unwrap();
|
|
|
|
|
|
|
|
// map initial pages as readwrite since the inital mmap is mapped as not accessible.
|
|
|
|
if initial_pages != 0 {
|
|
|
|
unsafe {
|
2019-01-08 21:57:28 -05:00
|
|
|
mmap.protect(
|
|
|
|
0..(initial_pages as usize * Self::PAGE_SIZE as usize),
|
|
|
|
Protect::ReadWrite,
|
|
|
|
)
|
|
|
|
.expect("unable to make memory accessible");
|
2019-01-08 12:09:47 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Self {
|
|
|
|
mmap,
|
|
|
|
current: initial_pages,
|
|
|
|
max: mem.max,
|
|
|
|
offset_guard_size,
|
|
|
|
requires_signal_catch,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns an base address of this linear memory.
|
2019-01-09 18:31:11 -05:00
|
|
|
fn base(&mut self) -> *mut u8 {
|
2019-01-08 12:09:47 -05:00
|
|
|
self.mmap.as_ptr()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a number of allocated wasm pages.
|
2019-01-09 18:31:11 -05:00
|
|
|
pub(crate) fn size(&self) -> usize {
|
2019-01-08 12:09:47 -05:00
|
|
|
self.current as usize * Self::PAGE_SIZE as usize
|
|
|
|
}
|
|
|
|
|
2019-01-09 18:31:11 -05:00
|
|
|
pub fn pages(&self) -> u32 {
|
2019-01-08 12:09:47 -05:00
|
|
|
self.current
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the maximum number of wasm pages allowed.
|
2019-01-09 18:31:11 -05:00
|
|
|
pub fn max(&self) -> u32 {
|
2019-01-08 12:09:47 -05:00
|
|
|
self.max.unwrap_or(Self::MAX_PAGES)
|
|
|
|
}
|
|
|
|
|
2019-01-09 18:31:11 -05:00
|
|
|
pub(crate) fn into_vm_memory(&mut self) -> LocalMemory {
|
2019-01-08 12:09:47 -05:00
|
|
|
LocalMemory {
|
|
|
|
base: self.base(),
|
2019-01-09 18:31:11 -05:00
|
|
|
size: self.size(),
|
2019-01-08 12:09:47 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Grow memory by the specified amount of pages.
|
|
|
|
///
|
|
|
|
/// Returns `None` if memory can't be grown by the specified amount
|
|
|
|
/// of pages.
|
2019-01-09 18:31:11 -05:00
|
|
|
pub(crate) fn grow_dynamic(&mut self, add_pages: u32) -> Option<i32> {
|
2019-01-08 12:09:47 -05:00
|
|
|
debug!("grow_memory_dynamic called!");
|
|
|
|
assert!(self.max.is_none());
|
|
|
|
if add_pages == 0 {
|
|
|
|
return Some(self.current as _);
|
|
|
|
}
|
|
|
|
|
|
|
|
let prev_pages = self.current;
|
|
|
|
|
|
|
|
let new_pages = match self.current.checked_add(add_pages) {
|
|
|
|
Some(new_pages) => new_pages,
|
|
|
|
None => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(val) = self.max {
|
|
|
|
if new_pages > val {
|
|
|
|
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.
|
|
|
|
} else if new_pages >= Self::MAX_PAGES {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let new_bytes = (new_pages * Self::PAGE_SIZE) as usize;
|
|
|
|
|
|
|
|
if new_bytes > self.mmap.size() - self.offset_guard_size {
|
|
|
|
let mmap_size = new_bytes.checked_add(self.offset_guard_size)?;
|
|
|
|
let mut new_mmap = Mmap::with_size(mmap_size).ok()?;
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
new_mmap.protect(0..new_bytes, Protect::ReadWrite).ok()?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let copy_size = self.mmap.size() - self.offset_guard_size;
|
|
|
|
unsafe {
|
|
|
|
new_mmap.as_slice_mut()[..copy_size]
|
|
|
|
.copy_from_slice(&self.mmap.as_slice()[..copy_size]);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.mmap = new_mmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.current = new_pages;
|
|
|
|
|
|
|
|
Some(prev_pages as i32)
|
|
|
|
}
|
|
|
|
|
2019-01-09 18:31:11 -05:00
|
|
|
pub(crate) fn grow_static(&mut self, add_pages: u32) -> Option<i32> {
|
2019-01-09 21:14:35 -05:00
|
|
|
// debug!("grow_memory_static called!");
|
|
|
|
// assert!(self.max.is_some());
|
2019-01-08 12:09:47 -05:00
|
|
|
if add_pages == 0 {
|
|
|
|
return Some(self.current as _);
|
|
|
|
}
|
|
|
|
|
|
|
|
let prev_pages = self.current;
|
|
|
|
|
|
|
|
let new_pages = match self.current.checked_add(add_pages) {
|
|
|
|
Some(new_pages) => new_pages,
|
|
|
|
None => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(val) = self.max {
|
|
|
|
if new_pages > val {
|
|
|
|
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.
|
|
|
|
} else if new_pages >= Self::MAX_PAGES {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let prev_bytes = (prev_pages * Self::PAGE_SIZE) as usize;
|
|
|
|
let new_bytes = (new_pages * Self::PAGE_SIZE) as usize;
|
|
|
|
|
|
|
|
unsafe {
|
2019-01-08 21:57:28 -05:00
|
|
|
self.mmap
|
|
|
|
.protect(prev_bytes..new_bytes, Protect::ReadWrite)
|
|
|
|
.ok()?;
|
2019-01-08 12:09:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
self.current = new_pages;
|
|
|
|
|
|
|
|
Some(prev_pages as i32)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Deref for LinearMemory {
|
|
|
|
type Target = [u8];
|
|
|
|
fn deref(&self) -> &[u8] {
|
2019-01-08 21:57:28 -05:00
|
|
|
unsafe { self.mmap.as_slice() }
|
2019-01-08 12:09:47 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DerefMut for LinearMemory {
|
|
|
|
fn deref_mut(&mut self) -> &mut [u8] {
|
2019-01-08 21:57:28 -05:00
|
|
|
unsafe { self.mmap.as_slice_mut() }
|
2019-01-08 12:09:47 -05:00
|
|
|
}
|
|
|
|
}
|