Add an instance limit to Config (#2593)
* 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. * Update crates/c-api/include/wasmtime.h Co-authored-by: Peter Huene <peter@huene.dev> Co-authored-by: Peter Huene <peter@huene.dev>
This commit is contained in:
@@ -274,6 +274,14 @@ WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t)
|
|||||||
*/
|
*/
|
||||||
WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t)
|
WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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.
|
||||||
|
*/
|
||||||
|
WASMTIME_CONFIG_PROP(void, max_instances, size_t)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Enables Wasmtime's cache and loads configuration from the specified
|
* \brief Enables Wasmtime's cache and loads configuration from the specified
|
||||||
* path.
|
* path.
|
||||||
|
|||||||
@@ -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) {
|
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);
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ pub struct Config {
|
|||||||
pub(crate) max_wasm_stack: usize,
|
pub(crate) max_wasm_stack: usize,
|
||||||
pub(crate) features: WasmFeatures,
|
pub(crate) features: WasmFeatures,
|
||||||
pub(crate) wasm_backtrace_details_env_used: bool,
|
pub(crate) wasm_backtrace_details_env_used: bool,
|
||||||
|
pub(crate) max_instances: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -79,6 +80,7 @@ impl Config {
|
|||||||
multi_value: true,
|
multi_value: true,
|
||||||
..WasmFeatures::default()
|
..WasmFeatures::default()
|
||||||
},
|
},
|
||||||
|
max_instances: 10_000,
|
||||||
};
|
};
|
||||||
ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
|
ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -635,6 +637,15 @@ impl Config {
|
|||||||
self
|
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<dyn TargetIsa> {
|
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
|
||||||
self.isa_flags
|
self.isa_flags
|
||||||
.clone()
|
.clone()
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ fn instantiate(
|
|||||||
&mut ImportsBuilder<'_>,
|
&mut ImportsBuilder<'_>,
|
||||||
) -> Result<()>,
|
) -> Result<()>,
|
||||||
) -> Result<RuntimeInstance, Error> {
|
) -> Result<RuntimeInstance, Error> {
|
||||||
|
store.bump_instance_count()?;
|
||||||
|
|
||||||
let compiled_module = module.compiled_module();
|
let compiled_module = module.compiled_module();
|
||||||
let env_module = compiled_module.module();
|
let env_module = compiled_module.module();
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::trampoline::StoreInstanceHandle;
|
|||||||
use crate::{Engine, Module};
|
use crate::{Engine, Module};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
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
|
/// Set of all compiled modules that we're holding a strong reference to
|
||||||
/// the module's code for. This includes JIT functions, trampolines, etc.
|
/// the module's code for. This includes JIT functions, trampolines, etc.
|
||||||
modules: RefCell<HashSet<ArcModuleCode>>,
|
modules: RefCell<HashSet<ArcModuleCode>>,
|
||||||
|
/// The number of instantiated instances in this store.
|
||||||
|
instance_count: Cell<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HostInfoKey(VMExternRef);
|
struct HostInfoKey(VMExternRef);
|
||||||
@@ -109,6 +111,7 @@ impl Store {
|
|||||||
stack_map_registry: StackMapRegistry::default(),
|
stack_map_registry: StackMapRegistry::default(),
|
||||||
frame_info: Default::default(),
|
frame_info: Default::default(),
|
||||||
modules: 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 {
|
pub(crate) unsafe fn add_instance(&self, handle: InstanceHandle) -> StoreInstanceHandle {
|
||||||
self.inner.instances.borrow_mut().push(handle.clone());
|
self.inner.instances.borrow_mut().push(handle.clone());
|
||||||
StoreInstanceHandle {
|
StoreInstanceHandle {
|
||||||
|
|||||||
@@ -185,3 +185,39 @@ fn imports_exports() -> Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user