differential fuzzer
This commit is contained in:
parent
bace89cd6b
commit
3f8ead1485
|
@ -14,8 +14,8 @@ libfuzzer-sys = "0.4"
|
||||||
wasm-smith = "0.8"
|
wasm-smith = "0.8"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
wasmi = "0.10"
|
|
||||||
wasmparser = "0.81"
|
wasmparser = "0.81"
|
||||||
|
wasmtime = "0.32"
|
||||||
|
|
||||||
[dependencies.waffle]
|
[dependencies.waffle]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
|
|
@ -3,8 +3,9 @@ use libfuzzer_sys::{arbitrary, fuzz_target};
|
||||||
|
|
||||||
use waffle::Module;
|
use waffle::Module;
|
||||||
|
|
||||||
fn has_loop(bytes: &[u8]) -> bool {
|
fn has_loop_or_no_start(bytes: &[u8]) -> bool {
|
||||||
let parser = wasmparser::Parser::new(0);
|
let parser = wasmparser::Parser::new(0);
|
||||||
|
let mut has_start = false;
|
||||||
for payload in parser.parse_all(bytes) {
|
for payload in parser.parse_all(bytes) {
|
||||||
match payload.unwrap() {
|
match payload.unwrap() {
|
||||||
wasmparser::Payload::CodeSectionEntry(body) => {
|
wasmparser::Payload::CodeSectionEntry(body) => {
|
||||||
|
@ -12,16 +13,25 @@ fn has_loop(bytes: &[u8]) -> bool {
|
||||||
let op = op.unwrap();
|
let op = op.unwrap();
|
||||||
match op {
|
match op {
|
||||||
wasmparser::Operator::Loop { .. } => {
|
wasmparser::Operator::Loop { .. } => {
|
||||||
|
// Disallow direct loops.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
wasmparser::Operator::Call { .. }
|
||||||
|
| wasmparser::Operator::CallIndirect { .. } => {
|
||||||
|
// Disallow recursion.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wasmparser::Payload::StartSection { .. } => {
|
||||||
|
has_start = true;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
!has_start
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -76,6 +86,9 @@ impl wasm_smith::Config for Config {
|
||||||
fn canonicalize_nans(&self) -> bool {
|
fn canonicalize_nans(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
fn max_memory_pages(&self, _is_64: bool) -> u64 {
|
||||||
|
1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fuzz_target!(|module: wasm_smith::ConfiguredModule<Config>| {
|
fuzz_target!(|module: wasm_smith::ConfiguredModule<Config>| {
|
||||||
|
@ -84,17 +97,19 @@ fuzz_target!(|module: wasm_smith::ConfiguredModule<Config>| {
|
||||||
|
|
||||||
let orig_bytes = module.module.to_bytes();
|
let orig_bytes = module.module.to_bytes();
|
||||||
|
|
||||||
if has_loop(&orig_bytes[..]) {
|
if has_loop_or_no_start(&orig_bytes[..]) {
|
||||||
log::debug!("has a loop; discarding fuzz run");
|
log::debug!(
|
||||||
|
"has a loop or no start; discarding fuzz run. Body:\n{:?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_wasmi_module =
|
let engine = wasmtime::Engine::default();
|
||||||
wasmi::Module::from_buffer(&orig_bytes[..]).expect("failed to parse original wasm");
|
let mut store = wasmtime::Store::new(&engine, ());
|
||||||
let orig_instance =
|
let orig_module =
|
||||||
wasmi::ModuleInstance::new(&orig_wasmi_module, &wasmi::ImportsBuilder::default())
|
wasmtime::Module::new(&engine, &orig_bytes[..]).expect("failed to parse original wasm");
|
||||||
.expect("cannot instantiate original wasm")
|
let orig_instance = wasmtime::Instance::new(&mut store, &orig_module, &[]);
|
||||||
.run_start(&mut wasmi::NopExternals);
|
|
||||||
let orig_instance = match orig_instance {
|
let orig_instance = match orig_instance {
|
||||||
Ok(orig_instance) => orig_instance,
|
Ok(orig_instance) => orig_instance,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -106,34 +121,48 @@ fuzz_target!(|module: wasm_smith::ConfiguredModule<Config>| {
|
||||||
let parsed_module = Module::from_wasm_bytes(&orig_bytes[..]).unwrap();
|
let parsed_module = Module::from_wasm_bytes(&orig_bytes[..]).unwrap();
|
||||||
let roundtrip_bytes = parsed_module.to_wasm_bytes();
|
let roundtrip_bytes = parsed_module.to_wasm_bytes();
|
||||||
|
|
||||||
let roundtrip_wasmi_module =
|
let roundtrip_module = wasmtime::Module::new(&engine, &roundtrip_bytes[..])
|
||||||
wasmi::Module::from_buffer(&roundtrip_bytes).expect("failed to parse roundtripped wasm");
|
.expect("failed to parse roundtripped wasm");
|
||||||
let roundtrip_instance =
|
let roundtrip_instance = wasmtime::Instance::new(&mut store, &roundtrip_module, &[])
|
||||||
wasmi::ModuleInstance::new(&roundtrip_wasmi_module, &wasmi::ImportsBuilder::default())
|
.expect("cannot instantiate roundtripped wasm");
|
||||||
.expect("cannot instantiate roundtripped wasm")
|
|
||||||
.run_start(&mut wasmi::NopExternals)
|
|
||||||
.expect("cannot run start on original wasm");
|
|
||||||
|
|
||||||
// Ensure globals are equal.
|
// Ensure exports are equal.
|
||||||
assert_eq!(
|
|
||||||
orig_instance.globals().len(),
|
let a_globals: Vec<_> = orig_instance
|
||||||
roundtrip_instance.globals().len()
|
.exports(&mut store)
|
||||||
);
|
.filter_map(|e| e.into_global())
|
||||||
for (a, b) in orig_instance
|
.collect();
|
||||||
.globals()
|
let a_globals: Vec<wasmtime::Val> = a_globals.into_iter().map(|g| g.get(&mut store)).collect();
|
||||||
.iter()
|
let a_mems: Vec<wasmtime::Memory> = orig_instance
|
||||||
.zip(roundtrip_instance.globals().iter())
|
.exports(&mut store)
|
||||||
{
|
.filter_map(|e| e.into_memory())
|
||||||
match (a.get(), b.get()) {
|
.collect();
|
||||||
(wasmi::RuntimeValue::I32(a), wasmi::RuntimeValue::I32(b)) => assert_eq!(a, b),
|
|
||||||
(wasmi::RuntimeValue::I64(a), wasmi::RuntimeValue::I64(b)) => assert_eq!(a, b),
|
let b_globals: Vec<_> = roundtrip_instance
|
||||||
(wasmi::RuntimeValue::F32(a), wasmi::RuntimeValue::F32(b)) => {
|
.exports(&mut store)
|
||||||
assert_eq!(a.to_bits(), b.to_bits())
|
.filter_map(|e| e.into_global())
|
||||||
}
|
.collect();
|
||||||
(wasmi::RuntimeValue::F64(a), wasmi::RuntimeValue::F64(b)) => {
|
let b_globals: Vec<wasmtime::Val> = b_globals.into_iter().map(|g| g.get(&mut store)).collect();
|
||||||
assert_eq!(a.to_bits(), b.to_bits())
|
let b_mems: Vec<wasmtime::Memory> = roundtrip_instance
|
||||||
}
|
.exports(&mut store)
|
||||||
_ => panic!("mismatched types"),
|
.filter_map(|e| e.into_memory())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_eq!(a_globals.len(), b_globals.len());
|
||||||
|
for (a, b) in a_globals.into_iter().zip(b_globals.into_iter()) {
|
||||||
|
match (a, b) {
|
||||||
|
(wasmtime::Val::I32(a), wasmtime::Val::I32(b)) => assert_eq!(a, b),
|
||||||
|
(wasmtime::Val::I64(a), wasmtime::Val::I64(b)) => assert_eq!(a, b),
|
||||||
|
(wasmtime::Val::F32(a), wasmtime::Val::F32(b)) => assert_eq!(a, b),
|
||||||
|
(wasmtime::Val::F64(a), wasmtime::Val::F64(b)) => assert_eq!(a, b),
|
||||||
|
_ => panic!("mismatching types"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(a_mems.len(), b_mems.len());
|
||||||
|
for (a, b) in a_mems.into_iter().zip(b_mems.into_iter()) {
|
||||||
|
let a_data = a.data(&store);
|
||||||
|
let b_data = b.data(&store);
|
||||||
|
assert_eq!(a_data, b_data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue