Add the ability to dump the raw wasm bytes controlled by an environment variable.

Ordinarily when the fuzzer fails it will use the wasmprinter to dump the plain text of the wasm as part of the failure. Also you can use `cargo fuzz fmt --features=... fuzzer_name /path/to/artifact` to dump it as text again. In the event that dumping as text fails (either crashes or fails to replicate the problem), you can use the DUMP_TEST_CASE environment variable to get the raw wasm bytes as documented in the readme.
This commit is contained in:
Nick Lewycky
2021-04-16 13:59:32 -07:00
parent 6c00c35e71
commit f0e5f2fdf4
7 changed files with 55 additions and 16 deletions

View File

@ -40,22 +40,8 @@ It will continue to generate random inputs forever, until it finds a bug or is t
## The corpus
Each fuzzer has an individual corpus under fuzz/corpus/test_name, created on first run if not already present. The validate fuzzer works directly with `.wasm` files as bytes and works best if seeded with examples of small Wasm file. Using `wast2json` from [wabt](https://github.com/WebAssembly/wabt), we can easily produce `.wasm` files out of the WebAssembly spec tests.
Each fuzzer has an individual corpus under fuzz/corpus/test_name, created on first run if not already present. The fuzzers use `wasm-smith` which means that the testcase files are random number seeds input to the wasm generator, not `.wasm` files themselves. In order to debug a testcase, you may find that you need to convert it into a `.wasm` file. Using the standalone `wasm-smith` tool doesn't work for this purpose because we use a custom configuration to our `wasm_smith::Module`. Instead, our fuzzers use an environment variable `DUMP_TESTCASE=path`. For example:
```sh
mkdir spec-test-corpus
for i in `find tests/ -name "*.wast"`; do wast2json --enable-all $i -o spec-test-corpus/$(basename $i).json; done
mv spec-test-corpus/*.wasm fuzz/corpus/validate/
rm -r spec-test-corpus
```
The others fuzzers use `wasm-smith` which means that the testcase files are the input to the wasm generator, not the valid `.wasm` bytes themselves. In order to debug a testcase, you may find that you need to convert it into a `.wasm` file. Using the standalone `wasm-smith` tool doesn't work for this purpose because we use a custom configuration to our `wasm_smith::Module`. Instead, add some code to the fuzzer target:
```rust
use std::fs::File;
use std::io::Write;
let mut file = File::create("/tmp/crash.wasm").unwrap();
file.write_all(&wasm_bytes).unwrap();
DUMP_TESTCASE=/tmp/crash.wasm cargo fuzz run --features=jit,singlepass jit_singlepass fuzz/artifacts/jit_singlepass/crash-0966412eab4f89c52ce5d681807c8030349470f6
```
and run it over just the one testcase.

View File

@ -172,6 +172,14 @@ fn evaluate_instance(instance: Result<Instance>) -> InstanceResult {
fuzz_target!(|module: WasmSmithModule| {
let wasm_bytes = module.0.to_bytes();
if let Ok(path) = std::env::var("DUMP_TESTCASE") {
use std::fs::File;
use std::io::Write;
let mut file = File::create(path).unwrap();
file.write_all(&wasm_bytes).unwrap();
return;
}
#[cfg(feature = "singlepass")]
let singlepass = maybe_instantiate_singlepass(&wasm_bytes)
.transpose()

View File

@ -30,6 +30,15 @@ impl std::fmt::Debug for WasmSmithModule {
fuzz_target!(|module: WasmSmithModule| {
let wasm_bytes = module.0.to_bytes();
if let Ok(path) = std::env::var("DUMP_TESTCASE") {
use std::fs::File;
use std::io::Write;
let mut file = File::create(path).unwrap();
file.write_all(&wasm_bytes).unwrap();
return;
}
let mut compiler = Cranelift::default();
compiler.canonicalize_nans(true);
compiler.enable_verifier();

View File

@ -30,6 +30,15 @@ impl std::fmt::Debug for WasmSmithModule {
fuzz_target!(|module: WasmSmithModule| {
let wasm_bytes = module.0.to_bytes();
if let Ok(path) = std::env::var("DUMP_TESTCASE") {
use std::fs::File;
use std::io::Write;
let mut file = File::create(path).unwrap();
file.write_all(&wasm_bytes).unwrap();
return;
}
let mut compiler = LLVM::default();
compiler.canonicalize_nans(true);
compiler.enable_verifier();

View File

@ -30,6 +30,15 @@ impl std::fmt::Debug for WasmSmithModule {
fuzz_target!(|module: WasmSmithModule| {
let wasm_bytes = module.0.to_bytes();
if let Ok(path) = std::env::var("DUMP_TESTCASE") {
use std::fs::File;
use std::io::Write;
let mut file = File::create(path).unwrap();
file.write_all(&wasm_bytes).unwrap();
return;
}
let compiler = Singlepass::default();
let store = Store::new(&JIT::new(compiler).engine());
let module = Module::new(&store, &wasm_bytes);

View File

@ -42,6 +42,15 @@ fn cost(operator: &Operator) -> u64 {
fuzz_target!(|module: WasmSmithModule| {
let wasm_bytes = module.0.to_bytes();
if let Ok(path) = std::env::var("DUMP_TESTCASE") {
use std::fs::File;
use std::io::Write;
let mut file = File::create(path).unwrap();
file.write_all(&wasm_bytes).unwrap();
return;
}
let mut compiler = Cranelift::default();
compiler.canonicalize_nans(true);
compiler.enable_verifier();

View File

@ -31,6 +31,15 @@ impl std::fmt::Debug for WasmSmithModule {
fuzz_target!(|module: WasmSmithModule| {
let serialized = {
let wasm_bytes = module.0.to_bytes();
if let Ok(path) = std::env::var("DUMP_TESTCASE") {
use std::fs::File;
use std::io::Write;
let mut file = File::create(path).unwrap();
file.write_all(&wasm_bytes).unwrap();
return;
}
let compiler = Cranelift::default();
let store = Store::new(&Native::new(compiler).engine());
let module = Module::new(&store, &wasm_bytes).unwrap();