-
Notifications
You must be signed in to change notification settings - Fork 286
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Modernize Wasmi differential fuzzing (#1257)
* refactor FuzzVal * add FuzzError * add Wasm module exports abstraction * add DifferentialOracle trait * add Wasmi oracle * remove invalid doc link * move wasmi/mod.rs -> wasmi.rs * move From FuzzVal -> wasmi::Val impl * add differential crate feature to wasmi_fuzz * rename binding * remove unused imports * add Wasmi v0.31 oracle impl * improve panic message * add wasmtime differential oracle * clean-up wasmtime oracle * improve FuzzError and add is_non_deterministic * add FuncType information to fuzz ModuleExports * add [Partial]Eq for FuzzVal * refactor DifferentialOracle traits * enable tail-call and extended-const Wasm proposals for Wasmi (stack) oracle * modernize differential fuzzing
- Loading branch information
Showing
12 changed files
with
932 additions
and
734 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#[derive(Debug, PartialEq, Eq)] | ||
pub enum FuzzError { | ||
Trap(TrapCode), | ||
Other, | ||
} | ||
|
||
impl FuzzError { | ||
/// Returns `true` if `self` may be of non-deterministic origin. | ||
pub fn is_non_deterministic(&self) -> bool { | ||
matches!(self, Self::Trap(TrapCode::StackOverflow) | Self::Other) | ||
} | ||
} | ||
|
||
#[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
pub enum TrapCode { | ||
UnreachableCodeReached, | ||
MemoryOutOfBounds, | ||
TableOutOfBounds, | ||
IndirectCallToNull, | ||
IntegerDivisionByZero, | ||
IntegerOverflow, | ||
BadConversionToInteger, | ||
StackOverflow, | ||
BadSignature, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
pub mod config; | ||
mod oracle; | ||
mod error; | ||
#[cfg(feature = "differential")] | ||
pub mod oracle; | ||
mod value; | ||
|
||
pub use self::{ | ||
config::{FuzzSmithConfig, FuzzWasmiConfig}, | ||
value::{FuzzRefTy, FuzzVal, FuzzValType}, | ||
error::{FuzzError, TrapCode}, | ||
value::{FuzzVal, FuzzValType}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
use core::slice; | ||
use wasmi::FuncType; | ||
|
||
/// Names of exported Wasm objects from a fuzzed Wasm module. | ||
#[derive(Debug, Default)] | ||
pub struct ModuleExports { | ||
/// Names of exported functions. | ||
funcs: StringSequence, | ||
/// The types of exported functions. | ||
func_types: Vec<FuncType>, | ||
/// Names of exported global variables. | ||
globals: StringSequence, | ||
/// Names of exported linear memories. | ||
memories: StringSequence, | ||
/// Names of exported tables. | ||
tables: StringSequence, | ||
} | ||
|
||
impl ModuleExports { | ||
/// Pushes an exported function `name` to `self`. | ||
pub(crate) fn push_func(&mut self, name: &str, ty: FuncType) { | ||
self.funcs.push(name); | ||
self.func_types.push(ty); | ||
} | ||
|
||
/// Pushes an exported global `name` to `self`. | ||
pub(crate) fn push_global(&mut self, name: &str) { | ||
self.globals.push(name); | ||
} | ||
|
||
/// Pushes an exported memory `name` to `self`. | ||
pub(crate) fn push_memory(&mut self, name: &str) { | ||
self.memories.push(name); | ||
} | ||
|
||
/// Pushes an exported table `name` to `self`. | ||
pub(crate) fn push_table(&mut self, name: &str) { | ||
self.tables.push(name); | ||
} | ||
|
||
/// Returns an iterator yielding the names of the exported Wasm functions. | ||
pub fn funcs(&self) -> ExportedFuncsIter { | ||
ExportedFuncsIter { | ||
names: self.funcs.iter(), | ||
types: self.func_types.iter(), | ||
} | ||
} | ||
|
||
/// Returns an iterator yielding the names of the exported Wasm globals. | ||
pub fn globals(&self) -> StringSequenceIter { | ||
self.globals.iter() | ||
} | ||
|
||
/// Returns an iterator yielding the names of the exported Wasm memories. | ||
pub fn memories(&self) -> StringSequenceIter { | ||
self.memories.iter() | ||
} | ||
|
||
/// Returns an iterator yielding the names of the exported Wasm tables. | ||
pub fn tables(&self) -> StringSequenceIter { | ||
self.tables.iter() | ||
} | ||
} | ||
|
||
/// Iterator yieling the exported functions of a fuzzed Wasm module. | ||
#[derive(Debug)] | ||
pub struct ExportedFuncsIter<'a> { | ||
/// The names of the exported Wasm functions. | ||
names: StringSequenceIter<'a>, | ||
/// The types of the exported Wasm functions. | ||
types: slice::Iter<'a, FuncType>, | ||
} | ||
|
||
impl<'a> Iterator for ExportedFuncsIter<'a> { | ||
type Item = (&'a str, &'a FuncType); | ||
|
||
#[inline] | ||
fn next(&mut self) -> Option<Self::Item> { | ||
let name = self.names.next()?; | ||
let ty = self.types.next()?; | ||
Some((name, ty)) | ||
} | ||
|
||
#[inline] | ||
fn size_hint(&self) -> (usize, Option<usize>) { | ||
self.names.size_hint() | ||
} | ||
} | ||
|
||
/// An append-only sequence of strings. | ||
#[derive(Debug, Default)] | ||
pub struct StringSequence { | ||
/// The underlying sequence of strings. | ||
strings: Vec<Box<str>>, | ||
} | ||
|
||
impl StringSequence { | ||
/// Pushes another string `s` to `self`. | ||
pub fn push(&mut self, s: &str) { | ||
self.strings.push(Box::from(s)); | ||
} | ||
|
||
/// Returns an iterator over the strings in `self`. | ||
/// | ||
/// The iterator yields the strings in order of their insertion. | ||
pub fn iter(&self) -> StringSequenceIter { | ||
StringSequenceIter { | ||
iter: self.strings.iter(), | ||
} | ||
} | ||
} | ||
|
||
/// An iterator yielding the strings of a sequence of strings. | ||
#[derive(Debug)] | ||
pub struct StringSequenceIter<'a> { | ||
/// The underlying iterator over strings. | ||
iter: slice::Iter<'a, Box<str>>, | ||
} | ||
|
||
impl<'a> Iterator for StringSequenceIter<'a> { | ||
type Item = &'a str; | ||
|
||
#[inline] | ||
fn next(&mut self) -> Option<Self::Item> { | ||
self.iter.next().map(|s| &**s) | ||
} | ||
|
||
#[inline] | ||
fn size_hint(&self) -> (usize, Option<usize>) { | ||
self.iter.size_hint() | ||
} | ||
} | ||
|
||
impl ExactSizeIterator for StringSequenceIter<'_> {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,77 @@ | ||
pub use self::{ | ||
exports::{ModuleExports, StringSequenceIter}, | ||
wasmi::WasmiOracle, | ||
wasmi_stack::WasmiStackOracle, | ||
wasmtime::WasmtimeOracle, | ||
}; | ||
use crate::{FuzzError, FuzzSmithConfig, FuzzVal}; | ||
use arbitrary::{Arbitrary, Unstructured}; | ||
|
||
mod exports; | ||
mod wasmi; | ||
mod wasmi_stack; | ||
mod wasmtime; | ||
|
||
/// Trait implemented by differential fuzzing oracles. | ||
pub trait DifferentialOracle { | ||
/// Returns the name of the differential fuzzing oracle. | ||
fn name(&self) -> &'static str; | ||
|
||
/// Calls the exported function with `name` and `params` and returns the result. | ||
fn call(&mut self, name: &str, params: &[FuzzVal]) -> Result<Box<[FuzzVal]>, FuzzError>; | ||
|
||
/// Returns the value of the global named `name` if any. | ||
fn get_global(&mut self, name: &str) -> Option<FuzzVal>; | ||
|
||
/// Returns the bytes of the memory named `name` if any. | ||
fn get_memory(&mut self, name: &str) -> Option<&[u8]>; | ||
} | ||
|
||
/// Trait implemented by differential fuzzing oracles. | ||
pub trait DifferentialOracleMeta: Sized { | ||
/// Tells `config` about the minimum viable configuration possible for this oracle. | ||
fn configure(config: &mut FuzzSmithConfig); | ||
|
||
/// Sets up the Wasm fuzzing oracle for the given `wasm` binary if possible. | ||
fn setup(wasm: &[u8]) -> Option<Self>; | ||
} | ||
|
||
/// A chosen differnential fuzzing oracle. | ||
#[derive(Debug, Default, Copy, Clone)] | ||
pub enum ChosenOracle { | ||
/// The legacy Wasmi v0.31 oracle. | ||
#[default] | ||
WasmiStack, | ||
/// The Wasmtime oracle. | ||
Wasmtime, | ||
} | ||
|
||
impl Arbitrary<'_> for ChosenOracle { | ||
fn arbitrary(u: &mut Unstructured) -> arbitrary::Result<Self> { | ||
let index = u8::arbitrary(u).unwrap_or_default(); | ||
let chosen = match index { | ||
0 => Self::Wasmtime, | ||
_ => Self::WasmiStack, | ||
}; | ||
Ok(chosen) | ||
} | ||
} | ||
|
||
impl ChosenOracle { | ||
/// Configures `fuzz_config` for the chosen differential fuzzing oracle. | ||
pub fn configure(&self, fuzz_config: &mut FuzzSmithConfig) { | ||
match self { | ||
ChosenOracle::WasmiStack => WasmiStackOracle::configure(fuzz_config), | ||
ChosenOracle::Wasmtime => WasmtimeOracle::configure(fuzz_config), | ||
} | ||
} | ||
|
||
/// Sets up the chosen differential fuzzing oracle. | ||
pub fn setup(&self, wasm: &[u8]) -> Option<Box<dyn DifferentialOracle>> { | ||
let oracle: Box<dyn DifferentialOracle> = match self { | ||
ChosenOracle::WasmiStack => Box::new(WasmiStackOracle::setup(wasm)?), | ||
ChosenOracle::Wasmtime => Box::new(WasmtimeOracle::setup(wasm)?), | ||
}; | ||
Some(oracle) | ||
} | ||
} |
Oops, something went wrong.