wasm-mutate fuzz targets (#3836)

* fuzzing: Add a custom mutator based on `wasm-mutate`

* fuzz: Add a version of the `compile` fuzz target that uses `wasm-mutate`

* Update `wasmparser` dependencies
This commit is contained in:
Nick Fitzgerald
2022-02-23 12:14:11 -08:00
committed by GitHub
parent 434e35c490
commit bad9a35418
14 changed files with 527 additions and 391 deletions

744
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,7 @@ file-per-thread-logger = "0.1.1"
libc = "0.2.60" libc = "0.2.60"
rayon = "1.5.0" rayon = "1.5.0"
humantime = "2.0.0" humantime = "2.0.0"
wasmparser = "0.82.0" wasmparser = "0.83.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
listenfd = "0.3.5" listenfd = "0.3.5"

View File

@@ -12,7 +12,7 @@ keywords = ["webassembly", "wasm"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmparser = { version = "0.82", default-features = false } wasmparser = { version = "0.83.0", default-features = false }
cranelift-codegen = { path = "../codegen", version = "0.81.0", default-features = false } cranelift-codegen = { path = "../codegen", version = "0.81.0", default-features = false }
cranelift-entity = { path = "../entity", version = "0.81.0" } cranelift-entity = { path = "../entity", version = "0.81.0" }
cranelift-frontend = { path = "../frontend", version = "0.81.0", default-features = false } cranelift-frontend = { path = "../frontend", version = "0.81.0", default-features = false }

View File

@@ -2013,23 +2013,23 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::ReturnCall { .. } | Operator::ReturnCallIndirect { .. } => { Operator::ReturnCall { .. } | Operator::ReturnCallIndirect { .. } => {
return Err(wasm_unsupported!("proposed tail-call operator {:?}", op)); return Err(wasm_unsupported!("proposed tail-call operator {:?}", op));
} }
Operator::I8x16SwizzleRelaxed Operator::I8x16RelaxedSwizzle
| Operator::I32x4TruncSatF32x4SRelaxed | Operator::I32x4RelaxedTruncSatF32x4S
| Operator::I32x4TruncSatF32x4URelaxed | Operator::I32x4RelaxedTruncSatF32x4U
| Operator::I32x4TruncSatF64x2SZeroRelaxed | Operator::I32x4RelaxedTruncSatF64x2SZero
| Operator::I32x4TruncSatF64x2UZeroRelaxed | Operator::I32x4RelaxedTruncSatF64x2UZero
| Operator::F32x4FmaRelaxed | Operator::F32x4Fma
| Operator::F32x4FmsRelaxed | Operator::F32x4Fms
| Operator::F64x2FmaRelaxed | Operator::F64x2Fma
| Operator::F64x2FmsRelaxed | Operator::F64x2Fms
| Operator::I8x16LaneSelect | Operator::I8x16LaneSelect
| Operator::I16x8LaneSelect | Operator::I16x8LaneSelect
| Operator::I32x4LaneSelect | Operator::I32x4LaneSelect
| Operator::I64x2LaneSelect | Operator::I64x2LaneSelect
| Operator::F32x4MinRelaxed | Operator::F32x4RelaxedMin
| Operator::F64x2MinRelaxed | Operator::F32x4RelaxedMax
| Operator::F32x4MaxRelaxed | Operator::F64x2RelaxedMin
| Operator::F64x2MaxRelaxed => { | Operator::F64x2RelaxedMax => {
return Err(wasm_unsupported!("proposed relaxed-simd operator {:?}", op)); return Err(wasm_unsupported!("proposed relaxed-simd operator {:?}", op));
} }
}; };

View File

@@ -19,7 +19,7 @@ cranelift-codegen = { path = "../../cranelift/codegen", version = "0.81.0" }
cranelift-frontend = { path = "../../cranelift/frontend", version = "0.81.0" } cranelift-frontend = { path = "../../cranelift/frontend", version = "0.81.0" }
cranelift-entity = { path = "../../cranelift/entity", version = "0.81.0" } cranelift-entity = { path = "../../cranelift/entity", version = "0.81.0" }
cranelift-native = { path = "../../cranelift/native", version = "0.81.0" } cranelift-native = { path = "../../cranelift/native", version = "0.81.0" }
wasmparser = "0.82.0" wasmparser = "0.83.0"
target-lexicon = "0.12" target-lexicon = "0.12"
gimli = { version = "0.26.0", default-features = false, features = ['read', 'std'] } gimli = { version = "0.26.0", default-features = false, features = ['read', 'std'] }
object = { version = "0.27.0", default-features = false, features = ['write'] } object = { version = "0.27.0", default-features = false, features = ['write'] }

View File

@@ -14,7 +14,7 @@ edition = "2018"
anyhow = "1.0" anyhow = "1.0"
cranelift-entity = { path = "../../cranelift/entity", version = "0.81.0" } cranelift-entity = { path = "../../cranelift/entity", version = "0.81.0" }
wasmtime-types = { path = "../types", version = "0.34.0" } wasmtime-types = { path = "../types", version = "0.34.0" }
wasmparser = "0.82" wasmparser = "0.83.0"
indexmap = { version = "1.0.2", features = ["serde-1"] } indexmap = { version = "1.0.2", features = ["serde-1"] }
thiserror = "1.0.4" thiserror = "1.0.4"
serde = { version = "1.0.94", features = ["derive"] } serde = { version = "1.0.94", features = ["derive"] }

View File

@@ -15,12 +15,13 @@ log = "0.4.8"
rayon = "1.2.1" rayon = "1.2.1"
target-lexicon = "0.12.3" target-lexicon = "0.12.3"
tempfile = "3.3.0" tempfile = "3.3.0"
wasmparser = "0.82" wasmparser = "0.83.0"
wasmprinter = "0.2.32" wasmprinter = "0.2.32"
wasmtime = { path = "../wasmtime" } wasmtime = { path = "../wasmtime" }
wasmtime-wast = { path = "../wast" } wasmtime-wast = { path = "../wast" }
wasm-encoder = "0.10.0" wasm-encoder = "0.10.0"
wasm-smith = "0.9.0" wasm-smith = "0.9.0"
wasm-mutate = "0.2"
wasm-spec-interpreter = { path = "./wasm-spec-interpreter" } wasm-spec-interpreter = { path = "./wasm-spec-interpreter" }
wasmi = "0.7.0" wasmi = "0.7.0"

View File

@@ -2,8 +2,10 @@
#![deny(missing_docs)] #![deny(missing_docs)]
pub use wasm_mutate;
pub use wasm_smith; pub use wasm_smith;
pub mod generators; pub mod generators;
pub mod mutators;
pub mod oracles; pub mod oracles;
/// One time start up initialization for fuzzing: /// One time start up initialization for fuzzing:

View File

@@ -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)
}

View File

@@ -12,4 +12,4 @@ edition = "2018"
cranelift-entity = { path = "../../cranelift/entity", version = "0.81.0", features = ['enable-serde'] } cranelift-entity = { path = "../../cranelift/entity", version = "0.81.0", features = ['enable-serde'] }
serde = { version = "1.0.94", features = ["derive"] } serde = { version = "1.0.94", features = ["derive"] }
thiserror = "1.0.4" thiserror = "1.0.4"
wasmparser = { version = "0.82", default-features = false } wasmparser = { version = "0.83.0", default-features = false }

View File

@@ -20,7 +20,7 @@ wasmtime-cache = { path = "../cache", version = "=0.34.0", optional = true }
wasmtime-fiber = { path = "../fiber", version = "=0.34.0", optional = true } wasmtime-fiber = { path = "../fiber", version = "=0.34.0", optional = true }
wasmtime-cranelift = { path = "../cranelift", version = "=0.34.0", optional = true } wasmtime-cranelift = { path = "../cranelift", version = "=0.34.0", optional = true }
target-lexicon = { version = "0.12.0", default-features = false } target-lexicon = { version = "0.12.0", default-features = false }
wasmparser = "0.82" wasmparser = "0.83.0"
anyhow = "1.0.19" anyhow = "1.0.19"
region = "2.2.0" region = "2.2.0"
libc = "0.2" libc = "0.2"

View File

@@ -98,6 +98,11 @@ impl From<&wasmparser::WasmFeatures> for WasmFeatures {
memory64, memory64,
relaxed_simd, relaxed_simd,
extended_const, extended_const,
// Always on; we don't currently have knobs for these.
mutable_global: _,
saturating_float_to_int: _,
sign_extension: _,
} = *other; } = *other;
Self { Self {

View File

@@ -32,6 +32,12 @@ path = "fuzz_targets/compile.rs"
test = false test = false
doc = false doc = false
[[bin]]
name = "compile-mutate"
path = "fuzz_targets/compile-mutate.rs"
test = false
doc = false
[[bin]] [[bin]]
name = "instantiate" name = "instantiate"
path = "fuzz_targets/instantiate.rs" path = "fuzz_targets/instantiate.rs"

View File

@@ -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,
)
});