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:
744
Cargo.lock
generated
744
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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'] }
|
||||||
|
|||||||
@@ -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"] }
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
100
crates/fuzzing/src/mutators.rs
Normal file
100
crates/fuzzing/src/mutators.rs
Normal 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)
|
||||||
|
}
|
||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
20
fuzz/fuzz_targets/compile-mutate.rs
Executable file
20
fuzz/fuzz_targets/compile-mutate.rs
Executable 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,
|
||||||
|
)
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user