Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 91 additions & 91 deletions Cargo.lock

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -230,18 +230,18 @@ io-extras = "0.18.0"
rustix = "0.38.21"
is-terminal = "0.4.0"
# wit-bindgen:
wit-bindgen = { version = "0.15.0", default-features = false }
wit-bindgen = { version = "0.17.0", default-features = false }

# wasm-tools family:
wasmparser = "0.121.0"
wat = "1.0.85"
wast = "70.0.2"
wasmprinter = "0.2.78"
wasm-encoder = "0.41.0"
wasm-smith = "0.15.1"
wasm-mutate = "0.2.46"
wit-parser = "0.13.1"
wit-component = "0.20.1"
wasmparser = "0.121.2"
wat = "1.0.88"
wast = "71.0.1"
wasmprinter = "0.2.80"
wasm-encoder = "0.41.2"
wasm-smith = "0.16.1"
wasm-mutate = "0.2.48"
wit-parser = "0.14.0"
wit-component = "0.21.0"

# Non-Bytecode Alliance maintained dependencies:
# --------------------------
Expand Down
2 changes: 1 addition & 1 deletion crates/fuzzing/src/generators/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl ModuleConfig {
let mut module = wasm_smith::Module::new(self.config.clone(), input)?;

if let Some(default_fuel) = default_fuel {
module.ensure_termination(default_fuel);
module.ensure_termination(default_fuel).unwrap();
}

Ok(module)
Expand Down
16 changes: 11 additions & 5 deletions crates/fuzzing/src/oracles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod stacks;
use self::diff_wasmtime::WasmtimeInstance;
use self::engine::{DiffEngine, DiffInstance};
use crate::generators::{self, DiffValue, DiffValueType};
use crate::single_module_fuzzer::KnownValid;
use arbitrary::Arbitrary;
pub use stacks::check_stacks;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
Expand Down Expand Up @@ -135,7 +136,12 @@ pub enum Timeout {
/// panic or segfault or anything else that can be detected "passively".
///
/// The engine will be configured using provided config.
pub fn instantiate(wasm: &[u8], known_valid: bool, config: &generators::Config, timeout: Timeout) {
pub fn instantiate(
wasm: &[u8],
known_valid: KnownValid,
config: &generators::Config,
timeout: Timeout,
) {
let mut store = config.to_store();

let module = match compile_module(store.engine(), wasm, known_valid, config) {
Expand Down Expand Up @@ -193,7 +199,7 @@ pub enum Command {
/// The modules are expected to *not* have start functions as no timeouts are configured.
pub fn instantiate_many(
modules: &[Vec<u8>],
known_valid: bool,
known_valid: KnownValid,
config: &generators::Config,
commands: &[Command],
) {
Expand Down Expand Up @@ -246,13 +252,13 @@ pub fn instantiate_many(
fn compile_module(
engine: &Engine,
bytes: &[u8],
known_valid: bool,
known_valid: KnownValid,
config: &generators::Config,
) -> Option<Module> {
log_wasm(bytes);
match config.compile(engine, bytes) {
Ok(module) => Some(module),
Err(_) if !known_valid => None,
Err(_) if known_valid == KnownValid::No => None,
Err(e) => {
if let generators::InstanceAllocationStrategy::Pooling(c) = &config.wasmtime.strategy {
// When using the pooling allocator, accept failures to compile
Expand Down Expand Up @@ -602,7 +608,7 @@ pub fn table_ops(

let wasm = ops.to_wasm_binary();
log_wasm(&wasm);
let module = match compile_module(store.engine(), &wasm, false, &fuzz_config) {
let module = match compile_module(store.engine(), &wasm, KnownValid::No, &fuzz_config) {
Some(m) => m,
None => return 0,
};
Expand Down
3 changes: 2 additions & 1 deletion crates/fuzzing/src/oracles/diff_wasmtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::generators::{self, DiffValue, DiffValueType, WasmtimeConfig};
use crate::oracles::dummy;
use crate::oracles::engine::DiffInstance;
use crate::oracles::{compile_module, engine::DiffEngine, StoreLimits};
use crate::single_module_fuzzer::KnownValid;
use anyhow::{Context, Error, Result};
use arbitrary::Unstructured;
use wasmtime::{Extern, FuncType, Instance, Module, Store, Trap, Val};
Expand Down Expand Up @@ -36,7 +37,7 @@ impl DiffEngine for WasmtimeEngine {

fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
let store = self.config.to_store();
let module = compile_module(store.engine(), wasm, true, &self.config).unwrap();
let module = compile_module(store.engine(), wasm, KnownValid::Yes, &self.config).unwrap();
let instance = WasmtimeInstance::new(store, module)?;
Ok(Box::new(instance))
}
Expand Down
44 changes: 32 additions & 12 deletions crates/fuzzing/src/single_module_fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ macro_rules! single_module_fuzzer {
/// module.
pub fn execute<'a, T, U>(
input: &'a [u8],
run: fn(&[u8], bool, T, &mut Unstructured<'a>) -> Result<U>,
gen_module: fn(&mut T, &mut Unstructured<'a>) -> Result<Vec<u8>>,
run: fn(&[u8], KnownValid, T, &mut Unstructured<'a>) -> Result<U>,
gen_module: fn(&mut T, &mut Unstructured<'a>) -> Result<(Vec<u8>, KnownValid)>,
) -> Result<U>
where
T: Arbitrary<'a>,
Expand All @@ -113,12 +113,29 @@ where
};
let mut u = Unstructured::new(fuzz_data);
let mut config = u.arbitrary()?;
let generated = gen_module(&mut config, &mut u)?;
let (generated, known_valid) = gen_module(&mut config, &mut u)?;
let module = module_in_input.unwrap_or(&generated);
if let Ok(file) = std::env::var("WRITE_FUZZ_INPUT_TO") {
std::fs::write(file, encode_module(&module, &fuzz_data)).unwrap();
}
run(module, module_in_input.is_none(), config, &mut u)
let known_valid = if module_in_input.is_some() {
KnownValid::No
} else {
known_valid
};
run(module, known_valid, config, &mut u)
}

/// Used as part of `execute` above to determine whether a module is known to
/// be valid ahead of time.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum KnownValid {
/// This module is known to be valid so it should assert compilation
/// succeeds for example.
Yes,
/// This module is not known to be valid and it may not compile
/// successfully. Note that it's also not known to compile unsuccessfully.
No,
}

const SECTION_NAME: &str = "wasmtime-fuzz-input";
Expand All @@ -133,7 +150,7 @@ pub fn mutate<T>(
data: &mut [u8],
mut size: usize,
max_size: usize,
gen_module: fn(&mut T, &mut Unstructured<'_>) -> Result<Vec<u8>>,
gen_module: fn(&mut T, &mut Unstructured<'_>) -> Result<(Vec<u8>, KnownValid)>,
mutate: fn(&mut [u8], usize, usize) -> usize,
) -> usize
where
Expand Down Expand Up @@ -170,7 +187,7 @@ where
.arbitrary()
.and_then(|mut config| gen_module(&mut config, &mut u))
{
Ok(module) => {
Ok((module, _known_valid)) => {
let module = encode_module(&module, &data[..new_size]);

if module.len() < max_size {
Expand Down Expand Up @@ -295,10 +312,10 @@ mod tests {
let run2 = run_config::<(u32, u32)>;

if let Ok((module, known_valid)) = execute(&buf[..seed_size], run1, gen) {
assert!(known_valid);
assert_eq!(known_valid, KnownValid::Yes);
let new_size = mutate(&mut buf, seed_size, max_size, gen, noop_mutate);
if let Ok((module2, known_valid)) = execute(&buf[..new_size], run2, gen) {
assert!(!known_valid);
assert_eq!(known_valid, KnownValid::No);
compares += 1;
if module != module2 {
panic!("modules differ");
Expand All @@ -313,21 +330,24 @@ mod tests {

fn run_config<T>(
data: &[u8],
known_valid: bool,
known_valid: KnownValid,
_: T,
_: &mut Unstructured<'_>,
) -> Result<(Vec<u8>, bool)>
) -> Result<(Vec<u8>, KnownValid)>
where
T: for<'a> Arbitrary<'a>,
{
Ok((data.to_vec(), known_valid))
}

fn gen<T>(_: &mut T, u: &mut Unstructured<'_>) -> Result<Vec<u8>>
fn gen<T>(_: &mut T, u: &mut Unstructured<'_>) -> Result<(Vec<u8>, KnownValid)>
where
T: for<'a> Arbitrary<'a>,
{
Ok(u.arbitrary::<wasm_smith::Module>()?.to_bytes())
Ok((
u.arbitrary::<wasm_smith::Module>()?.to_bytes(),
KnownValid::Yes,
))
}

fn noop_mutate(_buf: &mut [u8], size: usize, _new_size: usize) -> usize {
Expand Down
6 changes: 0 additions & 6 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,6 @@ path = "fuzz_targets/stacks.rs"
test = false
doc = false

[[bin]]
name = "compile-maybe-invalid"
path = "fuzz_targets/compile-maybe-invalid.rs"
test = false
doc = false

[[bin]]
name = "cranelift-fuzzgen"
path = "fuzz_targets/cranelift-fuzzgen.rs"
Expand Down
12 changes: 0 additions & 12 deletions fuzz/fuzz_targets/compile-maybe-invalid.rs

This file was deleted.

3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/instantiate-many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use libfuzzer_sys::arbitrary::{Result, Unstructured};
use libfuzzer_sys::fuzz_target;
use wasmtime_fuzzing::single_module_fuzzer::KnownValid;
use wasmtime_fuzzing::{generators, oracles};

const MAX_MODULES: usize = 5;
Expand Down Expand Up @@ -56,7 +57,7 @@ fn execute_one(data: &[u8]) -> Result<()> {
.collect::<Result<Vec<_>>>()?,
);

oracles::instantiate_many(&modules, true, &config, &commands);
oracles::instantiate_many(&modules, KnownValid::Yes, &config, &commands);

Ok(())
}
44 changes: 32 additions & 12 deletions fuzz/fuzz_targets/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use libfuzzer_sys::arbitrary::{Arbitrary, Result, Unstructured};
use wasmtime_fuzzing::generators::Config;
use wasmtime_fuzzing::oracles::{instantiate, Timeout};
use wasmtime_fuzzing::single_module_fuzzer::KnownValid;

wasmtime_fuzzing::single_module_fuzzer!(execute gen_module);

Expand Down Expand Up @@ -31,29 +32,48 @@ impl<'a> Arbitrary<'a> for InstantiateInput {

fn execute(
module: &[u8],
known_valid: bool,
known_valid: KnownValid,
mut input: InstantiateInput,
u: &mut Unstructured<'_>,
) -> Result<()> {
let timeout = match input.timeout {
// If the input module isn't a "known valid" module then it can't be
// relied on self-regulating itself, so force a timeout via epochs/fuel
// in the configuration.
Timeout::None if !known_valid => input.config.generate_timeout(u)?,
Timeout::None if known_valid == KnownValid::No => input.config.generate_timeout(u)?,
other => other,
};
instantiate(module, known_valid, &input.config, timeout);
Ok(())
}

fn gen_module(input: &mut InstantiateInput, u: &mut Unstructured<'_>) -> Result<Vec<u8>> {
let module = input.config.generate(
u,
if let Timeout::None = input.timeout {
Some(1000)
} else {
None
},
)?;
Ok(module.to_bytes())
fn gen_module(
input: &mut InstantiateInput,
u: &mut Unstructured<'_>,
) -> Result<(Vec<u8>, KnownValid)> {
// With a small-ish chance take raw fuzz input and put it in the module to
// stress module compilation/validation. In such a situation we can't use
// `ensure_termination` in wasm-smith so list the timeout as `None` to time
// out via epochs or Wasmtime-level fuel.
//
// Otherwise though if no timeout is configured use wasm-smith fuel to
// ensure termination.
let allow_invalid_funcs = u.ratio(1, 10)?;

let default_fuel = if allow_invalid_funcs {
input.config.module_config.config.allow_invalid_funcs = true;
input.timeout = Timeout::None;
None
} else if let Timeout::None = input.timeout {
Some(1000)
} else {
None
};
let module = input.config.generate(u, default_fuel)?;
let known_valid = if allow_invalid_funcs {
KnownValid::No
} else {
KnownValid::Yes
};
Ok((module.to_bytes(), known_valid))
}
Loading