From bace89cd6bfb36e95e38e7b92605b20574c76453 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 24 Dec 2021 16:39:58 -0800 Subject: [PATCH] WIP: roundtrip straightline differential fuzzing --- fuzz/Cargo.toml | 8 + .../fuzz_targets/straightline_differential.rs | 139 ++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 fuzz/fuzz_targets/straightline_differential.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 81ef681..b117d7d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -14,6 +14,8 @@ libfuzzer-sys = "0.4" wasm-smith = "0.8" env_logger = "0.9" log = "0.4" +wasmi = "0.10" +wasmparser = "0.81" [dependencies.waffle] path = ".." @@ -39,3 +41,9 @@ name = "roundtrip_roundtrip" path = "fuzz_targets/roundtrip_roundtrip.rs" test = false doc = false + +[[bin]] +name = "straightline_differential" +path = "fuzz_targets/straightline_differential.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/straightline_differential.rs b/fuzz/fuzz_targets/straightline_differential.rs new file mode 100644 index 0000000..b8478af --- /dev/null +++ b/fuzz/fuzz_targets/straightline_differential.rs @@ -0,0 +1,139 @@ +#![no_main] +use libfuzzer_sys::{arbitrary, fuzz_target}; + +use waffle::Module; + +fn has_loop(bytes: &[u8]) -> bool { + let parser = wasmparser::Parser::new(0); + for payload in parser.parse_all(bytes) { + match payload.unwrap() { + wasmparser::Payload::CodeSectionEntry(body) => { + for op in body.get_operators_reader().unwrap() { + let op = op.unwrap(); + match op { + wasmparser::Operator::Loop { .. } => { + return true; + } + _ => {} + } + } + } + _ => {} + } + } + false +} + +#[derive(Debug)] +struct Config; + +impl<'a> arbitrary::Arbitrary<'a> for Config { + fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + Ok(Config) + } +} + +impl wasm_smith::Config for Config { + fn min_funcs(&self) -> usize { + 1 + } + fn max_funcs(&self) -> usize { + 1 + } + fn min_memories(&self) -> u32 { + 1 + } + fn max_memories(&self) -> usize { + 1 + } + fn min_globals(&self) -> usize { + 10 + } + fn max_globals(&self) -> usize { + 10 + } + fn min_tables(&self) -> u32 { + 0 + } + fn max_tables(&self) -> usize { + 0 + } + fn min_imports(&self) -> usize { + 0 + } + fn max_imports(&self) -> usize { + 0 + } + fn min_exports(&self) -> usize { + 12 + } + fn max_exports(&self) -> usize { + 12 + } + fn allow_start_export(&self) -> bool { + true + } + fn canonicalize_nans(&self) -> bool { + true + } +} + +fuzz_target!(|module: wasm_smith::ConfiguredModule| { + let _ = env_logger::try_init(); + log::debug!("original module: {:?}", module.module); + + let orig_bytes = module.module.to_bytes(); + + if has_loop(&orig_bytes[..]) { + log::debug!("has a loop; discarding fuzz run"); + return; + } + + let orig_wasmi_module = + wasmi::Module::from_buffer(&orig_bytes[..]).expect("failed to parse original wasm"); + let orig_instance = + wasmi::ModuleInstance::new(&orig_wasmi_module, &wasmi::ImportsBuilder::default()) + .expect("cannot instantiate original wasm") + .run_start(&mut wasmi::NopExternals); + let orig_instance = match orig_instance { + Ok(orig_instance) => orig_instance, + Err(e) => { + log::debug!("cannot run start on orig intsance ({:?}); discarding", e); + return; + } + }; + + let parsed_module = Module::from_wasm_bytes(&orig_bytes[..]).unwrap(); + let roundtrip_bytes = parsed_module.to_wasm_bytes(); + + let roundtrip_wasmi_module = + wasmi::Module::from_buffer(&roundtrip_bytes).expect("failed to parse roundtripped wasm"); + let roundtrip_instance = + wasmi::ModuleInstance::new(&roundtrip_wasmi_module, &wasmi::ImportsBuilder::default()) + .expect("cannot instantiate roundtripped wasm") + .run_start(&mut wasmi::NopExternals) + .expect("cannot run start on original wasm"); + + // Ensure globals are equal. + assert_eq!( + orig_instance.globals().len(), + roundtrip_instance.globals().len() + ); + for (a, b) in orig_instance + .globals() + .iter() + .zip(roundtrip_instance.globals().iter()) + { + match (a.get(), b.get()) { + (wasmi::RuntimeValue::I32(a), wasmi::RuntimeValue::I32(b)) => assert_eq!(a, b), + (wasmi::RuntimeValue::I64(a), wasmi::RuntimeValue::I64(b)) => assert_eq!(a, b), + (wasmi::RuntimeValue::F32(a), wasmi::RuntimeValue::F32(b)) => { + assert_eq!(a.to_bits(), b.to_bits()) + } + (wasmi::RuntimeValue::F64(a), wasmi::RuntimeValue::F64(b)) => { + assert_eq!(a.to_bits(), b.to_bits()) + } + _ => panic!("mismatched types"), + } + } +});