Merge https://github.com/cfallin/waffle into pr/changes1
This commit is contained in:
commit
382f9808d8
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "waffle"
|
name = "waffle"
|
||||||
version = "0.0.25"
|
version = "0.0.27"
|
||||||
description = "Wasm Analysis Framework For Lightweight Experiments"
|
description = "Wasm Analysis Framework For Lightweight Experiments"
|
||||||
authors = ["Chris Fallin <chris@cfallin.org>"]
|
authors = ["Chris Fallin <chris@cfallin.org>"]
|
||||||
license = "Apache-2.0 WITH LLVM-exception"
|
license = "Apache-2.0 WITH LLVM-exception"
|
||||||
|
|
|
@ -11,11 +11,11 @@ cargo-fuzz = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libfuzzer-sys = "0.4"
|
libfuzzer-sys = "0.4"
|
||||||
wasm-smith = "0.8"
|
wasm-smith = "0.202.0"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
wasmparser = "0.95"
|
wasmparser = "0.202.0"
|
||||||
wasmtime = "7.0"
|
wasmtime = "19.0"
|
||||||
|
|
||||||
[dependencies.waffle]
|
[dependencies.waffle]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
|
|
@ -4,114 +4,110 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
use waffle::{FrontendOptions, Module};
|
use waffle::{FrontendOptions, Module};
|
||||||
|
|
||||||
fuzz_target!(
|
fuzz_target!(|module: waffle::fuzzing::ArbitraryModule| {
|
||||||
|module: wasm_smith::ConfiguredModule<waffle::fuzzing::Config>| {
|
let module = module.0;
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
log::debug!("original module: {:?}", module.module);
|
log::debug!("original module: {:?}", module);
|
||||||
|
|
||||||
let orig_bytes = module.module.to_bytes();
|
let orig_bytes = module.to_bytes();
|
||||||
|
|
||||||
if waffle::fuzzing::reject(&orig_bytes[..]) {
|
if waffle::fuzzing::reject(&orig_bytes[..]) {
|
||||||
log::debug!("Discarding fuzz run. Body:\n{:?}", module);
|
log::debug!("Discarding fuzz run. Body:\n{:?}", module);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
log::info!("body: {:?}", module);
|
log::info!("body: {:?}", module);
|
||||||
}
|
|
||||||
|
|
||||||
let mut config = wasmtime::Config::default();
|
|
||||||
config.consume_fuel(true);
|
|
||||||
let engine = wasmtime::Engine::new(&config).unwrap();
|
|
||||||
let orig_module =
|
|
||||||
wasmtime::Module::new(&engine, &orig_bytes[..]).expect("failed to parse original wasm");
|
|
||||||
let mut orig_store = wasmtime::Store::new(&engine, ());
|
|
||||||
orig_store.out_of_fuel_trap();
|
|
||||||
orig_store.add_fuel(10000).unwrap();
|
|
||||||
let orig_instance = wasmtime::Instance::new(&mut orig_store, &orig_module, &[]);
|
|
||||||
let orig_instance = match orig_instance {
|
|
||||||
Ok(orig_instance) => orig_instance,
|
|
||||||
Err(e) => {
|
|
||||||
log::info!("cannot run start on orig intsance ({:?}); discarding", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut parsed_module =
|
|
||||||
Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap();
|
|
||||||
parsed_module.expand_all_funcs().unwrap();
|
|
||||||
parsed_module.per_func_body(|body| body.optimize());
|
|
||||||
let roundtrip_bytes = parsed_module.to_wasm_bytes().unwrap();
|
|
||||||
|
|
||||||
if let Ok(filename) = std::env::var("FUZZ_DUMP_WASM") {
|
|
||||||
std::fs::write(format!("{}_orig.wasm", filename), &orig_bytes[..]).unwrap();
|
|
||||||
std::fs::write(format!("{}_roundtrip.wasm", filename), &roundtrip_bytes[..]).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let total = TOTAL.fetch_add(1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
let roundtrip_module = wasmtime::Module::new(&engine, &roundtrip_bytes[..])
|
|
||||||
.expect("failed to parse roundtripped wasm");
|
|
||||||
let mut roundtrip_store = wasmtime::Store::new(&engine, ());
|
|
||||||
roundtrip_store.out_of_fuel_trap();
|
|
||||||
// After roundtrip, fuel consumption rate may differ. That's fine;
|
|
||||||
// what matters is that it terminated above without a trap (hence
|
|
||||||
// halts in a reasonable time).
|
|
||||||
roundtrip_store.add_fuel(u64::MAX).unwrap();
|
|
||||||
let roundtrip_instance =
|
|
||||||
wasmtime::Instance::new(&mut roundtrip_store, &roundtrip_module, &[])
|
|
||||||
.expect("cannot instantiate roundtripped wasm");
|
|
||||||
|
|
||||||
// Ensure exports are equal.
|
|
||||||
|
|
||||||
let a_globals: Vec<_> = orig_instance
|
|
||||||
.exports(&mut orig_store)
|
|
||||||
.filter_map(|e| e.into_global())
|
|
||||||
.collect();
|
|
||||||
let a_globals: Vec<wasmtime::Val> = a_globals
|
|
||||||
.into_iter()
|
|
||||||
.map(|g| g.get(&mut orig_store))
|
|
||||||
.collect();
|
|
||||||
let a_mems: Vec<wasmtime::Memory> = orig_instance
|
|
||||||
.exports(&mut orig_store)
|
|
||||||
.filter_map(|e| e.into_memory())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let b_globals: Vec<_> = roundtrip_instance
|
|
||||||
.exports(&mut roundtrip_store)
|
|
||||||
.filter_map(|e| e.into_global())
|
|
||||||
.collect();
|
|
||||||
let b_globals: Vec<wasmtime::Val> = b_globals
|
|
||||||
.into_iter()
|
|
||||||
.map(|g| g.get(&mut roundtrip_store))
|
|
||||||
.collect();
|
|
||||||
let b_mems: Vec<wasmtime::Memory> = roundtrip_instance
|
|
||||||
.exports(&mut roundtrip_store)
|
|
||||||
.filter_map(|e| e.into_memory())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
log::info!("a_globals = {:?}", a_globals);
|
|
||||||
log::info!("b_globals = {:?}", b_globals);
|
|
||||||
|
|
||||||
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(&orig_store);
|
|
||||||
let b_data = b.data(&roundtrip_store);
|
|
||||||
assert_eq!(a_data, b_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
success(total);
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
let mut config = wasmtime::Config::default();
|
||||||
|
config.consume_fuel(true);
|
||||||
|
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||||
|
let orig_module =
|
||||||
|
wasmtime::Module::new(&engine, &orig_bytes[..]).expect("failed to parse original wasm");
|
||||||
|
let mut orig_store = wasmtime::Store::new(&engine, ());
|
||||||
|
orig_store.set_fuel(10000).unwrap();
|
||||||
|
let orig_instance = wasmtime::Instance::new(&mut orig_store, &orig_module, &[]);
|
||||||
|
let orig_instance = match orig_instance {
|
||||||
|
Ok(orig_instance) => orig_instance,
|
||||||
|
Err(e) => {
|
||||||
|
log::info!("cannot run start on orig intsance ({:?}); discarding", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut parsed_module =
|
||||||
|
Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap();
|
||||||
|
parsed_module.expand_all_funcs().unwrap();
|
||||||
|
parsed_module.per_func_body(|body| body.optimize());
|
||||||
|
let roundtrip_bytes = parsed_module.to_wasm_bytes().unwrap();
|
||||||
|
|
||||||
|
if let Ok(filename) = std::env::var("FUZZ_DUMP_WASM") {
|
||||||
|
std::fs::write(format!("{}_orig.wasm", filename), &orig_bytes[..]).unwrap();
|
||||||
|
std::fs::write(format!("{}_roundtrip.wasm", filename), &roundtrip_bytes[..]).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = TOTAL.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
|
let roundtrip_module = wasmtime::Module::new(&engine, &roundtrip_bytes[..])
|
||||||
|
.expect("failed to parse roundtripped wasm");
|
||||||
|
let mut roundtrip_store = wasmtime::Store::new(&engine, ());
|
||||||
|
// After roundtrip, fuel consumption rate may differ. That's fine;
|
||||||
|
// what matters is that it terminated above without a trap (hence
|
||||||
|
// halts in a reasonable time).
|
||||||
|
roundtrip_store.set_fuel(u64::MAX).unwrap();
|
||||||
|
let roundtrip_instance = wasmtime::Instance::new(&mut roundtrip_store, &roundtrip_module, &[])
|
||||||
|
.expect("cannot instantiate roundtripped wasm");
|
||||||
|
|
||||||
|
// Ensure exports are equal.
|
||||||
|
|
||||||
|
let a_globals: Vec<_> = orig_instance
|
||||||
|
.exports(&mut orig_store)
|
||||||
|
.filter_map(|e| e.into_global())
|
||||||
|
.collect();
|
||||||
|
let a_globals: Vec<wasmtime::Val> = a_globals
|
||||||
|
.into_iter()
|
||||||
|
.map(|g| g.get(&mut orig_store))
|
||||||
|
.collect();
|
||||||
|
let a_mems: Vec<wasmtime::Memory> = orig_instance
|
||||||
|
.exports(&mut orig_store)
|
||||||
|
.filter_map(|e| e.into_memory())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let b_globals: Vec<_> = roundtrip_instance
|
||||||
|
.exports(&mut roundtrip_store)
|
||||||
|
.filter_map(|e| e.into_global())
|
||||||
|
.collect();
|
||||||
|
let b_globals: Vec<wasmtime::Val> = b_globals
|
||||||
|
.into_iter()
|
||||||
|
.map(|g| g.get(&mut roundtrip_store))
|
||||||
|
.collect();
|
||||||
|
let b_mems: Vec<wasmtime::Memory> = roundtrip_instance
|
||||||
|
.exports(&mut roundtrip_store)
|
||||||
|
.filter_map(|e| e.into_memory())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
log::info!("a_globals = {:?}", a_globals);
|
||||||
|
log::info!("b_globals = {:?}", b_globals);
|
||||||
|
|
||||||
|
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(&orig_store);
|
||||||
|
let b_data = b.data(&roundtrip_store);
|
||||||
|
assert_eq!(a_data, b_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
success(total);
|
||||||
|
});
|
||||||
|
|
||||||
static TOTAL: AtomicU64 = AtomicU64::new(0);
|
static TOTAL: AtomicU64 = AtomicU64::new(0);
|
||||||
static SUCCESS: AtomicU64 = AtomicU64::new(0);
|
static SUCCESS: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
|
@ -3,66 +3,65 @@ use libfuzzer_sys::fuzz_target;
|
||||||
|
|
||||||
use waffle::{FrontendOptions, InterpContext, InterpResult, Module};
|
use waffle::{FrontendOptions, InterpContext, InterpResult, Module};
|
||||||
|
|
||||||
fuzz_target!(
|
fuzz_target!(|module: waffle::fuzzing::ArbitraryModule| {
|
||||||
|module: wasm_smith::ConfiguredModule<waffle::fuzzing::Config>| {
|
let module = module.0;
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
log::debug!("original module: {:?}", module.module);
|
log::debug!("original module: {:?}", module);
|
||||||
|
|
||||||
let orig_bytes = module.module.to_bytes();
|
let orig_bytes = module.to_bytes();
|
||||||
|
|
||||||
if waffle::fuzzing::reject(&orig_bytes[..]) {
|
if waffle::fuzzing::reject(&orig_bytes[..]) {
|
||||||
log::debug!("Discarding fuzz run. Body:\n{:?}", module);
|
log::debug!("Discarding fuzz run. Body:\n{:?}", module);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
log::info!("body: {:?}", module);
|
log::info!("body: {:?}", module);
|
||||||
}
|
|
||||||
|
|
||||||
let mut parsed_module =
|
|
||||||
Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap();
|
|
||||||
parsed_module.expand_all_funcs().unwrap();
|
|
||||||
|
|
||||||
let start = parsed_module.start_func.unwrap();
|
|
||||||
|
|
||||||
let mut orig_ctx = match InterpContext::new(&parsed_module) {
|
|
||||||
Ok(ctx) => ctx,
|
|
||||||
Err(e) => {
|
|
||||||
log::trace!("Rejecting due to instantiation error: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
orig_ctx.fuel = 10000;
|
|
||||||
|
|
||||||
match orig_ctx.call(&parsed_module, start, &[]) {
|
|
||||||
InterpResult::OutOfFuel => {
|
|
||||||
// Silently reject.
|
|
||||||
log::trace!("Rejecting due to timeout in orig");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
InterpResult::Trap(..) => {
|
|
||||||
// Silently reject.
|
|
||||||
log::trace!("Rejecting due to trap in orig");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
InterpResult::Ok(_) => {}
|
|
||||||
ret => panic!("Bad result: {:?}", ret),
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut opt_module = parsed_module.clone();
|
|
||||||
opt_module.per_func_body(|body| body.optimize());
|
|
||||||
opt_module.per_func_body(|body| body.convert_to_max_ssa(None));
|
|
||||||
|
|
||||||
let mut opt_ctx = InterpContext::new(&opt_module).unwrap();
|
|
||||||
// Allow a little leeway for opts to not actually optimize.
|
|
||||||
opt_ctx.fuel = 20000;
|
|
||||||
opt_ctx.call(&opt_module, start, &[]).ok().unwrap();
|
|
||||||
|
|
||||||
log::trace!(
|
|
||||||
"Orig ran in {} fuel; opt ran in {} fuel",
|
|
||||||
10000 - orig_ctx.fuel,
|
|
||||||
20000 - opt_ctx.fuel
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(orig_ctx.memories, opt_ctx.memories);
|
|
||||||
assert_eq!(orig_ctx.globals, opt_ctx.globals);
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
let mut parsed_module =
|
||||||
|
Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap();
|
||||||
|
parsed_module.expand_all_funcs().unwrap();
|
||||||
|
|
||||||
|
let start = parsed_module.start_func.unwrap();
|
||||||
|
|
||||||
|
let mut orig_ctx = match InterpContext::new(&parsed_module) {
|
||||||
|
Ok(ctx) => ctx,
|
||||||
|
Err(e) => {
|
||||||
|
log::trace!("Rejecting due to instantiation error: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
orig_ctx.fuel = 10000;
|
||||||
|
|
||||||
|
match orig_ctx.call(&parsed_module, start, &[]) {
|
||||||
|
InterpResult::OutOfFuel => {
|
||||||
|
// Silently reject.
|
||||||
|
log::trace!("Rejecting due to timeout in orig");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InterpResult::Trap(..) => {
|
||||||
|
// Silently reject.
|
||||||
|
log::trace!("Rejecting due to trap in orig");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InterpResult::Ok(_) => {}
|
||||||
|
ret => panic!("Bad result: {:?}", ret),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut opt_module = parsed_module.clone();
|
||||||
|
opt_module.per_func_body(|body| body.optimize());
|
||||||
|
opt_module.per_func_body(|body| body.convert_to_max_ssa(None));
|
||||||
|
|
||||||
|
let mut opt_ctx = InterpContext::new(&opt_module).unwrap();
|
||||||
|
// Allow a little leeway for opts to not actually optimize.
|
||||||
|
opt_ctx.fuel = 20000;
|
||||||
|
opt_ctx.call(&opt_module, start, &[]).ok().unwrap();
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"Orig ran in {} fuel; opt ran in {} fuel",
|
||||||
|
10000 - orig_ctx.fuel,
|
||||||
|
20000 - opt_ctx.fuel
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(orig_ctx.memories, opt_ctx.memories);
|
||||||
|
assert_eq!(orig_ctx.globals, opt_ctx.globals);
|
||||||
|
});
|
||||||
|
|
|
@ -940,6 +940,7 @@ impl<'a> WasmFuncBackend<'a> {
|
||||||
Operator::CallRef { sig_index } => {
|
Operator::CallRef { sig_index } => {
|
||||||
Some(wasm_encoder::Instruction::CallRef(sig_index.index() as u32))
|
Some(wasm_encoder::Instruction::CallRef(sig_index.index() as u32))
|
||||||
}
|
}
|
||||||
|
Operator::RefIsNull => Some(wasm_encoder::Instruction::RefIsNull),
|
||||||
Operator::RefFunc { func_index } => {
|
Operator::RefFunc { func_index } => {
|
||||||
Some(wasm_encoder::Instruction::RefFunc(func_index.index() as u32))
|
Some(wasm_encoder::Instruction::RefFunc(func_index.index() as u32))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1426,6 +1426,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
|
||||||
| wasmparser::Operator::F32x4DemoteF64x2Zero
|
| wasmparser::Operator::F32x4DemoteF64x2Zero
|
||||||
| wasmparser::Operator::F64x2PromoteLowF32x4
|
| wasmparser::Operator::F64x2PromoteLowF32x4
|
||||||
| wasmparser::Operator::CallRef { .. }
|
| wasmparser::Operator::CallRef { .. }
|
||||||
|
| wasmparser::Operator::RefIsNull
|
||||||
| wasmparser::Operator::RefFunc { .. } => {
|
| wasmparser::Operator::RefFunc { .. } => {
|
||||||
self.emit(Operator::try_from(&op).unwrap(), loc)?
|
self.emit(Operator::try_from(&op).unwrap(), loc)?
|
||||||
}
|
}
|
||||||
|
|
179
src/fuzzing.rs
179
src/fuzzing.rs
|
@ -1,111 +1,84 @@
|
||||||
//! Fuzzing-specific utilities.
|
//! Fuzzing-specific utilities.
|
||||||
|
|
||||||
// use libfuzzer_sys::arbitrary;
|
use libfuzzer_sys::arbitrary;
|
||||||
|
|
||||||
// pub fn reject(bytes: &[u8]) -> bool {
|
pub fn reject(bytes: &[u8]) -> bool {
|
||||||
// let parser = wasmparser::Parser::new(0);
|
let parser = wasmparser::Parser::new(0);
|
||||||
// let mut has_start = false;
|
let mut has_start = false;
|
||||||
// let mut has_global_set = false;
|
let mut has_global_set = false;
|
||||||
// let mut num_globals = 0;
|
let mut num_globals = 0;
|
||||||
// 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) => {
|
||||||
// for op in body.get_operators_reader().unwrap() {
|
for op in body.get_operators_reader().unwrap() {
|
||||||
// let op = op.unwrap();
|
let op = op.unwrap();
|
||||||
// match op {
|
match op {
|
||||||
// wasmparser::Operator::GlobalSet { .. } => {
|
wasmparser::Operator::GlobalSet { .. } => {
|
||||||
// has_global_set = true;
|
has_global_set = true;
|
||||||
// }
|
}
|
||||||
// _ => {}
|
_ => {}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// wasmparser::Payload::StartSection { .. } => {
|
wasmparser::Payload::StartSection { .. } => {
|
||||||
// has_start = true;
|
has_start = true;
|
||||||
// }
|
}
|
||||||
// wasmparser::Payload::ExportSection(mut reader) => {
|
wasmparser::Payload::ExportSection(reader) => {
|
||||||
// for _ in 0..reader.get_count() {
|
for export in reader {
|
||||||
// let e = reader.read().unwrap();
|
let export = export.unwrap();
|
||||||
// match &e.kind {
|
match &export.kind {
|
||||||
// &wasmparser::ExternalKind::Global => {
|
&wasmparser::ExternalKind::Global => {
|
||||||
// num_globals += 1;
|
num_globals += 1;
|
||||||
// }
|
}
|
||||||
// _ => {}
|
_ => {}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// wasmparser::Payload::MemorySection(mut reader) => {
|
wasmparser::Payload::MemorySection(reader) => {
|
||||||
// for _ in 0..reader.get_count() {
|
for mem in reader {
|
||||||
// let m = reader.read().unwrap();
|
let mem = mem.unwrap();
|
||||||
// if m.maximum.is_none() || m.maximum.unwrap() > 100 {
|
if mem.maximum.is_none() || mem.maximum.unwrap() > 100 {
|
||||||
// return true;
|
return true;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// _ => {}
|
_ => {}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if !has_start || !has_global_set || num_globals < 1 {
|
if !has_start || !has_global_set || num_globals < 1 {
|
||||||
// return true;
|
return true;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// false
|
false
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[derive(Debug)]
|
pub fn fuzzing_config() -> wasm_smith::Config {
|
||||||
// pub struct Config;
|
wasm_smith::Config {
|
||||||
|
min_funcs: 1,
|
||||||
|
max_funcs: 1,
|
||||||
|
min_memories: 1,
|
||||||
|
max_memories: 1,
|
||||||
|
min_globals: 10,
|
||||||
|
max_globals: 10,
|
||||||
|
min_tables: 0,
|
||||||
|
max_tables: 0,
|
||||||
|
min_imports: 0,
|
||||||
|
max_imports: 0,
|
||||||
|
min_exports: 12,
|
||||||
|
max_exports: 12,
|
||||||
|
allow_start_export: true,
|
||||||
|
canonicalize_nans: true,
|
||||||
|
max_memory32_pages: 1,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// impl<'a> arbitrary::Arbitrary<'a> for Config {
|
#[derive(Debug)]
|
||||||
// fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
pub struct ArbitraryModule(pub wasm_smith::Module);
|
||||||
// Ok(Config)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl wasm_smith::Config for Config {
|
impl<'a> arbitrary::Arbitrary<'a> for ArbitraryModule {
|
||||||
// fn min_funcs(&self) -> usize {
|
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||||
// 1
|
Ok(Self(wasm_smith::Module::new(fuzzing_config(), u)?))
|
||||||
// }
|
}
|
||||||
// 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
|
|
||||||
// }
|
|
||||||
// fn max_memory_pages(&self, _is_64: bool) -> u64 {
|
|
||||||
// 1
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use crate::entity::EntityRef;
|
use crate::entity::EntityRef;
|
||||||
use crate::ir::{Module, Type, Value};
|
use crate::ir::{Module, Type, Value};
|
||||||
use crate::Operator;
|
use crate::Operator;
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub fn op_inputs(
|
pub fn op_inputs(
|
||||||
|
@ -485,6 +485,7 @@ pub fn op_inputs(
|
||||||
params.push(Type::TypedFuncRef(true, sig_index.index() as u32));
|
params.push(Type::TypedFuncRef(true, sig_index.index() as u32));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
}
|
}
|
||||||
|
Operator::RefIsNull => Ok(vec![op_stack.context("in getting stack")?.last().unwrap().0].into()),
|
||||||
Operator::RefFunc { .. } => Ok(Cow::Borrowed(&[])),
|
Operator::RefFunc { .. } => Ok(Cow::Borrowed(&[])),
|
||||||
Operator::MemoryCopy { .. } => Ok(Cow::Borrowed(&[Type::I32, Type::I32, Type::I32])),
|
Operator::MemoryCopy { .. } => Ok(Cow::Borrowed(&[Type::I32, Type::I32, Type::I32])),
|
||||||
Operator::MemoryFill { .. } => Ok(Cow::Borrowed(&[Type::I32, Type::I32, Type::I32])),
|
Operator::MemoryFill { .. } => Ok(Cow::Borrowed(&[Type::I32, Type::I32, Type::I32])),
|
||||||
|
@ -716,10 +717,10 @@ pub fn op_outputs(
|
||||||
Operator::V128Load16Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::V128Load16Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
||||||
Operator::V128Load32Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::V128Load32Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
||||||
Operator::V128Load64Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::V128Load64Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
||||||
Operator::V128Store8Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::V128Store8Lane { .. } => Ok(Cow::Borrowed(&[])),
|
||||||
Operator::V128Store16Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::V128Store16Lane { .. } => Ok(Cow::Borrowed(&[])),
|
||||||
Operator::V128Store32Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::V128Store32Lane { .. } => Ok(Cow::Borrowed(&[])),
|
||||||
Operator::V128Store64Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::V128Store64Lane { .. } => Ok(Cow::Borrowed(&[])),
|
||||||
|
|
||||||
Operator::V128Const { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::V128Const { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
||||||
Operator::I8x16Shuffle { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
Operator::I8x16Shuffle { .. } => Ok(Cow::Borrowed(&[Type::V128])),
|
||||||
|
@ -955,6 +956,7 @@ pub fn op_outputs(
|
||||||
Operator::CallRef { sig_index } => {
|
Operator::CallRef { sig_index } => {
|
||||||
Ok(Vec::from(module.signatures[*sig_index].returns.clone()).into())
|
Ok(Vec::from(module.signatures[*sig_index].returns.clone()).into())
|
||||||
}
|
}
|
||||||
|
Operator::RefIsNull => Ok(Cow::Borrowed(&[Type::I32])),
|
||||||
Operator::RefFunc { func_index } => {
|
Operator::RefFunc { func_index } => {
|
||||||
let ty = module.funcs[*func_index].sig();
|
let ty = module.funcs[*func_index].sig();
|
||||||
Ok(vec![Type::TypedFuncRef(true, ty.index() as u32)].into())
|
Ok(vec![Type::TypedFuncRef(true, ty.index() as u32)].into())
|
||||||
|
@ -1427,6 +1429,7 @@ impl Operator {
|
||||||
Operator::F64x2PromoteLowF32x4 => &[],
|
Operator::F64x2PromoteLowF32x4 => &[],
|
||||||
|
|
||||||
Operator::CallRef { .. } => &[All],
|
Operator::CallRef { .. } => &[All],
|
||||||
|
Operator::RefIsNull => &[],
|
||||||
Operator::RefFunc { .. } => &[],
|
Operator::RefFunc { .. } => &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1922,6 +1925,7 @@ impl std::fmt::Display for Operator {
|
||||||
Operator::F64x2PromoteLowF32x4 => write!(f, "f64x2promotelowf32x4")?,
|
Operator::F64x2PromoteLowF32x4 => write!(f, "f64x2promotelowf32x4")?,
|
||||||
|
|
||||||
Operator::CallRef { sig_index } => write!(f, "call_ref<{}>", sig_index)?,
|
Operator::CallRef { sig_index } => write!(f, "call_ref<{}>", sig_index)?,
|
||||||
|
Operator::RefIsNull => write!(f, "ref_is_null")?,
|
||||||
Operator::RefFunc { func_index } => write!(f, "ref_func<{}>", func_index)?,
|
Operator::RefFunc { func_index } => write!(f, "ref_func<{}>", func_index)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -634,6 +634,7 @@ pub enum Operator {
|
||||||
CallRef {
|
CallRef {
|
||||||
sig_index: Signature,
|
sig_index: Signature,
|
||||||
},
|
},
|
||||||
|
RefIsNull,
|
||||||
RefFunc {
|
RefFunc {
|
||||||
func_index: Func,
|
func_index: Func,
|
||||||
},
|
},
|
||||||
|
@ -1276,6 +1277,7 @@ impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator {
|
||||||
&wasmparser::Operator::CallRef { type_index } => Ok(Operator::CallRef {
|
&wasmparser::Operator::CallRef { type_index } => Ok(Operator::CallRef {
|
||||||
sig_index: Signature::from(type_index),
|
sig_index: Signature::from(type_index),
|
||||||
}),
|
}),
|
||||||
|
&wasmparser::Operator::RefIsNull => Ok(Operator::RefIsNull),
|
||||||
&wasmparser::Operator::RefFunc { function_index } => Ok(Operator::RefFunc {
|
&wasmparser::Operator::RefFunc { function_index } => Ok(Operator::RefFunc {
|
||||||
func_index: Func::from(function_index),
|
func_index: Func::from(function_index),
|
||||||
}),
|
}),
|
||||||
|
|
8
wasm_tests/non-nullable-funcrefs.wat
Normal file
8
wasm_tests/non-nullable-funcrefs.wat
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
(module
|
||||||
|
(type $t (func (param i32) (result i32)))
|
||||||
|
(func $f1 (param i32) (result i32) local.get 0)
|
||||||
|
(func $f2 (param i32) (result i32) local.get 0)
|
||||||
|
(table $t 1 1 (ref null $t))
|
||||||
|
(elem $t (i32.const 0) (ref null $t)
|
||||||
|
(item (ref.func $f1))
|
||||||
|
(item (ref.func $f2))))
|
Loading…
Reference in a new issue