diff --git a/Cargo.lock b/Cargo.lock index d472da420d..7c5e8f0dc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "arbitrary" @@ -1954,10 +1954,13 @@ dependencies = [ name = "wasmi_fuzz" version = "0.38.0" dependencies = [ + "anyhow", "arbitrary", + "sha2", "wasm-smith", "wasmi 0.31.2", "wasmi 0.38.0", + "wasmprinter 0.219.1", "wasmtime", ] @@ -2022,6 +2025,17 @@ dependencies = [ "wasmparser 0.218.0", ] +[[package]] +name = "wasmprinter" +version = "0.219.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228cdc1f30c27816da225d239ce4231f28941147d34713dee8f1fff7cb330e54" +dependencies = [ + "anyhow", + "termcolor", + "wasmparser 0.219.1", +] + [[package]] name = "wasmtime" version = "26.0.0" @@ -2177,7 +2191,7 @@ dependencies = [ "target-lexicon", "wasm-encoder 0.218.0", "wasmparser 0.218.0", - "wasmprinter", + "wasmprinter 0.218.0", "wasmtime-component-util", ] diff --git a/crates/fuzz/Cargo.toml b/crates/fuzz/Cargo.toml index 9c48ebb7b7..47e889ee08 100644 --- a/crates/fuzz/Cargo.toml +++ b/crates/fuzz/Cargo.toml @@ -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", +] diff --git a/crates/fuzz/src/crash_inputs.rs b/crates/fuzz/src/crash_inputs.rs new file mode 100644 index 0000000000..eff98420a3 --- /dev/null +++ b/crates/fuzz/src/crash_inputs.rs @@ -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 { + 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 { + let mut s = String::new(); + for byte in hash { + write!(s, "{byte:02X}")?; + } + Ok(s) +} diff --git a/crates/fuzz/src/lib.rs b/crates/fuzz/src/lib.rs index 9178a00818..acc6b0f936 100644 --- a/crates/fuzz/src/lib.rs +++ b/crates/fuzz/src/lib.rs @@ -1,4 +1,5 @@ pub mod config; +mod crash_inputs; mod error; #[cfg(feature = "differential")] pub mod oracle; @@ -6,6 +7,7 @@ mod value; pub use self::{ config::{FuzzSmithConfig, FuzzWasmiConfig}, + crash_inputs::generate_crash_inputs, error::{FuzzError, TrapCode}, value::{FuzzVal, FuzzValType}, }; diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs index 724bb89eac..78605f33e2 100644 --- a/fuzz/fuzz_targets/differential.rs +++ b/fuzz/fuzz_targets/differential.rs @@ -73,6 +73,7 @@ fuzz_target!(|seed: &[u8]| { if wasmi_results == oracle_results { continue; } + let crash_input = generate_crash_inputs(wasm); panic!( "\ function call returned different values:\n\ @@ -80,6 +81,7 @@ fuzz_target!(|seed: &[u8]| { \tparams: {params:?}\n\ \t{wasmi_name}: {wasmi_results:?}\n\ \t{oracle_name}: {oracle_results:?}\n\ + \tcrash-report: 0x{crash_input}\n\ " ) } @@ -87,6 +89,7 @@ fuzz_target!(|seed: &[u8]| { if wasmi_err == oracle_err { continue; } + let crash_input = generate_crash_inputs(wasm); panic!( "\ function call returned different errors:\n\ @@ -94,10 +97,12 @@ fuzz_target!(|seed: &[u8]| { \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\ @@ -105,10 +110,12 @@ fuzz_target!(|seed: &[u8]| { \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\ @@ -116,6 +123,7 @@ fuzz_target!(|seed: &[u8]| { \tparams: {params:?}\n\ \t{wasmi_name}: {wasmi_err:?}\n\ \t{oracle_name}: {oracle_results:?}\n\ + \tcrash-report: 0x{crash_input}\n\ " ) } @@ -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\ " ) } @@ -167,6 +177,7 @@ 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\ @@ -174,7 +185,14 @@ fuzz_target!(|seed: &[u8]| { \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() +} diff --git a/fuzz/fuzz_targets/translate.rs b/fuzz/fuzz_targets/translate.rs index 6f1ca75c8c..341e107d9c 100644 --- a/fuzz/fuzz_targets/translate.rs +++ b/fuzz/fuzz_targets/translate.rs @@ -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\ + " + ); + } });