From 4866fa0e6a211c50458426eb3d033bd69885ae10 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 28 Feb 2020 16:32:06 -0800 Subject: [PATCH] Limit rayon to one thread during fuzzing This should enable more deterministic execution. --- Cargo.lock | 1 + crates/fuzzing/Cargo.toml | 1 + crates/fuzzing/src/generators.rs | 2 ++ crates/fuzzing/src/generators/api.rs | 2 ++ crates/fuzzing/src/lib.rs | 26 ++++++++++++++++++++++++++ crates/fuzzing/src/oracles.rs | 11 ++++++++--- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 018e1a438c..39b0a3fa5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2133,6 +2133,7 @@ dependencies = [ "binaryen", "env_logger 0.7.1", "log", + "rayon", "wasmparser 0.51.2", "wasmprinter", "wasmtime", diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 835b76edfc..807ac2bae9 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -12,6 +12,7 @@ arbitrary = { version = "0.4.0", features = ["derive"] } binaryen = "0.10.0" env_logger = "0.7.1" log = "0.4.8" +rayon = "1.2.1" wasmparser = "0.51.2" wasmprinter = "0.2.1" wasmtime = { path = "../api", version = "0.12.0" } diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index f982b42844..b096467ca8 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -32,6 +32,7 @@ impl fmt::Debug for WasmOptTtf { impl Arbitrary for WasmOptTtf { fn arbitrary(input: &mut Unstructured) -> arbitrary::Result { + crate::init_fuzzing(); let seed: Vec = Arbitrary::arbitrary(input)?; let module = binaryen::tools::translate_to_fuzz_mvp(&seed); let wasm = module.write(); @@ -39,6 +40,7 @@ impl Arbitrary for WasmOptTtf { } fn arbitrary_take_rest(input: Unstructured) -> arbitrary::Result { + crate::init_fuzzing(); let seed: Vec = Arbitrary::arbitrary_take_rest(input)?; let module = binaryen::tools::translate_to_fuzz_mvp(&seed); let wasm = module.write(); diff --git a/crates/fuzzing/src/generators/api.rs b/crates/fuzzing/src/generators/api.rs index 3def5aa9a2..c269365028 100644 --- a/crates/fuzzing/src/generators/api.rs +++ b/crates/fuzzing/src/generators/api.rs @@ -81,6 +81,8 @@ pub struct ApiCalls { impl Arbitrary for ApiCalls { fn arbitrary(input: &mut Unstructured) -> arbitrary::Result { + crate::init_fuzzing(); + let swarm = Swarm::arbitrary(input)?; let mut calls = vec![]; diff --git a/crates/fuzzing/src/lib.rs b/crates/fuzzing/src/lib.rs index f036efe617..d56295883a 100644 --- a/crates/fuzzing/src/lib.rs +++ b/crates/fuzzing/src/lib.rs @@ -4,3 +4,29 @@ pub mod generators; pub mod oracles; + +/// One time start up initialization for fuzzing: +/// +/// * Enables `env_logger`. +/// +/// * Restricts `rayon` to a single thread in its thread pool, for more +/// deterministic executions. +/// +/// If a fuzz target is taking raw input bytes from the fuzzer, it is fine to +/// call this function in the fuzz target's oracle or in the fuzz target +/// itself. However, if the fuzz target takes an `Arbitrary` type, and the +/// `Arbitrary` implementation is not derived and does interesting things, then +/// the `Arbitrary` implementation should call this function, since it runs +/// before the fuzz target itself. +pub(crate) fn init_fuzzing() { + static INIT: std::sync::Once = std::sync::Once::new(); + + INIT.call_once(|| { + let _ = env_logger::try_init(); + + rayon::ThreadPoolBuilder::new() + .num_threads(1) + .build_global() + .expect("should only initialize the rayon thread pool once!"); + }) +} diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index 3af5ea8fa0..1b3bb05d9e 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -18,7 +18,7 @@ use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use wasmtime::*; fn fuzz_default_config(strategy: Strategy) -> Config { - drop(env_logger::try_init()); + crate::init_fuzzing(); let mut config = Config::new(); config .cranelift_debug_verifier(true) @@ -61,6 +61,8 @@ pub fn instantiate(wasm: &[u8], strategy: Strategy) { /// /// See also `instantiate` functions. pub fn instantiate_with_config(wasm: &[u8], config: Config) { + crate::init_fuzzing(); + let engine = Engine::new(&config); let store = Store::new(&engine); @@ -94,6 +96,8 @@ pub fn instantiate_with_config(wasm: &[u8], config: Config) { /// /// You can control which compiler is used via passing a `Strategy`. pub fn compile(wasm: &[u8], strategy: Strategy) { + crate::init_fuzzing(); + let engine = Engine::new(&fuzz_default_config(strategy)); let store = Store::new(&engine); log_wasm(wasm); @@ -108,7 +112,8 @@ pub fn differential_execution( ttf: &crate::generators::WasmOptTtf, configs: &[crate::generators::DifferentialConfig], ) { - drop(env_logger::try_init()); + crate::init_fuzzing(); + // We need at least two configs. if configs.len() < 2 // And all the configs should be unique. @@ -280,7 +285,7 @@ fn assert_same_export_func_result( pub fn make_api_calls(api: crate::generators::api::ApiCalls) { use crate::generators::api::ApiCall; - drop(env_logger::try_init()); + crate::init_fuzzing(); let mut config: Option = None; let mut engine: Option = None;