... but turn it back on in CI by default. The `binaryen-sys` crate builds binaryen from source, which is a drag on CI for a few reasons: * This is quite large and takes a good deal of time to build * The debug build directory for binaryen is 4GB large In an effort to both save time and disk space on the builders this commit adds a `binaryen` feature to the `wasmtime-fuzz` crate. This feature is enabled specifically when running the fuzzers on CI, but it is disabled during the typical `cargo test --all` command. This means that the test builders should save an extra 4G of space and be a bit speedier now that they don't build a giant wad of C++. We'll need to update the OSS-fuzz integration to enable the `binaryen` feature when executing `cargo fuzz build`, and I'll do that once this gets closer to landing.
96 lines
3.1 KiB
Rust
96 lines
3.1 KiB
Rust
//! Test case generators.
|
|
//!
|
|
//! Test case generators take raw, unstructured input from a fuzzer
|
|
//! (e.g. libFuzzer) and translate that into a structured test case (e.g. a
|
|
//! valid Wasm binary).
|
|
//!
|
|
//! These are generally implementations of the `Arbitrary` trait, or some
|
|
//! wrapper over an external tool, such that the wrapper implements the
|
|
//! `Arbitrary` trait for the wrapped external tool.
|
|
|
|
#[cfg(feature = "binaryen")]
|
|
pub mod api;
|
|
|
|
use arbitrary::Arbitrary;
|
|
|
|
/// A Wasm test case generator that is powered by Binaryen's `wasm-opt -ttf`.
|
|
#[derive(Clone)]
|
|
#[cfg(feature = "binaryen")]
|
|
pub struct WasmOptTtf {
|
|
/// The raw, encoded Wasm bytes.
|
|
pub wasm: Vec<u8>,
|
|
}
|
|
|
|
#[cfg(feature = "binaryen")]
|
|
impl std::fmt::Debug for WasmOptTtf {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
write!(
|
|
f,
|
|
"WasmOptTtf {{ wasm: wat::parse_str(r###\"\n{}\n\"###).unwrap() }}",
|
|
wasmprinter::print_bytes(&self.wasm).expect("valid wasm should always disassemble")
|
|
)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "binaryen")]
|
|
impl Arbitrary for WasmOptTtf {
|
|
fn arbitrary(input: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
|
|
crate::init_fuzzing();
|
|
let seed: Vec<u8> = Arbitrary::arbitrary(input)?;
|
|
let module = binaryen::tools::translate_to_fuzz_mvp(&seed);
|
|
let wasm = module.write();
|
|
Ok(WasmOptTtf { wasm })
|
|
}
|
|
|
|
fn arbitrary_take_rest(input: arbitrary::Unstructured) -> arbitrary::Result<Self> {
|
|
crate::init_fuzzing();
|
|
let seed: Vec<u8> = Arbitrary::arbitrary_take_rest(input)?;
|
|
let module = binaryen::tools::translate_to_fuzz_mvp(&seed);
|
|
let wasm = module.write();
|
|
Ok(WasmOptTtf { wasm })
|
|
}
|
|
|
|
fn size_hint(depth: usize) -> (usize, Option<usize>) {
|
|
<Vec<u8> as Arbitrary>::size_hint(depth)
|
|
}
|
|
}
|
|
|
|
/// A description of configuration options that we should do differential
|
|
/// testing between.
|
|
#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct DifferentialConfig {
|
|
strategy: DifferentialStrategy,
|
|
opt_level: DifferentialOptLevel,
|
|
}
|
|
|
|
impl DifferentialConfig {
|
|
/// Convert this differential fuzzing config into a `wasmtime::Config`.
|
|
pub fn to_wasmtime_config(&self) -> anyhow::Result<wasmtime::Config> {
|
|
let mut config = wasmtime::Config::new();
|
|
config.cranelift_debug_verifier(true);
|
|
config.strategy(match self.strategy {
|
|
DifferentialStrategy::Cranelift => wasmtime::Strategy::Cranelift,
|
|
DifferentialStrategy::Lightbeam => wasmtime::Strategy::Lightbeam,
|
|
})?;
|
|
config.cranelift_opt_level(match self.opt_level {
|
|
DifferentialOptLevel::None => wasmtime::OptLevel::None,
|
|
DifferentialOptLevel::Speed => wasmtime::OptLevel::Speed,
|
|
DifferentialOptLevel::SpeedAndSize => wasmtime::OptLevel::SpeedAndSize,
|
|
});
|
|
Ok(config)
|
|
}
|
|
}
|
|
|
|
#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
|
|
enum DifferentialStrategy {
|
|
Cranelift,
|
|
Lightbeam,
|
|
}
|
|
|
|
#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
|
|
enum DifferentialOptLevel {
|
|
None,
|
|
Speed,
|
|
SpeedAndSize,
|
|
}
|