From bb85366a3bfab793de960b5be2b069760ab8a6df Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Aug 2021 16:24:42 -0500 Subject: [PATCH] Enable simd fuzzing on oss-fuzz (#3152) * Enable simd fuzzing on oss-fuzz This commit generally enables the simd feature while fuzzing, which should affect almost all fuzzers. For fuzzers that just throw random data at the wall and see what sticks, this means that they'll now be able to throw simd-shaped data at the wall and have it stick. For wasm-smith-based fuzzers this commit also updates wasm-smith to 0.6.0 which allows further configuring the `SwarmConfig` after generation, notably allowing `instantiate-swarm` to generate modules using simd using `wasm-smith`. This should much more reliably feed simd-related things into the fuzzers. Finally, this commit updates wasmtime to avoid usage of the general `wasm_smith::Module` generator to instead use a Wasmtime-specific custom default configuration which enables various features we have implemented. * Allow dummy table creation to fail Tables might creation for imports may exceed the memory limit on the store, which we'll want to gracefully recover from and not fail the fuzzers. --- Cargo.lock | 8 ++-- crates/fuzzing/Cargo.toml | 4 +- crates/fuzzing/src/generators.rs | 34 ++++++++++++++ crates/fuzzing/src/generators/api.rs | 28 ++++++++---- crates/fuzzing/src/generators/table_ops.rs | 8 ++-- crates/fuzzing/src/lib.rs | 2 + crates/fuzzing/src/oracles.rs | 6 +-- crates/fuzzing/src/oracles/dummy.rs | 9 ++-- fuzz/Cargo.toml | 2 +- fuzz/fuzz_targets/differential.rs | 2 +- fuzz/fuzz_targets/instantiate-swarm.rs | 49 ++++++++++++++------- fuzz/fuzz_targets/instantiate-wasm-smith.rs | 5 +-- 12 files changed, 110 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f736c8c08..59bb30fc78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3446,18 +3446,18 @@ checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" [[package]] name = "wasm-encoder" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3fb3541f4cdc05f0eacd7ce5544d69a4cc3e922ef1e7f37c1e7cb8b1e8e66" +checksum = "2caacc74c68c74f0008c4055cdf509c43e623775eaf73323bb818dcf666ed9bd" dependencies = [ "leb128", ] [[package]] name = "wasm-smith" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b73bf3c616211529547284f12db7bb1e4d9bca11c738533490546239bbd120" +checksum = "f93b328ed4cef568449c185c89816ad587b172681af3b3d57f63178213ae36b4" dependencies = [ "arbitrary", "indexmap", diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 7e6e5a014c..beb35779b0 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -17,8 +17,8 @@ wasmparser = "0.80" wasmprinter = "0.2.28" wasmtime = { path = "../wasmtime" } wasmtime-wast = { path = "../wast" } -wasm-encoder = "0.5.0" -wasm-smith = "0.5.0" +wasm-encoder = "0.6.0" +wasm-smith = "0.6.0" wasmi = "0.7.0" [dev-dependencies] diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index 48fee8e6e9..bfbf914289 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -116,3 +116,37 @@ impl<'a> Arbitrary<'a> for SpecTest { (1, Some(std::mem::size_of::())) } } + +/// Type alias for wasm-smith generated modules using wasmtime's default +/// configuration. +pub type GeneratedModule = wasm_smith::ConfiguredModule; + +/// Wasmtime-specific default configuration for wasm-smith-generated modules. +#[derive(Arbitrary, Clone, Debug)] +pub struct WasmtimeDefaultConfig; + +impl wasm_smith::Config for WasmtimeDefaultConfig { + // Allow multi-memory to get exercised + fn max_memories(&self) -> usize { + 2 + } + + // Allow multi-table (reference types) to get exercised + fn max_tables(&self) -> usize { + 4 + } + + // Turn some wasm features default-on for those that have a finished + // implementation in Wasmtime. + fn simd_enabled(&self) -> bool { + true + } + + fn reference_types_enabled(&self) -> bool { + true + } + + fn bulk_memory_enabled(&self) -> bool { + true + } +} diff --git a/crates/fuzzing/src/generators/api.rs b/crates/fuzzing/src/generators/api.rs index 1f47765dff..3e3561ebbb 100644 --- a/crates/fuzzing/src/generators/api.rs +++ b/crates/fuzzing/src/generators/api.rs @@ -17,7 +17,6 @@ use arbitrary::{Arbitrary, Unstructured}; use std::collections::BTreeMap; use std::mem; -use wasm_smith::Module; use wasmparser::*; #[derive(Arbitrary, Debug)] @@ -40,11 +39,24 @@ pub enum ApiCall { ConfigInterruptable(bool), EngineNew, StoreNew, - ModuleNew { id: usize, wasm: Module }, - ModuleDrop { id: usize }, - InstanceNew { id: usize, module: usize }, - InstanceDrop { id: usize }, - CallExportedFunc { instance: usize, nth: usize }, + ModuleNew { + id: usize, + wasm: super::GeneratedModule, + }, + ModuleDrop { + id: usize, + }, + InstanceNew { + id: usize, + module: usize, + }, + InstanceDrop { + id: usize, + }, + CallExportedFunc { + instance: usize, + nth: usize, + }, } use ApiCall::*; @@ -107,7 +119,7 @@ impl<'a> Arbitrary<'a> for ApiCalls { if swarm.module_new { choices.push(|input, scope| { let id = scope.next_id(); - let mut wasm = Module::arbitrary(input)?; + let mut wasm = super::GeneratedModule::arbitrary(input)?; wasm.ensure_termination(1000); let predicted_rss = predict_rss(&wasm.to_bytes()).unwrap_or(0); scope.modules.insert(id, predicted_rss); @@ -175,7 +187,7 @@ impl<'a> Arbitrary<'a> for ApiCalls { // We can generate arbitrary `WasmOptTtf` instances, which have // no upper bound on the number of bytes they consume. This sets // the upper bound to `None`. - ::size_hint(depth), + ::size_hint(depth), ) }) } diff --git a/crates/fuzzing/src/generators/table_ops.rs b/crates/fuzzing/src/generators/table_ops.rs index adb2373fea..c3e36f5b03 100644 --- a/crates/fuzzing/src/generators/table_ops.rs +++ b/crates/fuzzing/src/generators/table_ops.rs @@ -4,7 +4,7 @@ use arbitrary::Arbitrary; use std::ops::Range; use wasm_encoder::{ CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, ImportSection, - Instruction, Limits, Module, TableSection, TableType, TypeSection, ValType, + Instruction, Module, TableSection, TableType, TypeSection, ValType, }; /// A description of a Wasm module that makes a series of `externref` table @@ -57,10 +57,8 @@ impl TableOps { let mut tables = TableSection::new(); tables.table(TableType { element_type: ValType::ExternRef, - limits: Limits { - min: self.table_size(), - max: None, - }, + minimum: self.table_size(), + maximum: None, }); // Encode the types for all functions that we are using. diff --git a/crates/fuzzing/src/lib.rs b/crates/fuzzing/src/lib.rs index 7ef3382411..3ad094a6ef 100644 --- a/crates/fuzzing/src/lib.rs +++ b/crates/fuzzing/src/lib.rs @@ -39,6 +39,8 @@ pub fn fuzz_default_config(strategy: wasmtime::Strategy) -> anyhow::Result u32 { + fn max_memory_pages(&self, _is_64: bool) -> u64 { 1 } diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs index 068aa65c46..fc426e7c6e 100644 --- a/crates/fuzzing/src/oracles/dummy.rs +++ b/crates/fuzzing/src/oracles/dummy.rs @@ -39,7 +39,7 @@ pub fn dummy_extern(store: &mut Store, ty: ExternType) -> Result { Ok(match ty { ExternType::Func(func_ty) => Extern::Func(dummy_func(store, func_ty)), ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_ty)), - ExternType::Table(table_ty) => Extern::Table(dummy_table(store, table_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::Module(module_ty) => Extern::Module(dummy_module(store.engine(), module_ty)), @@ -81,9 +81,9 @@ pub fn dummy_global(store: &mut Store, ty: GlobalType) -> Global { } /// Construct a dummy table for the given table type. -pub fn dummy_table(store: &mut Store, ty: TableType) -> Table { +pub fn dummy_table(store: &mut Store, ty: TableType) -> Result { let init_val = dummy_value(ty.element().clone()); - Table::new(store, ty, init_val).unwrap() + Table::new(store, ty, init_val) } /// Construct a dummy memory for the given memory type. @@ -393,7 +393,8 @@ mod tests { let table = dummy_table( &mut store, TableType::new(ValType::ExternRef, Limits::at_least(10)), - ); + ) + .unwrap(); assert_eq!(table.size(&store), 10); for i in 0..10 { assert!(table diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index c740016b13..20eb657c80 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -20,7 +20,7 @@ target-lexicon = "0.12" peepmatic-fuzzing = { path = "../cranelift/peepmatic/crates/fuzzing", optional = true } wasmtime = { path = "../crates/wasmtime" } wasmtime-fuzzing = { path = "../crates/fuzzing" } -wasm-smith = "0.5.0" +wasm-smith = "0.6.0" [features] # Leave a stub feature with no side-effects in place for now: the OSS-Fuzz diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs index 9b92d7998f..4301786992 100644 --- a/fuzz/fuzz_targets/differential.rs +++ b/fuzz/fuzz_targets/differential.rs @@ -6,7 +6,7 @@ use wasmtime_fuzzing::{generators, oracles}; fuzz_target!(|data: ( generators::DifferentialConfig, generators::DifferentialConfig, - wasm_smith::Module, + generators::GeneratedModule, )| { let (lhs, rhs, mut wasm) = data; wasm.ensure_termination(1000); diff --git a/fuzz/fuzz_targets/instantiate-swarm.rs b/fuzz/fuzz_targets/instantiate-swarm.rs index b534cc4970..35cab80dba 100644 --- a/fuzz/fuzz_targets/instantiate-swarm.rs +++ b/fuzz/fuzz_targets/instantiate-swarm.rs @@ -1,24 +1,41 @@ #![no_main] +use libfuzzer_sys::arbitrary::{Result, Unstructured}; use libfuzzer_sys::fuzz_target; use std::time::Duration; -use wasm_smith::{Config, ConfiguredModule, SwarmConfig}; +use wasm_smith::{ConfiguredModule, SwarmConfig}; use wasmtime::Strategy; use wasmtime_fuzzing::oracles::{self, Timeout}; -fuzz_target!(|pair: (bool, ConfiguredModule)| { - let (timeout_with_time, module) = pair; - let mut cfg = wasmtime_fuzzing::fuzz_default_config(Strategy::Auto).unwrap(); - cfg.wasm_multi_memory(true); - cfg.wasm_module_linking(module.config().module_linking_enabled()); - oracles::instantiate_with_config( - &module.to_bytes(), - true, - cfg, - if timeout_with_time { - Timeout::Time(Duration::from_secs(20)) - } else { - Timeout::Fuel(100_000) - }, - ); +fuzz_target!(|data: &[u8]| { + // errors in `run` have to do with not enough input in `data`, which we + // ignore here since it doesn't affect how we'd like to fuzz. + drop(run(data)); }); + +fn run(data: &[u8]) -> Result<()> { + let mut u = Unstructured::new(data); + let timeout = if u.arbitrary()? { + Timeout::Time(Duration::from_secs(20)) + } else { + Timeout::Fuel(100_000) + }; + + // Further configure `SwarmConfig` after we generate one to enable features + // that aren't otherwise enabled by default. We want to test all of these in + // Wasmtime. + let mut config: SwarmConfig = u.arbitrary()?; + config.simd_enabled = u.arbitrary()?; + config.module_linking_enabled = u.arbitrary()?; + // Don't generate modules that allocate more than 6GB + config.max_memory_pages = 6 << 30; + let module = ConfiguredModule::new(config.clone(), &mut u)?; + + let mut cfg = wasmtime_fuzzing::fuzz_default_config(Strategy::Auto).unwrap(); + cfg.wasm_multi_memory(config.max_memories > 1); + cfg.wasm_module_linking(config.module_linking_enabled); + cfg.wasm_simd(config.simd_enabled); + + oracles::instantiate_with_config(&module.to_bytes(), true, cfg, timeout); + Ok(()) +} diff --git a/fuzz/fuzz_targets/instantiate-wasm-smith.rs b/fuzz/fuzz_targets/instantiate-wasm-smith.rs index 546a198ff0..b21cda78f3 100644 --- a/fuzz/fuzz_targets/instantiate-wasm-smith.rs +++ b/fuzz/fuzz_targets/instantiate-wasm-smith.rs @@ -1,11 +1,10 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use wasm_smith::Module; use wasmtime::Strategy; -use wasmtime_fuzzing::oracles; +use wasmtime_fuzzing::{generators::GeneratedModule, oracles}; -fuzz_target!(|module: Module| { +fuzz_target!(|module: GeneratedModule| { let mut module = module; module.ensure_termination(1000); let wasm_bytes = module.to_bytes();