This commit is contained in:
Graham Kelly 2024-04-05 14:54:28 -04:00
commit 382f9808d8
10 changed files with 261 additions and 277 deletions

View file

@ -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"

View file

@ -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 = ".."

View file

@ -4,12 +4,12 @@ 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);
@ -24,8 +24,7 @@ fuzz_target!(
let orig_module = let orig_module =
wasmtime::Module::new(&engine, &orig_bytes[..]).expect("failed to parse original wasm"); wasmtime::Module::new(&engine, &orig_bytes[..]).expect("failed to parse original wasm");
let mut orig_store = wasmtime::Store::new(&engine, ()); let mut orig_store = wasmtime::Store::new(&engine, ());
orig_store.out_of_fuel_trap(); orig_store.set_fuel(10000).unwrap();
orig_store.add_fuel(10000).unwrap();
let orig_instance = wasmtime::Instance::new(&mut orig_store, &orig_module, &[]); let orig_instance = wasmtime::Instance::new(&mut orig_store, &orig_module, &[]);
let orig_instance = match orig_instance { let orig_instance = match orig_instance {
Ok(orig_instance) => orig_instance, Ok(orig_instance) => orig_instance,
@ -51,13 +50,11 @@ fuzz_target!(
let roundtrip_module = wasmtime::Module::new(&engine, &roundtrip_bytes[..]) let roundtrip_module = wasmtime::Module::new(&engine, &roundtrip_bytes[..])
.expect("failed to parse roundtripped wasm"); .expect("failed to parse roundtripped wasm");
let mut roundtrip_store = wasmtime::Store::new(&engine, ()); let mut roundtrip_store = wasmtime::Store::new(&engine, ());
roundtrip_store.out_of_fuel_trap();
// After roundtrip, fuel consumption rate may differ. That's fine; // After roundtrip, fuel consumption rate may differ. That's fine;
// what matters is that it terminated above without a trap (hence // what matters is that it terminated above without a trap (hence
// halts in a reasonable time). // halts in a reasonable time).
roundtrip_store.add_fuel(u64::MAX).unwrap(); roundtrip_store.set_fuel(u64::MAX).unwrap();
let roundtrip_instance = let roundtrip_instance = wasmtime::Instance::new(&mut roundtrip_store, &roundtrip_module, &[])
wasmtime::Instance::new(&mut roundtrip_store, &roundtrip_module, &[])
.expect("cannot instantiate roundtripped wasm"); .expect("cannot instantiate roundtripped wasm");
// Ensure exports are equal. // Ensure exports are equal.
@ -110,8 +107,7 @@ fuzz_target!(
} }
success(total); 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);

View file

@ -3,12 +3,12 @@ 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);
@ -64,5 +64,4 @@ fuzz_target!(
assert_eq!(orig_ctx.memories, opt_ctx.memories); assert_eq!(orig_ctx.memories, opt_ctx.memories);
assert_eq!(orig_ctx.globals, opt_ctx.globals); assert_eq!(orig_ctx.globals, opt_ctx.globals);
} });
);

View file

@ -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))
} }

View file

@ -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)?
} }

View file

@ -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
// }
// }

View file

@ -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)?,
} }

View file

@ -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),
}), }),

View 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))))