diff --git a/Cargo.toml b/Cargo.toml index c15d39019a..785e809974 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,10 @@ name = "wasm2obj" path = "src/wasm2obj.rs" [dependencies] -cranelift-codegen = "0.25.0" -cranelift-native = "0.25.0" -cranelift-entity = "0.25.0" -cranelift-wasm = "0.25.0" +cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } wasmtime-environ = { path = "lib/environ" } wasmtime-execute = { path = "lib/execute" } wasmtime-obj = { path = "lib/obj" } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 238b9b8a3a..3e83ceabc1 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,9 +10,9 @@ cargo-fuzz = true [dependencies] wasmtime-environ = { path = "../lib/environ" } wasmtime-execute = { path = "../lib/execute" } -cranelift-codegen = "0.25.0" -cranelift-wasm = "0.25.0" -cranelift-native = "0.25.0" +cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } wasmparser = { version = "0.22.0", default-features = false } diff --git a/lib/environ/Cargo.toml b/lib/environ/Cargo.toml index 4debd42e60..33acdbf194 100644 --- a/lib/environ/Cargo.toml +++ b/lib/environ/Cargo.toml @@ -10,9 +10,9 @@ license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" [dependencies] -cranelift-codegen = "0.25.0" -cranelift-entity = "0.25.0" -cranelift-wasm = "0.25.0" +cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } memoffset = "0.2.1" [features] diff --git a/lib/environ/src/environ.rs b/lib/environ/src/environ.rs index 9b4bcce805..7335eedd24 100644 --- a/lib/environ/src/environ.rs +++ b/lib/environ/src/environ.rs @@ -1,6 +1,6 @@ use cranelift_codegen::cursor::FuncCursor; use cranelift_codegen::ir; -use cranelift_codegen::ir::immediates::{Imm64, Offset32}; +use cranelift_codegen::ir::immediates::{Imm64, Offset32, Uimm64}; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{ AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, InstBuilder, Signature, @@ -11,11 +11,16 @@ use cranelift_wasm::{ self, translate_module, FuncIndex, Global, GlobalIndex, GlobalVariable, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, WasmResult, }; -use module::{DataInitializer, Export, LazyContents, Module, TableElements}; +use module::{ + DataInitializer, Export, LazyContents, MemoryPlan, MemoryStyle, Module, TableElements, +}; +use std::clone::Clone; use std::mem; use std::string::String; use std::vec::Vec; +use tunables::Tunables; use vmcontext; +use WASM_PAGE_SIZE; /// Compute a `ir::ExternalName` for a given wasm function index. pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName { @@ -34,20 +39,28 @@ pub struct ModuleEnvironment<'data, 'module> { /// References to information to be decoded later. pub lazy: LazyContents<'data>, + + /// Tunable parameters. + pub tunables: Tunables, } impl<'data, 'module> ModuleEnvironment<'data, 'module> { /// Allocates the enironment data structures with the given isa. - pub fn new(isa: &'module isa::TargetIsa, module: &'module mut Module) -> Self { + pub fn new( + isa: &'module isa::TargetIsa, + module: &'module mut Module, + tunables: Tunables, + ) -> Self { Self { isa, module, lazy: LazyContents::new(), + tunables, } } fn func_env(&self) -> FuncEnvironment { - FuncEnvironment::new(self.isa, &self.module) + FuncEnvironment::new(self.isa, &self.module, self.tunables.clone()) } fn pointer_type(&self) -> ir::Type { @@ -66,6 +79,7 @@ impl<'data, 'module> ModuleEnvironment<'data, 'module> { isa: self.isa, module: self.module, lazy: self.lazy, + tunables: self.tunables, }) } } @@ -95,12 +109,16 @@ pub struct FuncEnvironment<'module_environment> { /// The external function declaration for implementing wasm's `grow_memory`. pub grow_memory_extfunc: Option, + + /// Tunable parameters. + pub tunables: Tunables, } impl<'module_environment> FuncEnvironment<'module_environment> { pub fn new( isa: &'module_environment isa::TargetIsa, module: &'module_environment Module, + tunables: Tunables, ) -> Self { Self { isa, @@ -111,6 +129,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> { globals_base: None, current_memory_extfunc: None, grow_memory_extfunc: None, + tunables, } } @@ -228,7 +247,8 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data> } fn declare_memory(&mut self, memory: Memory) { - self.module.memories.push(memory); + let plan = MemoryPlan::for_memory(memory, &self.tunables); + self.module.memory_plans.push(plan); } fn declare_data_initialization( @@ -330,16 +350,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m debug_assert_eq!(offset32 as usize, offset); // If we have a declared maximum, we can make this a "static" heap, which is // allocated up front and never moved. - let (guard_size, heap_style, readonly_base) = - if self.module.memories[index].maximum.is_some() { - ( - 0x8000_0000.into(), - ir::HeapStyle::Static { - bound: 0x1_0000_0000.into(), - }, - true, - ) - } else { + let (offset_guard_size, heap_style, readonly_base) = match self.module.memory_plans[index] { + MemoryPlan { + memory: _, + style: MemoryStyle::Dynamic, + offset_guard_size, + } => { let heap_bound = func.create_global_value(ir::GlobalValueData::Load { base: memories_base, offset: Offset32::new( @@ -349,13 +365,26 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m readonly: false, }); ( - 0.into(), + Uimm64::new(offset_guard_size), ir::HeapStyle::Dynamic { bound_gv: heap_bound, }, false, ) - }; + } + MemoryPlan { + memory: _, + style: MemoryStyle::Static { bound }, + offset_guard_size, + } => ( + Uimm64::new(offset_guard_size), + ir::HeapStyle::Static { + bound: Uimm64::new(u64::from(bound) * u64::from(WASM_PAGE_SIZE)), + }, + true, + ), + }; + let heap_base = func.create_global_value(ir::GlobalValueData::Load { base: memories_base, offset: Offset32::new(offset32 + offset_of!(vmcontext::VMMemory, base) as i32), @@ -365,7 +394,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m func.create_heap(ir::HeapData { base: heap_base, min_size: 0.into(), - guard_size, + offset_guard_size, style: heap_style, index_type: I32, }) @@ -403,9 +432,9 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m func.create_table(ir::TableData { base_gv, - min_size: Imm64::new(0), + min_size: Uimm64::new(0), bound_gv, - element_size: Imm64::new(i64::from(self.pointer_bytes())), + element_size: Uimm64::new(u64::from(self.pointer_bytes())), index_type: I32, }) } @@ -548,12 +577,14 @@ pub struct ModuleTranslation<'data, 'module> { /// Pointers into the raw data buffer. pub lazy: LazyContents<'data>, + + /// Tunable parameters. + pub tunables: Tunables, } -/// Convenience functions for the user to be called after execution for debug purposes. impl<'data, 'module> ModuleTranslation<'data, 'module> { /// Return a new `FuncEnvironment` for translation a function. pub fn func_env(&self) -> FuncEnvironment { - FuncEnvironment::new(self.isa, &self.module) + FuncEnvironment::new(self.isa, &self.module, self.tunables.clone()) } } diff --git a/lib/environ/src/lib.rs b/lib/environ/src/lib.rs index edbc4cb3e6..fb3b98c159 100644 --- a/lib/environ/src/lib.rs +++ b/lib/environ/src/lib.rs @@ -42,11 +42,19 @@ extern crate alloc; mod compilation; mod environ; mod module; +mod tunables; mod vmcontext; pub use compilation::{compile_module, Compilation, Relocation, RelocationTarget, Relocations}; pub use environ::{ModuleEnvironment, ModuleTranslation}; -pub use module::{DataInitializer, Export, Module, TableElements}; +pub use module::{DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements}; +pub use tunables::Tunables; + +/// WebAssembly page sizes are defined to be 64KiB. +pub const WASM_PAGE_SIZE: u32 = 0x10000; + +/// The number of pages we can have before we run out of byte index space. +pub const WASM_MAX_PAGES: u32 = 0x10000; #[cfg(not(feature = "std"))] mod std { diff --git a/lib/environ/src/module.rs b/lib/environ/src/module.rs index c9bed3266c..a12e528aca 100644 --- a/lib/environ/src/module.rs +++ b/lib/environ/src/module.rs @@ -6,9 +6,11 @@ use cranelift_wasm::{ DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, }; +use std::cmp; use std::collections::HashMap; use std::string::String; use std::vec::Vec; +use tunables::Tunables; /// A WebAssembly table initializer. #[derive(Clone, Debug)] @@ -36,6 +38,59 @@ pub enum Export { Global(GlobalIndex), } +/// Implemenation styles for WebAssembly linear memory. +#[derive(Debug, Clone)] +pub enum MemoryStyle { + /// The actual memory can be resized and moved. + Dynamic, + /// Addresss space is allocated up front. + Static { + /// The number of mapped and unmapped pages. + bound: u32, + }, +} + +impl MemoryStyle { + /// Decide on an implementation style for the given `Memory`. + pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self { + if let Some(maximum) = memory.maximum { + // A heap with a declared maximum is prepared to be used with + // threads and therefore be immovable, so make it static. + MemoryStyle::Static { + bound: cmp::max(tunables.static_memory_bound, maximum), + } + } else { + // A heap without a declared maximum is likely to want to be small + // at least some of the time, so make it dynamic. + MemoryStyle::Dynamic + } + } +} + +/// A WebAssembly linear memory description along with our chosen style for +/// implementing it. +#[derive(Debug)] +pub struct MemoryPlan { + /// The WebAssembly linear memory description. + pub memory: Memory, + /// Our chosen implementation style. + pub style: MemoryStyle, + /// Our chosen offset-guard size. + pub offset_guard_size: u64, +} + +impl MemoryPlan { + /// Draw up a plan for implementing `Memory`. + pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self { + Self { + memory, + style: MemoryStyle::for_memory(memory, tunables), + // fixme: saturate this + offset_guard_size: tunables.offset_guard_size, + } + } +} + /// A translated WebAssembly module, excluding the function bodies and /// memory initializers. #[derive(Debug)] @@ -52,8 +107,8 @@ pub struct Module { /// WebAssembly tables. pub tables: PrimaryMap, - /// WebAssembly linear memories. - pub memories: PrimaryMap, + /// WebAssembly linear memory plans. + pub memory_plans: PrimaryMap, /// WebAssembly global variables. pub globals: PrimaryMap, @@ -76,7 +131,7 @@ impl Module { imported_funcs: Vec::new(), functions: PrimaryMap::new(), tables: PrimaryMap::new(), - memories: PrimaryMap::new(), + memory_plans: PrimaryMap::new(), globals: PrimaryMap::new(), exports: HashMap::new(), start_func: None, diff --git a/lib/execute/Cargo.toml b/lib/execute/Cargo.toml index 9c587b1a64..306b8ad7f1 100644 --- a/lib/execute/Cargo.toml +++ b/lib/execute/Cargo.toml @@ -10,14 +10,14 @@ license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" [dependencies] -cranelift-codegen = "0.25.0" -cranelift-entity = "0.25.0" -cranelift-wasm = "0.25.0" +cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } wasmtime-environ = { path = "../environ" } region = "1.0.0" -memmap = "0.7.0" lazy_static = "1.2.0" libc = "0.2.44" +errno = "0.2.4" [build-dependencies] cmake = "0.1.35" diff --git a/lib/execute/src/instance.rs b/lib/execute/src/instance.rs index 36ade34767..ddb95611fe 100644 --- a/lib/execute/src/instance.rs +++ b/lib/execute/src/instance.rs @@ -28,16 +28,16 @@ impl Instance { module: &Module, compilation: &Compilation, data_initializers: &[DataInitializer], - ) -> Self { + ) -> Result { let mut result = Self { tables: PrimaryMap::new(), memories: PrimaryMap::new(), globals: Vec::new(), }; result.instantiate_tables(module, compilation, &module.table_elements); - result.instantiate_memories(module, data_initializers); + result.instantiate_memories(module, data_initializers)?; result.instantiate_globals(module); - result + Ok(result) } /// Allocate memory in `self` for just the tables of the current module. @@ -48,10 +48,9 @@ impl Instance { table_initializers: &[TableElements], ) { debug_assert!(self.tables.is_empty()); - // TODO: Enable this once PrimaryMap supports this. - //self.tables.reserve_exact(module.tables.len()); + self.tables.reserve_exact(module.tables.len()); for table in module.tables.values() { - let len = table.size; + let len = table.minimum as usize; let mut v = Vec::with_capacity(len); v.resize(len, 0); self.tables.push(v); @@ -70,13 +69,16 @@ impl Instance { } /// Allocate memory in `instance` for just the memories of the current module. - fn instantiate_memories(&mut self, module: &Module, data_initializers: &[DataInitializer]) { + fn instantiate_memories( + &mut self, + module: &Module, + data_initializers: &[DataInitializer], + ) -> Result<(), String> { debug_assert!(self.memories.is_empty()); // Allocate the underlying memory and initialize it to all zeros. - // TODO: Enable this once PrimaryMap supports it. - //self.memories.reserve_exact(module.memories.len()); - for memory in module.memories.values() { - let v = LinearMemory::new(memory.pages_count as u32, memory.maximum.map(|m| m as u32)); + self.memories.reserve_exact(module.memory_plans.len()); + for plan in module.memory_plans.values() { + let v = LinearMemory::new(&plan)?; self.memories.push(v); } for init in data_initializers { @@ -85,6 +87,7 @@ impl Instance { let to_init = &mut mem_mut[init.offset..init.offset + init.data.len()]; to_init.copy_from_slice(init.data); } + Ok(()) } /// Allocate memory in `instance` for just the globals of the current module, diff --git a/lib/execute/src/lib.rs b/lib/execute/src/lib.rs index b03046ef75..ae73241637 100644 --- a/lib/execute/src/lib.rs +++ b/lib/execute/src/lib.rs @@ -30,7 +30,7 @@ extern crate cranelift_codegen; extern crate cranelift_entity; extern crate cranelift_wasm; -extern crate memmap; +extern crate errno; extern crate region; extern crate wasmtime_environ; #[cfg(not(feature = "std"))] diff --git a/lib/execute/src/memory.rs b/lib/execute/src/memory.rs index 4830b93861..916d7af9a9 100644 --- a/lib/execute/src/memory.rs +++ b/lib/execute/src/memory.rs @@ -1,92 +1,225 @@ -use memmap; +use errno; +use libc; +use region; use std::fmt; +use std::mem; +use std::ptr; +use std::slice; +use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE}; -const PAGE_SIZE: u32 = 65536; -const MAX_PAGES: u32 = 65536; +/// Round `size` up to the nearest multiple of `page_size`. +fn round_up_to_page_size(size: usize, page_size: usize) -> usize { + (size + (page_size - 1)) & !(page_size - 1) +} + +/// A simple struct consisting of a page-aligned pointer to page-aligned +/// and initially-zeroed memory and a length. +struct PtrLen { + ptr: *mut u8, + len: usize, +} + +impl PtrLen { + /// Create a new `PtrLen` pointing to at least `size` bytes of memory, + /// suitably sized and aligned for memory protection. + #[cfg(not(target_os = "windows"))] + fn with_size(size: usize) -> Result { + let page_size = region::page::size(); + let alloc_size = round_up_to_page_size(size, page_size); + unsafe { + let ptr = libc::mmap( + ptr::null_mut(), + alloc_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + if mem::transmute::<_, isize>(ptr) != -1isize { + Ok(Self { + ptr: ptr as *mut u8, + len: alloc_size, + }) + } else { + Err(errno::errno().to_string()) + } + } + } + + #[cfg(target_os = "windows")] + fn with_size(size: usize) -> Result { + use winapi::um::memoryapi::VirtualAlloc; + use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE}; + + let page_size = region::page::size(); + + // VirtualAlloc always rounds up to the next multiple of the page size + let ptr = unsafe { + VirtualAlloc( + ptr::null_mut(), + size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + ) + }; + if !ptr.is_null() { + Ok(Self { + ptr: ptr as *mut u8, + len: round_up_to_page_size(size, page_size), + }) + } else { + Err(errno::errno().to_string()) + } + } + + fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.ptr, self.len) } + } + + fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.ptr, self.len) } + } +} + +impl Drop for PtrLen { + #[cfg(not(target_os = "windows"))] + fn drop(&mut self) { + let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.len) }; + assert_eq!(r, 0); + } + + #[cfg(target_os = "windows")] + fn drop(&mut self) { + use winapi::um::memoryapi::VirtualFree; + use winapi::um::winnt::MEM_RELEASE; + let r = unsafe { VirtualFree(self.ptr, self.len, MEM_RELEASE) }; + assert_eq!(r, 0); + } +} /// A linear memory instance. /// /// This linear memory has a stable base address and at the same time allows /// for dynamical growing. pub struct LinearMemory { - mmap: memmap::MmapMut, + ptrlen: PtrLen, current: u32, maximum: Option, + offset_guard_size: usize, } impl LinearMemory { - /// 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(initial: u32, maximum: Option) -> Self { - assert!(initial <= MAX_PAGES); - assert!(maximum.is_none() || maximum.unwrap() <= MAX_PAGES); + /// Create a new linear memory instance with specified minimum and maximum number of 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 len = PAGE_SIZE * match maximum { - Some(val) => val, - None => initial, - }; - let mmap = memmap::MmapMut::map_anon(len as usize).unwrap(); - Self { - mmap, - current: initial, - maximum, + let offset_guard_bytes = plan.offset_guard_size as usize; + + 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 unmapped_pages = minimum_pages - mapped_pages; + let unmapped_bytes = unmapped_pages * WASM_PAGE_SIZE as usize; + let inaccessible_bytes = unmapped_bytes + offset_guard_bytes; + + let ptrlen = PtrLen::with_size(request_bytes)?; + + // Make the unmapped and offset-guard pages inaccessible. + unsafe { + region::protect( + ptrlen.ptr.add(mapped_bytes), + inaccessible_bytes, + region::Protection::Read, + ).expect("unable to make memory readonly"); } + + Ok(Self { + ptrlen, + current: plan.memory.minimum, + maximum: plan.memory.maximum, + offset_guard_size: offset_guard_bytes, + }) } /// Returns an base address of this linear memory. pub fn base_addr(&mut self) -> *mut u8 { - self.mmap.as_mut_ptr() + self.ptrlen.ptr } /// Returns a number of allocated wasm pages. pub fn current_size(&self) -> u32 { - self.current + assert_eq!(self.ptrlen.len % WASM_PAGE_SIZE as usize, 0); + let num_pages = self.ptrlen.len / WASM_PAGE_SIZE as usize; + assert_eq!(num_pages as u32 as usize, num_pages); + num_pages as u32 } /// Grow memory by the specified amount of pages. /// /// Returns `None` if memory can't be grown by the specified amount /// of pages. - pub fn grow(&mut self, add_pages: u32) -> Option { - let new_pages = match self.current.checked_add(add_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, }; - if let Some(val) = self.maximum { - if new_pages > val { - return None; - } - } else { - // 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 >= 65536 { + 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; } } - let prev_pages = self.current; - let new_bytes = (new_pages * PAGE_SIZE) as usize; + // 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; + } - if self.mmap.len() < new_bytes { - // If we have no maximum, this is a "dynamic" heap, and it's allowed - // to move. + let new_bytes = new_pages as usize * WASM_PAGE_SIZE as usize; + + if new_bytes > self.ptrlen.len { + // If we have no maximum, this is a "dynamic" heap, and it's allowed to move. assert!(self.maximum.is_none()); - let mut new_mmap = memmap::MmapMut::map_anon(new_bytes).unwrap(); - new_mmap.copy_from_slice(&self.mmap); - self.mmap = new_mmap; + let mapped_pages = self.current as usize; + let mapped_bytes = mapped_pages * WASM_PAGE_SIZE as usize; + let guard_bytes = self.offset_guard_size; + + let mut new_ptrlen = PtrLen::with_size(new_bytes).ok()?; + + // Make the offset-guard pages inaccessible. + unsafe { + region::protect( + new_ptrlen.ptr.add(mapped_bytes), + guard_bytes, + region::Protection::Read, + ).expect("unable to make memory readonly"); + } + + new_ptrlen + .as_mut_slice() + .copy_from_slice(self.ptrlen.as_slice()); + + self.ptrlen = new_ptrlen; } self.current = new_pages; - // Ensure that newly allocated area is zeroed. - let new_start_offset = (prev_pages * PAGE_SIZE) as usize; - let new_end_offset = (new_pages * PAGE_SIZE) as usize; - for i in new_start_offset..new_end_offset { - assert!(self.mmap[i] == 0); - } - Some(prev_pages) } } @@ -102,12 +235,25 @@ impl fmt::Debug for LinearMemory { impl AsRef<[u8]> for LinearMemory { fn as_ref(&self) -> &[u8] { - &self.mmap + self.ptrlen.as_slice() } } impl AsMut<[u8]> for LinearMemory { fn as_mut(&mut self) -> &mut [u8] { - &mut self.mmap + self.ptrlen.as_mut_slice() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_round_up_to_page_size() { + assert_eq!(round_up_to_page_size(0, 4096), 0); + assert_eq!(round_up_to_page_size(1, 4096), 4096); + assert_eq!(round_up_to_page_size(4096, 4096), 4096); + assert_eq!(round_up_to_page_size(4097, 4096), 8192); } } diff --git a/lib/obj/Cargo.toml b/lib/obj/Cargo.toml index b6fc681398..9f7415a55b 100644 --- a/lib/obj/Cargo.toml +++ b/lib/obj/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" [dependencies] -cranelift-codegen = "0.25.0" -cranelift-entity = "0.25.0" +cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } +cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" } wasmtime-environ = { path = "../environ" } faerie = "0.6.0" diff --git a/src/main.rs b/src/main.rs index 63bb9bdaa9..4936e9bf7f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,8 +59,8 @@ use std::io::prelude::*; use std::io::stdout; use std::path::Path; use std::path::PathBuf; -use std::process::exit; -use wasmtime_environ::{Module, ModuleEnvironment}; +use std::process::{exit, Command}; +use wasmtime_environ::{Module, ModuleEnvironment, Tunables}; use wasmtime_execute::{compile_and_link_module, execute, finish_instantiation, Instance}; static LOG_FILENAME_PREFIX: &str = "cranelift.dbg."; @@ -149,7 +149,9 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri data = wabt::wat2wasm(data).map_err(|err| String::from(err.description()))?; } let mut module = Module::new(); - let environ = ModuleEnvironment::new(isa, &mut module); + // TODO: Expose the tunables as command-line flags. + let tunables = Tunables::default(); + let environ = ModuleEnvironment::new(isa, &mut module, tunables); let imports_resolver = |_env: &str, _function: &str| None; @@ -161,7 +163,7 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri translation.module, &compilation, &translation.lazy.data_initializers, - ); + )?; let mut context = finish_instantiation(&translation.module, &compilation, &mut instance)?; @@ -219,7 +221,7 @@ mod tests { use cranelift_codegen::settings::Configurable; use std::path::PathBuf; use wabt; - use wasmtime_environ::{Module, ModuleEnvironment}; + use wasmtime_environ::{Module, ModuleEnvironment, Tunables}; const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat"; @@ -242,7 +244,8 @@ mod tests { let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let mut module = Module::new(); - let environ = ModuleEnvironment::new(&*isa, &mut module); + let tunables = Tunables::default(); + let environ = ModuleEnvironment::new(&*isa, &mut module, tunables); let translation = environ.translate(&data); assert!(translation.is_ok()); diff --git a/src/wasm2obj.rs b/src/wasm2obj.rs index e7785dba62..766e3f1560 100644 --- a/src/wasm2obj.rs +++ b/src/wasm2obj.rs @@ -56,7 +56,7 @@ use std::path::PathBuf; use std::process; use std::str::FromStr; use target_lexicon::Triple; -use wasmtime_environ::{compile_module, Module, ModuleEnvironment}; +use wasmtime_environ::{compile_module, Module, ModuleEnvironment, Tunables}; use wasmtime_obj::emit_module; const USAGE: &str = " @@ -136,7 +136,9 @@ fn handle_module(path: PathBuf, target: &Option, output: &str) -> Result let mut obj = Artifact::new(isa.triple().clone(), String::from(output)); let mut module = Module::new(); - let environ = ModuleEnvironment::new(&*isa, &mut module); + // TODO: Expose the tunables as command-line flags. + let tunables = Tunables::default(); + let environ = ModuleEnvironment::new(&*isa, &mut module, tunables); let translation = environ.translate(&data).map_err(|e| e.to_string())?; // FIXME: We need to initialize memory in a way that supports alternate