Merge pull request #2617 from alexcrichton/limit-tables-and-memeories
Add knobs to limit memories/tables in a `Store`
This commit is contained in:
@@ -75,7 +75,7 @@ pub struct Config {
|
|||||||
impl Config {
|
impl Config {
|
||||||
/// Converts this to a `wasmtime::Config` object
|
/// Converts this to a `wasmtime::Config` object
|
||||||
pub fn to_wasmtime(&self) -> wasmtime::Config {
|
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)
|
cfg.debug_info(self.debug_info)
|
||||||
.static_memory_maximum_size(self.static_memory_maximum_size.unwrap_or(0).into())
|
.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())
|
.static_memory_guard_size(self.static_memory_guard_size.unwrap_or(0).into())
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ pub fn fuzz_default_config(strategy: wasmtime::Strategy) -> anyhow::Result<wasmt
|
|||||||
.wasm_bulk_memory(true)
|
.wasm_bulk_memory(true)
|
||||||
.wasm_reference_types(true)
|
.wasm_reference_types(true)
|
||||||
.wasm_module_linking(true)
|
.wasm_module_linking(true)
|
||||||
|
.max_instances(100)
|
||||||
|
.max_tables(100)
|
||||||
|
.max_memories(100)
|
||||||
.strategy(strategy)?;
|
.strategy(strategy)?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ pub fn instantiate_with_config(
|
|||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
// Allow traps which can happen normally with `unreachable`
|
// Allow traps which can happen normally with `unreachable`
|
||||||
Err(e) if e.downcast_ref::<Trap>().is_some() => {}
|
Err(e) if e.downcast_ref::<Trap>().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),
|
Err(e) => panic!("failed to instantiate {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ pub struct Config {
|
|||||||
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,
|
pub(crate) max_instances: usize,
|
||||||
|
pub(crate) max_tables: usize,
|
||||||
|
pub(crate) max_memories: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -81,6 +83,8 @@ impl Config {
|
|||||||
..WasmFeatures::default()
|
..WasmFeatures::default()
|
||||||
},
|
},
|
||||||
max_instances: 10_000,
|
max_instances: 10_000,
|
||||||
|
max_tables: 10_000,
|
||||||
|
max_memories: 10_000,
|
||||||
};
|
};
|
||||||
ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
|
ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -655,11 +659,35 @@ impl Config {
|
|||||||
/// this `Store`.
|
/// this `Store`.
|
||||||
///
|
///
|
||||||
/// Instantiation will fail with an error if this limit is exceeded.
|
/// 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 {
|
pub fn max_instances(&mut self, instances: usize) -> &mut Self {
|
||||||
self.max_instances = instances;
|
self.max_instances = instances;
|
||||||
self
|
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<dyn TargetIsa> {
|
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
|
||||||
self.isa_flags
|
self.isa_flags
|
||||||
.clone()
|
.clone()
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ impl<'a> Instantiator<'a> {
|
|||||||
/// defined here.
|
/// defined here.
|
||||||
fn step(&mut self) -> Result<Option<(StoreInstanceHandle, Option<RuntimeInstance>)>> {
|
fn step(&mut self) -> Result<Option<(StoreInstanceHandle, Option<RuntimeInstance>)>> {
|
||||||
if self.cur.initializer == 0 {
|
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
|
// Read the current module's initializer and move forward the
|
||||||
|
|||||||
@@ -67,8 +67,11 @@ 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.
|
|
||||||
|
// Numbers of resources instantiated in this store.
|
||||||
instance_count: Cell<usize>,
|
instance_count: Cell<usize>,
|
||||||
|
memory_count: Cell<usize>,
|
||||||
|
table_count: Cell<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HostInfoKey(VMExternRef);
|
struct HostInfoKey(VMExternRef);
|
||||||
@@ -112,6 +115,8 @@ impl Store {
|
|||||||
frame_info: Default::default(),
|
frame_info: Default::default(),
|
||||||
modules: Default::default(),
|
modules: Default::default(),
|
||||||
instance_count: 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<()> {
|
pub(crate) fn bump_resource_counts(&self, module: &Module) -> Result<()> {
|
||||||
let n = self.inner.instance_count.get();
|
let config = self.engine().config();
|
||||||
self.inner.instance_count.set(n + 1);
|
|
||||||
if n >= self.engine().config().max_instances {
|
fn bump(slot: &Cell<usize>, max: usize, amt: usize, desc: &str) -> Result<()> {
|
||||||
bail!("instance limit of {} exceeded", n);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -218,6 +218,81 @@ fn limit_instances() -> Result<()> {
|
|||||||
)?;
|
)?;
|
||||||
let store = Store::new(&engine);
|
let store = Store::new(&engine);
|
||||||
let err = Instance::new(&store, &module, &[]).err().unwrap();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user