Skip to content

Commit

Permalink
Generate .wasm and .wat fuzz inputs upon crashes (#1259)
Browse files Browse the repository at this point in the history
* add crash input generation to wasmi_fuzz

* generate crash reports in `translate` and `diferential` fuzzers
  • Loading branch information
Robbepop authored Oct 27, 2024
1 parent cadc12f commit 5d48b3c
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 5 deletions.
20 changes: 17 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion crates/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ wasmi-stack = { package = "wasmi", feature = ["std"], version = "0.31.2", option
wasmtime = { version = "26.0.0", feature = ["std"], optional = true }
wasm-smith = "0.219.1"
arbitrary = "1.3.2"
sha2 = "0.10"
wasmprinter = "0.219.1"
anyhow = "1.0.91"

[features]
default = ["differential"]
differential = ["dep:wasmi-stack", "dep:wasmtime"]
differential = [
"dep:wasmi-stack",
"dep:wasmtime",
]
33 changes: 33 additions & 0 deletions crates/fuzz/src/crash_inputs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use core::fmt::Write as _;
use sha2::{Digest, Sha256};
use std::fs;

/// Writes `.wasm` and `.wat` files for `target` with `wasm` contents.
///
/// Returns the `hex` encoded string of the SHA-2 of the `wasm` input upon success.
///
/// # Errors
///
/// - If hex encoding fails.
/// - If converting `.wasm` to `.wat` fails.
/// - If writing the `.wasm` or `.wat` files fails.
pub fn generate_crash_inputs(target: &str, wasm: &[u8]) -> Result<String, anyhow::Error> {
let mut sha2 = Sha256::default();
sha2.update(wasm);
let hash: [u8; 32] = sha2.finalize().into();
let hash_str = hash_str(hash)?;
let wat = wasmprinter::print_bytes(wasm)?;
let file_path = format!("fuzz/crash-inputs/{target}/crash-{hash_str}");
fs::write(format!("{file_path}.wasm"), wasm)?;
fs::write(format!("{file_path}.wat"), wat)?;
Ok(hash_str)
}

/// Returns the `hex` string of the `[u8; 32]` hash.
fn hash_str(hash: [u8; 32]) -> Result<String, anyhow::Error> {
let mut s = String::new();
for byte in hash {
write!(s, "{byte:02X}")?;
}
Ok(s)
}
2 changes: 2 additions & 0 deletions crates/fuzz/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
pub mod config;
mod crash_inputs;
mod error;
#[cfg(feature = "differential")]
pub mod oracle;
mod value;

pub use self::{
config::{FuzzSmithConfig, FuzzWasmiConfig},
crash_inputs::generate_crash_inputs,
error::{FuzzError, TrapCode},
value::{FuzzVal, FuzzValType},
};
18 changes: 18 additions & 0 deletions fuzz/fuzz_targets/differential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,49 +73,57 @@ fuzz_target!(|seed: &[u8]| {
if wasmi_results == oracle_results {
continue;
}
let crash_input = generate_crash_inputs(wasm);
panic!(
"\
function call returned different values:\n\
\tfunc: {name}\n\
\tparams: {params:?}\n\
\t{wasmi_name}: {wasmi_results:?}\n\
\t{oracle_name}: {oracle_results:?}\n\
\tcrash-report: 0x{crash_input}\n\
"
)
}
(Err(wasmi_err), Err(oracle_err)) => {
if wasmi_err == oracle_err {
continue;
}
let crash_input = generate_crash_inputs(wasm);
panic!(
"\
function call returned different errors:\n\
\tfunc: {name}\n\
\tparams: {params:?}\n\
\t{wasmi_name}: {wasmi_err:?}\n\
\t{oracle_name}: {oracle_err:?}\n\
\tcrash-report: 0x{crash_input}\n\
"
)
}
(Ok(wasmi_results), Err(oracle_err)) => {
let crash_input = generate_crash_inputs(wasm);
panic!(
"\
function call returned results and error:\n\
\tfunc: {name}\n\
\tparams: {params:?}\n\
\t{wasmi_name}: {wasmi_results:?}\n\
\t{oracle_name}: {oracle_err:?}\n\
\tcrash-report: 0x{crash_input}\n\
"
)
}
(Err(wasmi_err), Ok(oracle_results)) => {
let crash_input = generate_crash_inputs(wasm);
panic!(
"\
function call returned results and error:\n\
\tfunc: {name}\n\
\tparams: {params:?}\n\
\t{wasmi_name}: {wasmi_err:?}\n\
\t{oracle_name}: {oracle_results:?}\n\
\tcrash-report: 0x{crash_input}\n\
"
)
}
Expand All @@ -135,12 +143,14 @@ fuzz_target!(|seed: &[u8]| {
}
let wasmi_name = wasmi_oracle.name();
let oracle_name = chosen_oracle.name();
let crash_input = generate_crash_inputs(wasm);
panic!(
"\
encountered unequal globals:\n\
\tglobal: {name}\n\
\t{wasmi_name}: {wasmi_val:?}\n\
\t{oracle_name}: {oracle_val:?}\n\
\tcrash-report: 0x{crash_input}\n\
"
)
}
Expand All @@ -167,14 +177,22 @@ fuzz_target!(|seed: &[u8]| {
}
let wasmi_name = wasmi_oracle.name();
let oracle_name = chosen_oracle.name();
let crash_input = generate_crash_inputs(wasm);
panic!(
"\
encountered unequal memories:\n\
\tmemory: {name}\n\
\tindex first non-matching: {first_nonmatching}\n\
\t{wasmi_name}: {byte_wasmi:?}\n\
\t{oracle_name}: {byte_oracle:?}\n\
\tcrash-report: 0x{crash_input}\n\
"
)
}
});

/// Generate crash input reports for `differential` fuzzing.`
#[track_caller]
fn generate_crash_inputs(wasm: &[u8]) -> String {
wasmi_fuzz::generate_crash_inputs("differential", wasm).unwrap()
}
10 changes: 9 additions & 1 deletion fuzz/fuzz_targets/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,13 @@ fuzz_target!(|seed: &[u8]| {
unsafe { Module::new_unchecked(&engine, wasm) }
}
};
status.unwrap();
if let Err(err) = status {
let crash_input = wasmi_fuzz::generate_crash_inputs("translate", wasm).unwrap();
panic!(
"\
encountered invalid translation: {err}\n\
\t- crash-report: 0x{crash_input}\n\
"
);
}
});

0 comments on commit 5d48b3c

Please sign in to comment.