diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index f2c674edff..efb90a8486 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -45,18 +45,53 @@ fn log_wasm(wasm: &[u8]) { fn create_store(engine: &Engine) -> Store { let mut store = Store::new( &engine, - StoreLimitsBuilder::new() - // The limits here are chosen based on the default "maximum type size" - // configured in wasm-smith, which is 1000. This means that instances - // are allowed to, for example, export up to 1000 memories. We bump that - // a little bit here to give us some slop. - .instances(1100) - .tables(1100) - .memories(1100) - .build(), + StoreLimits { + // Limits tables/memories within a store to at most 2gb for now to + // exercise some larger address but not overflow various limits. + remaining_memory: 2 << 30, + oom: false, + }, ); store.limiter(|s| s as &mut dyn ResourceLimiter); - store + return store; +} + +struct StoreLimits { + /// Remaining memory, in bytes, left to allocate + remaining_memory: usize, + /// Whether or not an allocation request has been denied + oom: bool, +} + +impl StoreLimits { + fn alloc(&mut self, amt: usize) -> bool { + match self.remaining_memory.checked_sub(amt) { + Some(mem) => { + self.remaining_memory = mem; + true + } + None => { + self.oom = true; + false + } + } + } +} + +impl ResourceLimiter for StoreLimits { + fn memory_growing(&mut self, current: u32, desired: u32, _maximum: Option) -> bool { + // Units provided are in wasm pages, so adjust them to bytes to see if + // we are ok to allocate this much. + self.alloc((desired - current) as usize * 16 * 1024) + } + + fn table_growing(&mut self, current: u32, desired: u32, _maximum: Option) -> bool { + // Units provided are in table elements, and for now we allocate one + // pointer per table element, so use that size for an adjustment into + // bytes. + let delta = (desired - current) as usize * std::mem::size_of::(); + self.alloc(delta) + } } /// Methods of timing out execution of a WebAssembly module @@ -159,22 +194,26 @@ pub fn instantiate_with_config( match linker.instantiate(&mut store, &module) { Ok(_) => {} Err(e) => { - let string = e.to_string(); + // If the instantiation hit OOM for some reason then that's ok, it's + // expected that fuzz-generated programs try to allocate lots of + // stuff. + if store.data().oom { + return; + } + // Allow traps which can happen normally with `unreachable` or a - // timeout - if e.downcast_ref::().is_some() - // Allow resource exhaustion since this is something that - // our wasm-smith generator doesn't guarantee is forbidden. - || string.contains("resource limit exceeded") - // Also allow errors related to fuel consumption - || string.contains("all fuel consumed") + // timeout or such + if e.downcast_ref::().is_some() { + return; + } + + let string = e.to_string(); + // Also allow errors related to fuel consumption + if string.contains("all fuel consumed") // Currently we instantiate with a `Linker` which can't instantiate // every single module under the sun due to using name-based resolution // rather than positional-based resolution || string.contains("incompatible import type") - // If we ran out of resources instantiating this wasm module that's - // ok, no need to consider that a fatal error. - || string.contains("Insufficient resources") { return; } diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs index 24ee1e406e..068aa65c46 100644 --- a/crates/fuzzing/src/oracles/dummy.rs +++ b/crates/fuzzing/src/oracles/dummy.rs @@ -41,7 +41,7 @@ pub fn dummy_extern(store: &mut Store, ty: ExternType) -> Result { ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_ty)), ExternType::Table(table_ty) => Extern::Table(dummy_table(store, table_ty)), ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(store, mem_ty)?), - ExternType::Instance(instance_ty) => Extern::Instance(dummy_instance(store, instance_ty)), + ExternType::Instance(instance_ty) => Extern::Instance(dummy_instance(store, instance_ty)?), ExternType::Module(module_ty) => Extern::Module(dummy_module(store.engine(), module_ty)), }) } @@ -95,13 +95,13 @@ pub fn dummy_memory(store: &mut Store, ty: MemoryType) -> Result { /// /// This is done by using the expected type to generate a module on-the-fly /// which we the instantiate. -pub fn dummy_instance(store: &mut Store, ty: InstanceType) -> Instance { +pub fn dummy_instance(store: &mut Store, ty: InstanceType) -> Result { let mut wat = WatGenerator::new(); for ty in ty.exports() { wat.export(&ty); } let module = Module::new(store.engine(), &wat.finish()).unwrap(); - Instance::new(store, &module, &[]).unwrap() + Instance::new(store, &module, &[]) } /// Construct a dummy module for the given module type. @@ -469,7 +469,7 @@ mod tests { instance_ty.add_named_export("instance0", InstanceType::new().into()); instance_ty.add_named_export("instance1", InstanceType::new().into()); - let instance = dummy_instance(&mut store, instance_ty.clone()); + let instance = dummy_instance(&mut store, instance_ty.clone()).unwrap(); let mut expected_exports = vec![ "func0",