Browse Source
* fuzzing: Add a custom mutator based on `wasm-mutate` * fuzz: Add a version of the `compile` fuzz target that uses `wasm-mutate` * Update `wasmparser` dependenciespull/3846/head
Nick Fitzgerald
3 years ago
committed by
GitHub
14 changed files with 526 additions and 390 deletions
File diff suppressed because it is too large
@ -0,0 +1,100 @@ |
|||
//! Custom fuzz input mutators.
|
|||
//!
|
|||
//! The functions in this module are intended to be used with [the
|
|||
//! `libfuzzer_sys::fuzz_mutator!` macro][fuzz-mutator].
|
|||
//!
|
|||
//! [fuzz-mutator]: https://docs.rs/libfuzzer-sys/latest/libfuzzer_sys/macro.fuzz_mutator.html
|
|||
|
|||
use arbitrary::{Arbitrary, Unstructured}; |
|||
use std::sync::Arc; |
|||
|
|||
/// Use [`wasm-mutate`][wasm-mutate] to mutate a fuzz input.
|
|||
///
|
|||
/// [wasm-mutate]: https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-mutate
|
|||
pub fn wasm_mutate( |
|||
data: &mut [u8], |
|||
size: usize, |
|||
max_size: usize, |
|||
seed: u32, |
|||
libfuzzer_mutate: fn(data: &mut [u8], size: usize, max_size: usize) -> usize, |
|||
) -> usize { |
|||
const MUTATION_FUEL: u64 = 100; |
|||
const MUTATION_ITERS: usize = 100; |
|||
|
|||
let wasm = &data[..size]; |
|||
|
|||
if wasmparser::validate(wasm).is_ok() { |
|||
let mut wasm_mutate = wasm_mutate::WasmMutate::default(); |
|||
wasm_mutate |
|||
.seed(seed.into()) |
|||
.fuel(MUTATION_FUEL) |
|||
.reduce(max_size < size) |
|||
.raw_mutate_func(Some(Arc::new(move |data, max_size| { |
|||
let len = data.len(); |
|||
|
|||
// The given max could be very large, so clamp it to no more
|
|||
// than `len * 2` in any single, given mutation. This way we
|
|||
// don't over-allocate a bunch of space.
|
|||
let max_size = std::cmp::min(max_size, len * 2); |
|||
// Also, the max must always be greater than zero (`libfuzzer`
|
|||
// asserts this).
|
|||
let max_size = std::cmp::max(max_size, 1); |
|||
|
|||
// Make sure we have capacity in case `libfuzzer` decides to
|
|||
// grow this data.
|
|||
if max_size > len { |
|||
data.resize(max_size, 0); |
|||
} |
|||
|
|||
// Finally, have `libfuzzer` mutate the data!
|
|||
let new_len = libfuzzer_mutate(data, len, max_size); |
|||
|
|||
// Resize the data to the mutated size, releasing any extra
|
|||
// capacity that we don't need anymore.
|
|||
data.resize(new_len, 0); |
|||
data.shrink_to_fit(); |
|||
|
|||
Ok(()) |
|||
}))); |
|||
|
|||
let wasm = wasm.to_vec(); |
|||
let mutations = wasm_mutate.run(&wasm); |
|||
if let Ok(mutations) = mutations { |
|||
for mutation in mutations.take(MUTATION_ITERS) { |
|||
if let Ok(mutated_wasm) = mutation { |
|||
if mutated_wasm.len() <= max_size { |
|||
data[..mutated_wasm.len()].copy_from_slice(&mutated_wasm); |
|||
return mutated_wasm.len(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// If we can't mutate the input because it isn't valid Wasm or `wasm-mutate`
|
|||
// otherwise fails, try to use `wasm-smith` to generate a new, arbitrary
|
|||
// Wasm module that fits within the max-size limit.
|
|||
let mut u = Unstructured::new(&data[..max_size]); |
|||
if let Ok(module) = wasm_smith::Module::arbitrary(&mut u) { |
|||
let wasm = module.to_bytes(); |
|||
if wasm.len() <= max_size { |
|||
data[..wasm.len()].copy_from_slice(&wasm); |
|||
return wasm.len(); |
|||
} |
|||
} |
|||
|
|||
// Otherwise, try to return an empty Wasm module:
|
|||
//
|
|||
// ```
|
|||
// (module)
|
|||
// ```
|
|||
static EMPTY_WASM: &[u8] = &[0x00, b'a', b's', b'm', 0x01, 0x00, 0x00, 0x00]; |
|||
if EMPTY_WASM.len() <= max_size { |
|||
data[..EMPTY_WASM.len()].copy_from_slice(EMPTY_WASM); |
|||
return EMPTY_WASM.len(); |
|||
} |
|||
|
|||
// If the max size is even smaller than an empty Wasm module, then just let
|
|||
// `libfuzzer` mutate the data.
|
|||
libfuzzer_mutate(data, size, max_size) |
|||
} |
@ -0,0 +1,20 @@ |
|||
#![no_main] |
|||
|
|||
use libfuzzer_sys::{fuzz_mutator, fuzz_target}; |
|||
use wasmtime::{Engine, Module}; |
|||
|
|||
fuzz_target!(|data: &[u8]| { |
|||
let engine = Engine::default(); |
|||
wasmtime_fuzzing::oracles::log_wasm(data); |
|||
drop(Module::new(&engine, data)); |
|||
}); |
|||
|
|||
fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| { |
|||
wasmtime_fuzzing::mutators::wasm_mutate( |
|||
data, |
|||
size, |
|||
max_size, |
|||
seed, |
|||
libfuzzer_sys::fuzzer_mutate, |
|||
) |
|||
}); |
Loading…
Reference in new issue