From 2fb793c8b8934438ebeac41d49c3751a344073f9 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 25 Jun 2018 17:46:13 +0300 Subject: [PATCH] Add hfuzz into repo (#103) * Add fuzzing against spec interpreter. * Redirect output of spec to /dev/null * Also stderr * Refactor * Oops. Revert to temp file creation. * Version of libfuzzer pinned * Add honggfuzz. * Impl hfuzz * Update parity-wasm. * Update honggfuzz to 0.5.9. * Update parity-wasm * Stack hash. * Update script a bit. * Unpin parity-wasm version * Indentation --- fuzz/Cargo.toml | 6 +++ fuzz/fuzz_targets/load_spec.rs | 53 +++++++++++++++++++++++++ hfuzz/.gitignore | 2 + hfuzz/Cargo.toml | 10 +++++ hfuzz/src/main.rs | 70 ++++++++++++++++++++++++++++++++++ hfuzz/test.sh | 9 +++++ 6 files changed, 150 insertions(+) create mode 100644 fuzz/fuzz_targets/load_spec.rs create mode 100644 hfuzz/.gitignore create mode 100644 hfuzz/Cargo.toml create mode 100644 hfuzz/src/main.rs create mode 100755 hfuzz/test.sh diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 52c3c5f..1dd173d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -12,9 +12,11 @@ cargo-fuzz = true wasmi = { path = ".." } wabt = "0.2.0" wasmparser = "0.14.1" +tempdir = "0.3.6" [dependencies.libfuzzer-sys] git = "https://github.com/rust-fuzz/libfuzzer-sys.git" +rev = "737524f7de1e85342b8b6cd1c01edc71018183ba" # Prevent this from interfering with workspaces [workspace] @@ -31,3 +33,7 @@ path = "fuzz_targets/load_wabt.rs" [[bin]] name = "load_wasmparser" path = "fuzz_targets/load_wasmparser.rs" + +[[bin]] +name = "load_spec" +path = "fuzz_targets/load_spec.rs" diff --git a/fuzz/fuzz_targets/load_spec.rs b/fuzz/fuzz_targets/load_spec.rs new file mode 100644 index 0000000..41a4aad --- /dev/null +++ b/fuzz/fuzz_targets/load_spec.rs @@ -0,0 +1,53 @@ +#![no_main] +#[macro_use] +extern crate libfuzzer_sys; +extern crate wabt; +extern crate wasmi; +extern crate tempdir; + +use std::fs::File; +use std::io::Write; +use std::process::{Command, Stdio}; + +fn run_spec(data: &[u8]) -> Result<(), ()> { + let temp_dir = tempdir::TempDir::new("spec").unwrap(); + let mut seed_path = temp_dir.path().to_path_buf(); + seed_path.push("test.wasm"); + + { + let mut seedfile = + File::create(&seed_path).expect("open temporary file for writing to store fuzzer input"); + seedfile.write_all(data).expect( + "write fuzzer input to temporary file", + ); + seedfile.flush().expect( + "flush fuzzer input to temporary file before starting wasm-opt", + ); + } + + let exit_status = Command::new("wasm") + .arg("-d") + .arg(&seed_path) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .expect("failed to execute `wasm`"); + + if exit_status.success() { + Ok(()) + } else { + Err(()) + } +} + +fn run_wasmi(data: &[u8]) -> Result<(), ()> { + let _ = wasmi::Module::from_buffer(data).map_err(|_| ())?; + Ok(()) +} + +fuzz_target!(|data: &[u8]| { + let wasmi_result = run_wasmi(data); + let wasm_result = run_spec(data); + + assert_eq!(wasmi_result.is_ok(), wasm_result.is_ok()); +}); diff --git a/hfuzz/.gitignore b/hfuzz/.gitignore new file mode 100644 index 0000000..e2e686c --- /dev/null +++ b/hfuzz/.gitignore @@ -0,0 +1,2 @@ +hfuzz_workspace/ +hfuzz_target/ diff --git a/hfuzz/Cargo.toml b/hfuzz/Cargo.toml new file mode 100644 index 0000000..473364f --- /dev/null +++ b/hfuzz/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "hfuzz" +version = "0.1.0" +authors = ["Sergey Pepyakin "] + +[dependencies] +honggfuzz = "=0.5.9" # Strict equal since hfuzz requires dep and cmd versions to match. +wasmi = { path = ".." } +tempdir = "0.3.6" +wabt = "0.2.0" diff --git a/hfuzz/src/main.rs b/hfuzz/src/main.rs new file mode 100644 index 0000000..c1ca84d --- /dev/null +++ b/hfuzz/src/main.rs @@ -0,0 +1,70 @@ +#[macro_use] extern crate honggfuzz; + +extern crate wabt; +extern crate wasmi; +extern crate tempdir; + +use std::fs::File; +use std::io::Write; +use std::process::{Command, Stdio}; + +fn dump_all_into_buf(src: &[u8], buf: &mut [u8; 64]) { + let common_len = usize::min(src.len(), buf.len()); + buf[0..common_len].copy_from_slice(&src[0..common_len]); +} + +fn run_spec(data: &[u8], stdout_msg_buf: &mut [u8; 64], stderr_msg_buf: &mut [u8; 64]) -> Result<(), ()> { + let temp_dir = tempdir::TempDir::new("spec").unwrap(); + let mut seed_path = temp_dir.path().to_path_buf(); + seed_path.push("test.wasm"); + + { + let mut seedfile = + File::create(&seed_path).expect("open temporary file for writing to store fuzzer input"); + seedfile.write_all(data).expect( + "write fuzzer input to temporary file", + ); + seedfile.flush().expect( + "flush fuzzer input to temporary file before starting wasm-opt", + ); + } + + let output = Command::new("wasm") + .arg("-d") + .arg(&seed_path) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .output() + .expect("failed to execute `wasm`"); + + if output.status.success() { + Ok(()) + } else { + dump_all_into_buf(&output.stdout, stdout_msg_buf); + dump_all_into_buf(&output.stderr, stderr_msg_buf); + Err(()) + } +} + +fn run_wasmi(data: &[u8]) -> Result<(), ()> { + let _ = wasmi::Module::from_buffer(data).map_err(|_| ())?; + Ok(()) +} + +fn main() { + loop { + fuzz!(|data: &[u8]| { + // Keep messages on stack. This should lead to a different stack hashes for + // different error messages. + let mut stdout_msg_buf: [u8; 64] = [0; 64]; + let mut stderr_msg_buf: [u8; 64] = [0; 64]; + + let wasmi_result = run_wasmi(data); + let wasm_result = run_spec(data, &mut stdout_msg_buf, &mut stderr_msg_buf); + + if wasmi_result.is_ok() != wasm_result.is_ok() { + panic!("stdout: {:?}, stderr: {:?}", &stdout_msg_buf[..], &stderr_msg_buf as &[u8]); + } + }); + } +} diff --git a/hfuzz/test.sh b/hfuzz/test.sh new file mode 100755 index 0000000..17fa42c --- /dev/null +++ b/hfuzz/test.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +export HFUZZ_RUN_ARGS="--max_file_size 2048" + +die() { echo "$*"; exit 1; } + +command -v wasm || die "spec interpreter 'wasm' is not on PATH"; + +rustup run nightly cargo hfuzz run hfuzz