wasm-mutate
wasm-mutate is a new tool for fuzzing Wasm compilers, runtimes, validators, and other Wasm-consuming programs.
Add this to your Cargo.toml
:
toml
[dependencies]
wasm-mutate = "0.2.0"
You can mutate a WebAssembly binary by using the cli tool:
bash
./wasm-mutate original.wasm --seed 0 -o out.wasm --preserve-semantics
wasm-mutate
has the ability to
only apply semantics-preserving changes to the input Wasm module. When it is
used in this mode, the mutated Wasm computes identical results when
given the same inputs as the original Wasm module.wasm-mutate
is deterministic, i.e., given the same input
Wasm module and the same seed, it always produces the same mutated
output Wasm module.libfuzzer integration: wasm-mutate
integrates well with mutation-based fuzzers like libFuzzer. It
reuses the fuzzer's raw input strings. wasm-mutate
works with the
LLVMFuzzerCustomMutator
hook and the
libfuzzer_sys::fuzz_mutator!
macro.
```rust
use libfuzzersys::{fuzzmutator, fuzz_target}; use std::io::{BufRead, Read, Write}; use wasmparser::WasmFeatures;
fuzz_target!(|bytes: &[u8]| { // Initialize the Wasm for example });
fuzzmutator!(|data: &mut [u8], size: usize, maxsize: usize, seed: u32| {
// Generate a random Wasm module with wasm-smith
as well as a RNG seed for
let wasm = &data[..size];
let features = WasmFeatures::default();
let mut validator = wasmparser::Validator::new();
validator.wasmfeatures(features);
let validationresult = validator.validate_all(&wasm);
// Mutate the data if its a valid Wasm file, otherwise, create a random one let wasm = if validationresult.isok() { wasm.tovec() } else { let (w, _) = match wasmtoolsfuzz::generatevalidmodulefromseed(seed, |config, u| { config.exceptionsenabled = false; config.simdenabled = false; config.referencetypesenabled = false; config.memory64enabled = false; config.maxmemories = 1; Ok(()) }) { Ok(m) => m, Err() => { return size; } }; w };
let mutatedwasm = wasmmutate::WasmMutate::default() .seed(seed.into()) .fuel(1000) .preserve_semantics(true) .run(&wasm);
let mutatedwasm = match mutatedwasm { Ok(w) => w, Err(_) => wasm, };
// The mutated Wasm should still be valid, since the input Wasm was valid. let newsize = mutatedwasm.len(); data[..newsize].copyfromslice(&mutatedwasm[..newsize]); newsize });
```
test case reduction (WIP): wasm-mutate
can have the ability to restrict
mutations to only those that shrink the size of the Wasm module. If it is used
in this mode, wasm-mutate
essentially becomes a Wasm test-case reducer. We
are currently working to provide a prototype of this feature as a separate
binary. The following pseudo-Rust provides the general picture of it as an
standard hill-climbing algorithm.
```rust let wasmmutate = wasmmutate::WasmMutate::default() .seed(seed) .fuel(1000) .preservesemantics(true) .reduce(true);
while MAXITERATIONS > 0 { let newwasm = wasmmutate.run(&wasm); wasm = if checkequivalence(newwasm, wasm) { wasm } else{ panic!("No valid transformations") } MAX_ITERATIONS -= 1; }
return wasm
```
This project is licensed under the Apache 2.0 license with the LLVM exception. See LICENSE for more details.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.