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:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -116,3 +116,37 @@ impl<'a> Arbitrary<'a> for SpecTest {
|
||||
(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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
<Module as Arbitrary>::size_hint(depth),
|
||||
<super::GeneratedModule as Arbitrary>::size_hint(depth),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -39,6 +39,8 @@ pub fn fuzz_default_config(strategy: wasmtime::Strategy) -> anyhow::Result<wasmt
|
||||
.wasm_bulk_memory(true)
|
||||
.wasm_reference_types(true)
|
||||
.wasm_module_linking(true)
|
||||
.wasm_multi_memory(true)
|
||||
.wasm_simd(true)
|
||||
.strategy(strategy)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// we call the exported functions for all of our different configs.
|
||||
pub fn differential_execution(
|
||||
module: &wasm_smith::Module,
|
||||
module: &crate::generators::GeneratedModule,
|
||||
configs: &[crate::generators::DifferentialConfig],
|
||||
) {
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -271,7 +271,7 @@ pub fn differential_execution(
|
||||
|
||||
for mut config in configs {
|
||||
// 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
|
||||
// to accept modules that would otherwise be broken by module linking.
|
||||
config.wasm_module_linking(false);
|
||||
@@ -631,7 +631,7 @@ impl wasm_smith::Config for DifferentialWasmiModuleConfig {
|
||||
2
|
||||
}
|
||||
|
||||
fn max_memory_pages(&self) -> u32 {
|
||||
fn max_memory_pages(&self, _is_64: bool) -> u64 {
|
||||
1
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ pub fn dummy_extern<T>(store: &mut Store<T>, ty: ExternType) -> Result<Extern> {
|
||||
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<T>(store: &mut Store<T>, ty: GlobalType) -> Global {
|
||||
}
|
||||
|
||||
/// 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());
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<SwarmConfig>)| {
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user