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.
This commit is contained in:
Alex Crichton
2021-08-05 16:24:42 -05:00
committed by GitHub
parent 214c5f862d
commit bb85366a3b
12 changed files with 110 additions and 47 deletions

8
Cargo.lock generated
View File

@@ -3446,18 +3446,18 @@ checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f"
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.5.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3fb3541f4cdc05f0eacd7ce5544d69a4cc3e922ef1e7f37c1e7cb8b1e8e66" checksum = "2caacc74c68c74f0008c4055cdf509c43e623775eaf73323bb818dcf666ed9bd"
dependencies = [ dependencies = [
"leb128", "leb128",
] ]
[[package]] [[package]]
name = "wasm-smith" name = "wasm-smith"
version = "0.5.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0b73bf3c616211529547284f12db7bb1e4d9bca11c738533490546239bbd120" checksum = "f93b328ed4cef568449c185c89816ad587b172681af3b3d57f63178213ae36b4"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"indexmap", "indexmap",

View File

@@ -17,8 +17,8 @@ wasmparser = "0.80"
wasmprinter = "0.2.28" wasmprinter = "0.2.28"
wasmtime = { path = "../wasmtime" } wasmtime = { path = "../wasmtime" }
wasmtime-wast = { path = "../wast" } wasmtime-wast = { path = "../wast" }
wasm-encoder = "0.5.0" wasm-encoder = "0.6.0"
wasm-smith = "0.5.0" wasm-smith = "0.6.0"
wasmi = "0.7.0" wasmi = "0.7.0"
[dev-dependencies] [dev-dependencies]

View File

@@ -116,3 +116,37 @@ impl<'a> Arbitrary<'a> for SpecTest {
(1, Some(std::mem::size_of::<usize>())) (1, Some(std::mem::size_of::<usize>()))
} }
} }
/// Type alias for wasm-smith generated modules using wasmtime's default
/// configuration.
pub type GeneratedModule = wasm_smith::ConfiguredModule<WasmtimeDefaultConfig>;
/// 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
}
}

View File

@@ -17,7 +17,6 @@
use arbitrary::{Arbitrary, Unstructured}; use arbitrary::{Arbitrary, Unstructured};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::mem; use std::mem;
use wasm_smith::Module;
use wasmparser::*; use wasmparser::*;
#[derive(Arbitrary, Debug)] #[derive(Arbitrary, Debug)]
@@ -40,11 +39,24 @@ pub enum ApiCall {
ConfigInterruptable(bool), ConfigInterruptable(bool),
EngineNew, EngineNew,
StoreNew, StoreNew,
ModuleNew { id: usize, wasm: Module }, ModuleNew {
ModuleDrop { id: usize }, id: usize,
InstanceNew { id: usize, module: usize }, wasm: super::GeneratedModule,
InstanceDrop { id: usize }, },
CallExportedFunc { instance: usize, nth: usize }, ModuleDrop {
id: usize,
},
InstanceNew {
id: usize,
module: usize,
},
InstanceDrop {
id: usize,
},
CallExportedFunc {
instance: usize,
nth: usize,
},
} }
use ApiCall::*; use ApiCall::*;
@@ -107,7 +119,7 @@ impl<'a> Arbitrary<'a> for ApiCalls {
if swarm.module_new { if swarm.module_new {
choices.push(|input, scope| { choices.push(|input, scope| {
let id = scope.next_id(); let id = scope.next_id();
let mut wasm = Module::arbitrary(input)?; let mut wasm = super::GeneratedModule::arbitrary(input)?;
wasm.ensure_termination(1000); wasm.ensure_termination(1000);
let predicted_rss = predict_rss(&wasm.to_bytes()).unwrap_or(0); let predicted_rss = predict_rss(&wasm.to_bytes()).unwrap_or(0);
scope.modules.insert(id, predicted_rss); scope.modules.insert(id, predicted_rss);
@@ -175,7 +187,7 @@ impl<'a> Arbitrary<'a> for ApiCalls {
// We can generate arbitrary `WasmOptTtf` instances, which have // We can generate arbitrary `WasmOptTtf` instances, which have
// no upper bound on the number of bytes they consume. This sets // no upper bound on the number of bytes they consume. This sets
// the upper bound to `None`. // the upper bound to `None`.
<Module as Arbitrary>::size_hint(depth), <super::GeneratedModule as Arbitrary>::size_hint(depth),
) )
}) })
} }

View File

@@ -4,7 +4,7 @@ use arbitrary::Arbitrary;
use std::ops::Range; use std::ops::Range;
use wasm_encoder::{ use wasm_encoder::{
CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, ImportSection, 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 /// A description of a Wasm module that makes a series of `externref` table
@@ -57,10 +57,8 @@ impl TableOps {
let mut tables = TableSection::new(); let mut tables = TableSection::new();
tables.table(TableType { tables.table(TableType {
element_type: ValType::ExternRef, element_type: ValType::ExternRef,
limits: Limits { minimum: self.table_size(),
min: self.table_size(), maximum: None,
max: None,
},
}); });
// Encode the types for all functions that we are using. // Encode the types for all functions that we are using.

View File

@@ -39,6 +39,8 @@ 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)
.wasm_multi_memory(true)
.wasm_simd(true)
.strategy(strategy)?; .strategy(strategy)?;
Ok(config) Ok(config)
} }

View File

@@ -243,7 +243,7 @@ pub fn compile(wasm: &[u8], strategy: Strategy) {
/// or aren't enabled for different configs, we should get the same results when /// or aren't enabled for different configs, we should get the same results when
/// we call the exported functions for all of our different configs. /// we call the exported functions for all of our different configs.
pub fn differential_execution( pub fn differential_execution(
module: &wasm_smith::Module, module: &crate::generators::GeneratedModule,
configs: &[crate::generators::DifferentialConfig], configs: &[crate::generators::DifferentialConfig],
) { ) {
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@@ -271,7 +271,7 @@ pub fn differential_execution(
for mut config in configs { for mut config in configs {
// Disable module linking since it isn't enabled by default for // Disable module linking since it isn't enabled by default for
// `wasm_smith::Module` but is enabled by default for our fuzz config. // `GeneratedModule` but is enabled by default for our fuzz config.
// Since module linking is currently a breaking change this is required // Since module linking is currently a breaking change this is required
// to accept modules that would otherwise be broken by module linking. // to accept modules that would otherwise be broken by module linking.
config.wasm_module_linking(false); config.wasm_module_linking(false);
@@ -631,7 +631,7 @@ impl wasm_smith::Config for DifferentialWasmiModuleConfig {
2 2
} }
fn max_memory_pages(&self) -> u32 { fn max_memory_pages(&self, _is_64: bool) -> u64 {
1 1
} }

View File

@@ -39,7 +39,7 @@ pub fn dummy_extern<T>(store: &mut Store<T>, ty: ExternType) -> Result<Extern> {
Ok(match ty { Ok(match ty {
ExternType::Func(func_ty) => Extern::Func(dummy_func(store, func_ty)), ExternType::Func(func_ty) => Extern::Func(dummy_func(store, func_ty)),
ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_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::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)), ExternType::Module(module_ty) => Extern::Module(dummy_module(store.engine(), module_ty)),
@@ -81,9 +81,9 @@ pub fn dummy_global<T>(store: &mut Store<T>, ty: GlobalType) -> Global {
} }
/// Construct a dummy table for the given table type. /// Construct a dummy table for the given table type.
pub fn dummy_table<T>(store: &mut Store<T>, ty: TableType) -> Table { pub fn dummy_table<T>(store: &mut Store<T>, ty: TableType) -> Result<Table> {
let init_val = dummy_value(ty.element().clone()); 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. /// Construct a dummy memory for the given memory type.
@@ -393,7 +393,8 @@ mod tests {
let table = dummy_table( let table = dummy_table(
&mut store, &mut store,
TableType::new(ValType::ExternRef, Limits::at_least(10)), TableType::new(ValType::ExternRef, Limits::at_least(10)),
); )
.unwrap();
assert_eq!(table.size(&store), 10); assert_eq!(table.size(&store), 10);
for i in 0..10 { for i in 0..10 {
assert!(table assert!(table

View File

@@ -20,7 +20,7 @@ target-lexicon = "0.12"
peepmatic-fuzzing = { path = "../cranelift/peepmatic/crates/fuzzing", optional = true } peepmatic-fuzzing = { path = "../cranelift/peepmatic/crates/fuzzing", optional = true }
wasmtime = { path = "../crates/wasmtime" } wasmtime = { path = "../crates/wasmtime" }
wasmtime-fuzzing = { path = "../crates/fuzzing" } wasmtime-fuzzing = { path = "../crates/fuzzing" }
wasm-smith = "0.5.0" wasm-smith = "0.6.0"
[features] [features]
# Leave a stub feature with no side-effects in place for now: the OSS-Fuzz # Leave a stub feature with no side-effects in place for now: the OSS-Fuzz

View File

@@ -6,7 +6,7 @@ use wasmtime_fuzzing::{generators, oracles};
fuzz_target!(|data: ( fuzz_target!(|data: (
generators::DifferentialConfig, generators::DifferentialConfig,
generators::DifferentialConfig, generators::DifferentialConfig,
wasm_smith::Module, generators::GeneratedModule,
)| { )| {
let (lhs, rhs, mut wasm) = data; let (lhs, rhs, mut wasm) = data;
wasm.ensure_termination(1000); wasm.ensure_termination(1000);

View File

@@ -1,24 +1,41 @@
#![no_main] #![no_main]
use libfuzzer_sys::arbitrary::{Result, Unstructured};
use libfuzzer_sys::fuzz_target; use libfuzzer_sys::fuzz_target;
use std::time::Duration; use std::time::Duration;
use wasm_smith::{Config, ConfiguredModule, SwarmConfig}; use wasm_smith::{ConfiguredModule, SwarmConfig};
use wasmtime::Strategy; use wasmtime::Strategy;
use wasmtime_fuzzing::oracles::{self, Timeout}; use wasmtime_fuzzing::oracles::{self, Timeout};
fuzz_target!(|pair: (bool, ConfiguredModule<SwarmConfig>)| { fuzz_target!(|data: &[u8]| {
let (timeout_with_time, module) = pair; // errors in `run` have to do with not enough input in `data`, which we
let mut cfg = wasmtime_fuzzing::fuzz_default_config(Strategy::Auto).unwrap(); // ignore here since it doesn't affect how we'd like to fuzz.
cfg.wasm_multi_memory(true); drop(run(data));
cfg.wasm_module_linking(module.config().module_linking_enabled()); });
oracles::instantiate_with_config(
&module.to_bytes(), fn run(data: &[u8]) -> Result<()> {
true, let mut u = Unstructured::new(data);
cfg, let timeout = if u.arbitrary()? {
if timeout_with_time {
Timeout::Time(Duration::from_secs(20)) Timeout::Time(Duration::from_secs(20))
} else { } else {
Timeout::Fuel(100_000) 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(())
}

View File

@@ -1,11 +1,10 @@
#![no_main] #![no_main]
use libfuzzer_sys::fuzz_target; use libfuzzer_sys::fuzz_target;
use wasm_smith::Module;
use wasmtime::Strategy; 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; let mut module = module;
module.ensure_termination(1000); module.ensure_termination(1000);
let wasm_bytes = module.to_bytes(); let wasm_bytes = module.to_bytes();