diff --git a/crates/cache/src/lib.rs b/crates/cache/src/lib.rs index 9fe69c5bafd2..fc50ff4dad7c 100644 --- a/crates/cache/src/lib.rs +++ b/crates/cache/src/lib.rs @@ -43,7 +43,7 @@ impl<'config> ModuleCacheEntry<'config> { } /// Gets cached data if state matches, otherwise calls the `compute`. - pub fn get_data(&self, state: T, compute: fn(T) -> Result) -> Result + pub fn get_data(&self, state: T, compute: impl Fn(T) -> Result) -> Result where T: Hash, U: Serialize + for<'a> Deserialize<'a>, diff --git a/crates/cache/src/tests.rs b/crates/cache/src/tests.rs index 4362aaba2222..857a1f0ab76b 100644 --- a/crates/cache/src/tests.rs +++ b/crates/cache/src/tests.rs @@ -65,28 +65,68 @@ fn test_write_read_cache() { let entry1 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(compiler1, &cache_config)); let entry2 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(compiler2, &cache_config)); - entry1.get_data::<_, i32, i32>(1, |_| Ok(100)).unwrap(); - entry1.get_data::<_, i32, i32>(1, |_| panic!()).unwrap(); + entry1 + .get_data(1, |_| -> Result { Ok(100) }) + .unwrap(); + entry1 + .get_data(1, |_| -> Result { panic!() }) + .unwrap(); - entry1.get_data::<_, i32, i32>(2, |_| Ok(100)).unwrap(); - entry1.get_data::<_, i32, i32>(1, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(2, |_| panic!()).unwrap(); + entry1 + .get_data(2, |_| -> Result { Ok(100) }) + .unwrap(); + entry1 + .get_data(1, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(2, |_| -> Result { panic!() }) + .unwrap(); - entry1.get_data::<_, i32, i32>(3, |_| Ok(100)).unwrap(); - entry1.get_data::<_, i32, i32>(1, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(2, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(3, |_| panic!()).unwrap(); + entry1 + .get_data(3, |_| -> Result { Ok(100) }) + .unwrap(); + entry1 + .get_data(1, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(2, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(3, |_| -> Result { panic!() }) + .unwrap(); - entry1.get_data::<_, i32, i32>(4, |_| Ok(100)).unwrap(); - entry1.get_data::<_, i32, i32>(1, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(2, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(3, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(4, |_| panic!()).unwrap(); + entry1 + .get_data(4, |_| -> Result { Ok(100) }) + .unwrap(); + entry1 + .get_data(1, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(2, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(3, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(4, |_| -> Result { panic!() }) + .unwrap(); - entry2.get_data::<_, i32, i32>(1, |_| Ok(100)).unwrap(); - entry1.get_data::<_, i32, i32>(1, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(2, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(3, |_| panic!()).unwrap(); - entry1.get_data::<_, i32, i32>(4, |_| panic!()).unwrap(); - entry2.get_data::<_, i32, i32>(1, |_| panic!()).unwrap(); + entry2 + .get_data(1, |_| -> Result { Ok(100) }) + .unwrap(); + entry1 + .get_data(1, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(2, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(3, |_| -> Result { panic!() }) + .unwrap(); + entry1 + .get_data(4, |_| -> Result { panic!() }) + .unwrap(); + entry2 + .get_data(1, |_| -> Result { panic!() }) + .unwrap(); } diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index 957feb55f0a7..7282501e4a59 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -42,8 +42,16 @@ impl MemoryStyle { // A heap with a maximum that doesn't exceed the static memory bound specified by the // tunables make it static. // - // If the module doesn't declare an explicit maximum treat it as 4GiB. - let maximum = memory.maximum.unwrap_or(WASM_MAX_PAGES); + // If the module doesn't declare an explicit maximum treat it as 4GiB when not + // requested to use the static memory bound itself as the maximum. + let maximum = memory + .maximum + .unwrap_or(if tunables.static_memory_bound_is_maximum { + tunables.static_memory_bound + } else { + WASM_MAX_PAGES + }); + if maximum <= tunables.static_memory_bound { assert_ge!(tunables.static_memory_bound, memory.minimum); return ( diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs index bd86fef237f9..4e5aba91450e 100644 --- a/crates/environ/src/tunables.rs +++ b/crates/environ/src/tunables.rs @@ -27,6 +27,9 @@ pub struct Tunables { /// Whether or not fuel is enabled for generated code, meaning that fuel /// will be consumed every time a wasm instruction is executed. pub consume_fuel: bool, + + /// Whether or not to treat the static memory bound as the maximum for unbounded heaps. + pub static_memory_bound_is_maximum: bool, } impl Default for Tunables { @@ -62,6 +65,7 @@ impl Default for Tunables { parse_wasm_debuginfo: true, interruptable: false, consume_fuel: false, + static_memory_bound_is_maximum: false, } } } diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index a1647cde5a9b..4d43f43ba12b 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -101,6 +101,7 @@ impl CompilationArtifacts { pub fn build( compiler: &Compiler, data: &[u8], + validate: impl Fn(&ModuleTranslation) -> Result<(), String> + Sync, ) -> Result<(usize, Vec, TypeTables), SetupError> { let (main_module, translations, types) = ModuleEnvironment::new( compiler.frontend_config(), @@ -112,6 +113,8 @@ impl CompilationArtifacts { let list = maybe_parallel!(translations.(into_iter | into_par_iter)) .map(|mut translation| { + validate(&translation).map_err(|e| SetupError::Validate(e))?; + let Compilation { obj, unwind_info, diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs index 395b7c120fd0..bda83832b6af 100644 --- a/crates/runtime/src/instance/allocator.rs +++ b/crates/runtime/src/instance/allocator.rs @@ -24,7 +24,9 @@ use wasmtime_environ::wasm::{ DefinedFuncIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, GlobalInit, SignatureIndex, TableElementType, WasmType, }; -use wasmtime_environ::{ir, Module, ModuleType, OwnedDataInitializer, TableElements, VMOffsets}; +use wasmtime_environ::{ + ir, Module, ModuleTranslation, ModuleType, OwnedDataInitializer, TableElements, VMOffsets, +}; /// Represents a request for a new runtime instance. pub struct InstanceAllocationRequest<'a> { @@ -80,6 +82,21 @@ pub enum InstantiationError { /// /// This trait is unsafe as it requires knowledge of Wasmtime's runtime internals to implement correctly. pub unsafe trait InstanceAllocator: Send + Sync { + /// Validates a module translation. + /// + /// This is used to ensure a module being compiled is supported by the instance allocator. + fn validate_module(&self, translation: &ModuleTranslation) -> Result<(), String> { + drop(translation); + Ok(()) + } + + /// Adjusts the tunables prior to creation of any JIT compiler. + /// + /// This method allows the instance allocator control over tunables passed to a `wasmtime_jit::Compiler`. + fn adjust_tunables(&self, tunables: &mut wasmtime_environ::Tunables) { + drop(tunables); + } + /// Allocates an instance for the given allocation request. /// /// # Safety diff --git a/crates/runtime/src/memory.rs b/crates/runtime/src/memory.rs index 340feb31304d..a2c03e1d31d3 100644 --- a/crates/runtime/src/memory.rs +++ b/crates/runtime/src/memory.rs @@ -52,10 +52,6 @@ pub struct MmapMemory { // 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, } #[derive(Debug)] @@ -75,15 +71,6 @@ impl MmapMemory { 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 } => { @@ -105,7 +92,6 @@ impl MmapMemory { mmap: mmap.into(), maximum: plan.memory.maximum, offset_guard_size: offset_guard_bytes, - needs_signal_handlers, }) } } diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 60adfb3005f7..324b03501d13 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -764,7 +764,9 @@ impl Config { pub(crate) fn build_compiler(&self) -> Compiler { let isa = self.target_isa(); - Compiler::new(isa, self.strategy, self.tunables.clone(), self.features) + let mut tunables = self.tunables.clone(); + self.instance_allocator().adjust_tunables(&mut tunables); + Compiler::new(isa, self.strategy, tunables, self.features) } pub(crate) fn instance_allocator(&self) -> &dyn InstanceAllocator { diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index 0bcf8f568657..a9d32ee1d17d 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -307,15 +307,22 @@ impl Module { /// # } /// ``` pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result { + // Check with the instance allocator to see if the given module is supported + let allocator = engine.config().instance_allocator(); + #[cfg(feature = "cache")] - let (main_module, artifacts, types) = - ModuleCacheEntry::new("wasmtime", engine.cache_config()) - .get_data((engine.compiler(), binary), |(compiler, binary)| { - CompilationArtifacts::build(compiler, binary) - })?; + let (main_module, artifacts, types) = ModuleCacheEntry::new( + "wasmtime", + engine.cache_config(), + ) + .get_data((engine.compiler(), binary), |(compiler, binary)| { + CompilationArtifacts::build(compiler, binary, |m| allocator.validate_module(m)) + })?; #[cfg(not(feature = "cache"))] let (main_module, artifacts, types) = - CompilationArtifacts::build(engine.compiler(), binary)?; + CompilationArtifacts::build(engine.compiler(), binary, |m| { + allocator.validate_module(m) + })?; let mut modules = CompiledModule::from_artifacts_list( artifacts,