Skip to content

Commit

Permalink
Add an instance limit to Config (#2593)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
alexcrichton and peterhuene authored Jan 21, 2021
1 parent 2d5037d commit 8748cf5
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 1 deletion.
8 changes: 8 additions & 0 deletions crates/c-api/include/wasmtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
* path.
Expand Down
5 changes: 5 additions & 0 deletions crates/c-api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
11 changes: 11 additions & 0 deletions crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -79,6 +80,7 @@ impl Config {
multi_value: true,
..WasmFeatures::default()
},
max_instances: 10_000,
};
ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
return ret;
Expand Down Expand Up @@ -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<dyn TargetIsa> {
self.isa_flags
.clone()
Expand Down
2 changes: 2 additions & 0 deletions crates/wasmtime/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ fn instantiate(
&mut ImportsBuilder<'_>,
) -> Result<()>,
) -> Result<RuntimeInstance, Error> {
store.bump_instance_count()?;

let compiled_module = module.compiled_module();
let env_module = compiled_module.module();

Expand Down
14 changes: 13 additions & 1 deletion crates/wasmtime/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<HashSet<ArcModuleCode>>,
/// The number of instantiated instances in this store.
instance_count: Cell<usize>,
}

struct HostInfoKey(VMExternRef);
Expand Down Expand Up @@ -109,6 +111,7 @@ impl Store {
stack_map_registry: StackMapRegistry::default(),
frame_info: Default::default(),
modules: Default::default(),
instance_count: Default::default(),
}),
}
}
Expand Down Expand Up @@ -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 {
Expand Down
36 changes: 36 additions & 0 deletions tests/all/module_linking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

0 comments on commit 8748cf5

Please sign in to comment.