From f6f69c9abb7af33574bf9f7b53ad386d12ad76b2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 19 Jan 2021 11:42:44 -0800 Subject: [PATCH 1/2] Add an instance limit to `Config` This commit adds a new parameter to `Config` which limits the number of instances that can be created within a store connected to that `Config`. The intention here is to provide a default safeguard against module-linking modules that recursively create too many instances. --- crates/c-api/include/wasmtime.h | 8 ++++++++ crates/c-api/src/config.rs | 5 +++++ crates/wasmtime/src/config.rs | 11 ++++++++++ crates/wasmtime/src/instance.rs | 2 ++ crates/wasmtime/src/store.rs | 14 ++++++++++++- tests/all/module_linking.rs | 36 +++++++++++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 6752459a4d71..2dcbbf12488a 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -274,6 +274,14 @@ WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t) */ WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t) +/** + * \brief Configures the maximum number of instances that cana be created. + * + * For more information see the Rust documentation at + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.max_instances. + */ +WASMTIME_CONFIG_PROP(void, max_instances, size_t) + /** * \brief Enables Wasmtime's cache and loads configuration from the specified * path. diff --git a/crates/c-api/src/config.rs b/crates/c-api/src/config.rs index 08da598ec136..ae0cb6abdc36 100644 --- a/crates/c-api/src/config.rs +++ b/crates/c-api/src/config.rs @@ -171,3 +171,8 @@ pub extern "C" fn wasmtime_config_static_memory_guard_size_set(c: &mut wasm_conf pub extern "C" fn wasmtime_config_dynamic_memory_guard_size_set(c: &mut wasm_config_t, size: u64) { c.config.dynamic_memory_guard_size(size); } + +#[no_mangle] +pub extern "C" fn wasmtime_config_max_instances(c: &mut wasm_config_t, limit: usize) { + c.config.max_instances(limit); +} diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 5a0686de9166..15b5ad58cb80 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -33,6 +33,7 @@ pub struct Config { pub(crate) max_wasm_stack: usize, pub(crate) features: WasmFeatures, pub(crate) wasm_backtrace_details_env_used: bool, + pub(crate) max_instances: usize, } impl Config { @@ -79,6 +80,7 @@ impl Config { multi_value: true, ..WasmFeatures::default() }, + max_instances: 10_000, }; ret.wasm_backtrace_details(WasmBacktraceDetails::Environment); return ret; @@ -635,6 +637,15 @@ impl Config { self } + /// Configures the maximum number of instances which can be created within + /// this `Store`. + /// + /// Instantiation will fail with an error if this limit is exceeded. + pub fn max_instances(&mut self, instances: usize) -> &mut Self { + self.max_instances = instances; + 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 60c52d6271cf..fed2bb29cec7 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -46,6 +46,8 @@ fn instantiate( &mut ImportsBuilder<'_>, ) -> Result<()>, ) -> Result { + store.bump_instance_count()?; + let compiled_module = module.compiled_module(); let env_module = compiled_module.module(); diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 9c622faadf56..be4aaf1563d8 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -4,7 +4,7 @@ use crate::trampoline::StoreInstanceHandle; use crate::{Engine, Module}; use anyhow::{bail, Result}; use std::any::Any; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::HashSet; use std::fmt; use std::hash::{Hash, Hasher}; @@ -67,6 +67,8 @@ 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. + instance_count: Cell, } struct HostInfoKey(VMExternRef); @@ -109,6 +111,7 @@ impl Store { stack_map_registry: StackMapRegistry::default(), frame_info: Default::default(), modules: Default::default(), + instance_count: Default::default(), }), } } @@ -213,6 +216,15 @@ 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); + } + Ok(()) + } + pub(crate) unsafe fn add_instance(&self, handle: InstanceHandle) -> StoreInstanceHandle { self.inner.instances.borrow_mut().push(handle.clone()); StoreInstanceHandle { diff --git a/tests/all/module_linking.rs b/tests/all/module_linking.rs index 10ccfc098d1e..b81a9deb335f 100644 --- a/tests/all/module_linking.rs +++ b/tests/all/module_linking.rs @@ -185,3 +185,39 @@ fn imports_exports() -> Result<()> { } Ok(()) } + +#[test] +fn limit_instances() -> Result<()> { + let mut config = Config::new(); + config.wasm_module_linking(true); + config.max_instances(10); + let engine = Engine::new(&config); + let module = Module::new( + &engine, + r#" + (module $PARENT + (module $m0) + (module $m1 + (instance (instantiate (module outer $PARENT $m0))) + (instance (instantiate (module outer $PARENT $m0)))) + (module $m2 + (instance (instantiate (module outer $PARENT $m1))) + (instance (instantiate (module outer $PARENT $m1)))) + (module $m3 + (instance (instantiate (module outer $PARENT $m2))) + (instance (instantiate (module outer $PARENT $m2)))) + (module $m4 + (instance (instantiate (module outer $PARENT $m3))) + (instance (instantiate (module outer $PARENT $m3)))) + (module $m5 + (instance (instantiate (module outer $PARENT $m4))) + (instance (instantiate (module outer $PARENT $m4)))) + (instance (instantiate $m5)) + ) + "#, + )?; + let store = Store::new(&engine); + let err = Instance::new(&store, &module, &[]).err().unwrap(); + assert!(err.to_string().contains("instance limit of 10 exceeded")); + Ok(()) +} From c503acc0d635ba1391eb4f7d7aa1ab474d472f31 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 21 Jan 2021 11:59:20 -0600 Subject: [PATCH 2/2] Update crates/c-api/include/wasmtime.h Co-authored-by: Peter Huene --- crates/c-api/include/wasmtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 2dcbbf12488a..948552b63396 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -275,7 +275,7 @@ WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t) WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t) /** - * \brief Configures the maximum number of instances that cana be created. + * \brief Configures the maximum number of instances that can be created. * * For more information see the Rust documentation at * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.max_instances.