diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index d31eba04cb..a35bd186a9 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -75,7 +75,7 @@ pub struct Config { impl Config { /// Converts this to a `wasmtime::Config` object pub fn to_wasmtime(&self) -> wasmtime::Config { - let mut cfg = wasmtime::Config::new(); + let mut cfg = crate::fuzz_default_config(wasmtime::Strategy::Auto).unwrap(); cfg.debug_info(self.debug_info) .static_memory_maximum_size(self.static_memory_maximum_size.unwrap_or(0).into()) .static_memory_guard_size(self.static_memory_guard_size.unwrap_or(0).into()) diff --git a/crates/fuzzing/src/lib.rs b/crates/fuzzing/src/lib.rs index 7ef3382411..d131149ddb 100644 --- a/crates/fuzzing/src/lib.rs +++ b/crates/fuzzing/src/lib.rs @@ -39,6 +39,9 @@ pub fn fuzz_default_config(strategy: wasmtime::Strategy) -> anyhow::Result {} // Allow traps which can happen normally with `unreachable` Err(e) if e.downcast_ref::().is_some() => {} + // Allow resource exhaustion since this is something that our wasm-smith + // generator doesn't guarantee is forbidden. + Err(e) if e.to_string().contains("resource limit exceeded") => {} Err(e) => panic!("failed to instantiate {}", e), } } diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 80b10548ed..3edbfebb2b 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -34,6 +34,8 @@ pub struct Config { pub(crate) features: WasmFeatures, pub(crate) wasm_backtrace_details_env_used: bool, pub(crate) max_instances: usize, + pub(crate) max_tables: usize, + pub(crate) max_memories: usize, } impl Config { @@ -81,6 +83,8 @@ impl Config { ..WasmFeatures::default() }, max_instances: 10_000, + max_tables: 10_000, + max_memories: 10_000, }; ret.wasm_backtrace_details(WasmBacktraceDetails::Environment); return ret; @@ -655,11 +659,35 @@ impl Config { /// this `Store`. /// /// Instantiation will fail with an error if this limit is exceeded. + /// + /// This value defaults to 10,000. pub fn max_instances(&mut self, instances: usize) -> &mut Self { self.max_instances = instances; self } + /// Configures the maximum number of tables which can be created within + /// this `Store`. + /// + /// Instantiation will fail with an error if this limit is exceeded. + /// + /// This value defaults to 10,000. + pub fn max_tables(&mut self, tables: usize) -> &mut Self { + self.max_tables = tables; + self + } + + /// Configures the maximum number of memories which can be created within + /// this `Store`. + /// + /// Instantiation will fail with an error if this limit is exceeded. + /// + /// This value defaults to 10,000. + pub fn max_memories(&mut self, memories: usize) -> &mut Self { + self.max_memories = memories; + self + } + pub(crate) fn target_isa(&self) -> Box { self.isa_flags .clone() diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index 02ad1a1b98..47115d90ae 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -257,7 +257,7 @@ impl<'a> Instantiator<'a> { /// defined here. fn step(&mut self) -> Result)>> { if self.cur.initializer == 0 { - self.store.bump_instance_count()?; + self.store.bump_resource_counts(&self.cur.module)?; } // Read the current module's initializer and move forward the diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index acd229eb1f..cc86011fb2 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -67,8 +67,11 @@ pub(crate) struct StoreInner { /// Set of all compiled modules that we're holding a strong reference to /// the module's code for. This includes JIT functions, trampolines, etc. modules: RefCell>, - /// The number of instantiated instances in this store. + + // Numbers of resources instantiated in this store. instance_count: Cell, + memory_count: Cell, + table_count: Cell, } struct HostInfoKey(VMExternRef); @@ -112,6 +115,8 @@ impl Store { frame_info: Default::default(), modules: Default::default(), instance_count: Default::default(), + memory_count: Default::default(), + table_count: Default::default(), }), } } @@ -217,12 +222,40 @@ impl Store { } } - pub(crate) fn bump_instance_count(&self) -> Result<()> { - let n = self.inner.instance_count.get(); - self.inner.instance_count.set(n + 1); - if n >= self.engine().config().max_instances { - bail!("instance limit of {} exceeded", n); + pub(crate) fn bump_resource_counts(&self, module: &Module) -> Result<()> { + let config = self.engine().config(); + + fn bump(slot: &Cell, max: usize, amt: usize, desc: &str) -> Result<()> { + let new = slot.get().saturating_add(amt); + if new > max { + bail!( + "resource limit exceeded: {} count too high at {}", + desc, + new + ); + } + slot.set(new); + Ok(()) } + + let module = module.env_module(); + let memories = module.memory_plans.len() - module.num_imported_memories; + let tables = module.table_plans.len() - module.num_imported_tables; + + bump( + &self.inner.instance_count, + config.max_instances, + 1, + "instance", + )?; + bump( + &self.inner.memory_count, + config.max_memories, + memories, + "memory", + )?; + bump(&self.inner.table_count, config.max_tables, tables, "table")?; + Ok(()) } diff --git a/tests/all/module_linking.rs b/tests/all/module_linking.rs index b81a9deb33..829ddd9b8e 100644 --- a/tests/all/module_linking.rs +++ b/tests/all/module_linking.rs @@ -218,6 +218,81 @@ fn limit_instances() -> Result<()> { )?; let store = Store::new(&engine); let err = Instance::new(&store, &module, &[]).err().unwrap(); - assert!(err.to_string().contains("instance limit of 10 exceeded")); + assert!( + err.to_string().contains("resource limit exceeded"), + "bad error: {}", + err + ); + Ok(()) +} + +#[test] +fn limit_memories() -> Result<()> { + let mut config = Config::new(); + config.wasm_module_linking(true); + config.wasm_multi_memory(true); + config.max_memories(10); + let engine = Engine::new(&config); + let module = Module::new( + &engine, + r#" + (module + (module $m0 + (memory 1 1) + (memory 1 1) + (memory 1 1) + (memory 1 1) + (memory 1 1) + ) + + (instance (instantiate $m0)) + (instance (instantiate $m0)) + (instance (instantiate $m0)) + (instance (instantiate $m0)) + ) + "#, + )?; + let store = Store::new(&engine); + let err = Instance::new(&store, &module, &[]).err().unwrap(); + assert!( + err.to_string().contains("resource limit exceeded"), + "bad error: {}", + err + ); + Ok(()) +} + +#[test] +fn limit_tables() -> Result<()> { + let mut config = Config::new(); + config.wasm_module_linking(true); + config.max_tables(10); + let engine = Engine::new(&config); + let module = Module::new( + &engine, + r#" + (module + (module $m0 + (table 1 1 funcref) + (table 1 1 funcref) + (table 1 1 funcref) + (table 1 1 funcref) + (table 1 1 funcref) + ) + + (instance (instantiate $m0)) + (instance (instantiate $m0)) + (instance (instantiate $m0)) + (instance (instantiate $m0)) + ) + "#, + )?; + let store = Store::new(&engine); + let err = Instance::new(&store, &module, &[]).err().unwrap(); + assert!( + err.to_string().contains("resource limit exceeded"), + "bad error: {}", + err + ); Ok(()) }