diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0cdc7065a7..7108f63daf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -351,6 +351,8 @@ jobs: - run: cargo fuzz build --dev -s none # Check that the ISLE fuzz targets build too. - run: cargo fuzz build --dev -s none --fuzz-dir ./cranelift/isle/fuzz + # Check that winch fuzzing builds too. + - run: cargo fuzz build --dev -s none --features fuzz-winch # common logic to cancel the entire run if this job fails - run: gh run cancel ${{ github.run_id }} diff --git a/Cargo.lock b/Cargo.lock index 9811cb8cb1..3c3adcb714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3984,6 +3984,7 @@ dependencies = [ "rand 0.8.5", "smallvec", "target-lexicon", + "wasmparser", "wasmtime", "wasmtime-fuzzing", ] diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 053bd79ddf..5ead20aca9 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -46,3 +46,4 @@ wasm-spec-interpreter = { path = "./wasm-spec-interpreter", optional = true, fea [features] fuzz-spec-interpreter = ['wasm-spec-interpreter'] +winch = ["wasmtime/winch"] diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index 3da2e24040..8bc11b1a64 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -23,6 +23,8 @@ pub mod table_ops; mod value; pub use codegen_settings::CodegenSettings; +#[cfg(feature = "winch")] +pub use config::CompilerStrategy; pub use config::{Config, WasmtimeConfig}; pub use instance_allocation_strategy::InstanceAllocationStrategy; pub use memory::{MemoryConfig, NormalMemoryConfig, UnalignedMemory, UnalignedMemoryCreator}; diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs index d689158326..d564cabbd0 100644 --- a/crates/fuzzing/src/generators/config.rs +++ b/crates/fuzzing/src/generators/config.rs @@ -168,6 +168,9 @@ impl Config { .allocation_strategy(self.wasmtime.strategy.to_wasmtime()) .generate_address_map(self.wasmtime.generate_address_map); + #[cfg(feature = "winch")] + cfg.strategy(self.wasmtime.compiler_strategy.to_wasmtime()); + self.wasmtime.codegen.configure(&mut cfg); // If the wasm-smith-generated module use nan canonicalization then we @@ -392,6 +395,9 @@ pub struct WasmtimeConfig { padding_between_functions: Option, generate_address_map: bool, native_unwind_info: bool, + #[cfg(feature = "winch")] + /// Configuration for the compiler to use. + pub compiler_strategy: CompilerStrategy, } impl WasmtimeConfig { @@ -435,3 +441,23 @@ impl OptLevel { } } } + +#[cfg(feature = "winch")] +#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)] +/// Compiler to use. +pub enum CompilerStrategy { + /// Cranelift compiler. + Cranelift, + /// Winch compiler. + Winch, +} + +#[cfg(feature = "winch")] +impl CompilerStrategy { + fn to_wasmtime(&self) -> wasmtime::Strategy { + match self { + CompilerStrategy::Cranelift => wasmtime::Strategy::Cranelift, + CompilerStrategy::Winch => wasmtime::Strategy::Winch, + } + } +} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 53575417f2..17cb3b2900 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -21,6 +21,7 @@ cranelift-control = { workspace = true } libfuzzer-sys = { version = "0.4.0", features = ["arbitrary-derive"] } target-lexicon = { workspace = true } smallvec = { workspace = true } +wasmparser = { workspace = true, optional = true } wasmtime = { workspace = true } wasmtime-fuzzing = { workspace = true } component-test-util = { workspace = true } @@ -35,6 +36,7 @@ quote = "1.0" component-fuzz-util = { workspace = true } [features] +fuzz-winch = ["wasmtime/winch", "wasmtime-fuzzing/winch", "dep:wasmparser"] default = ['fuzz-spec-interpreter'] fuzz-spec-interpreter = ['wasmtime-fuzzing/fuzz-spec-interpreter'] chaos = ["cranelift-control/chaos"] diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs index e6a1f50127..cf71d34232 100644 --- a/fuzz/fuzz_targets/differential.rs +++ b/fuzz/fuzz_targets/differential.rs @@ -6,10 +6,14 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use std::sync::Once; use wasmtime::Trap; +#[cfg(feature = "fuzz-winch")] +use wasmtime_fuzzing::generators::CompilerStrategy; use wasmtime_fuzzing::generators::{Config, DiffValue, DiffValueType, SingleInstModule}; use wasmtime_fuzzing::oracles::diff_wasmtime::WasmtimeInstance; use wasmtime_fuzzing::oracles::engine::{build_allowed_env_list, parse_env_list}; use wasmtime_fuzzing::oracles::{differential, engine, log_wasm}; +#[cfg(feature = "fuzz-winch")] +use wasmtime_fuzzing::wasm_smith::{InstructionKind, InstructionKinds}; // Upper limit on the number of invocations for each WebAssembly function // executed by this fuzz target. @@ -35,16 +39,22 @@ fuzz_target!(|data: &[u8]| { // `setup_ocaml_runtime`. engine::setup_engine_runtimes(); + let (default_engines, default_modules) = if cfg!(feature = "fuzz-winch") { + (vec!["wasmi"], vec!["wasm-smith", "single-inst"]) + } else { + ( + vec!["wasmtime", "wasmi", "spec", "v8"], + vec!["wasm-smith", "single-inst"], + ) + }; + // Retrieve the configuration for this fuzz target from `ALLOWED_*` // environment variables. - let allowed_engines = build_allowed_env_list( - parse_env_list("ALLOWED_ENGINES"), - &["wasmtime", "wasmi", "spec", "v8"], - ); - let allowed_modules = build_allowed_env_list( - parse_env_list("ALLOWED_MODULES"), - &["wasm-smith", "single-inst"], - ); + let allowed_engines = + build_allowed_env_list(parse_env_list("ALLOWED_ENGINES"), &default_engines); + let allowed_modules = + build_allowed_env_list(parse_env_list("ALLOWED_MODULES"), &default_modules); + unsafe { ALLOWED_ENGINES = allowed_engines; ALLOWED_MODULES = allowed_modules; @@ -68,6 +78,16 @@ fn execute_one(data: &[u8]) -> Result<()> { let mut config: Config = u.arbitrary()?; config.set_differential_config(); + #[cfg(feature = "fuzz-winch")] + { + // When fuzzing Winch: + // 1. Explicitly override the compiler strategy. + // 2. Explicitly set the allowed instructions for `wasm-smith`. + config.wasmtime.compiler_strategy = CompilerStrategy::Winch; + config.module_config.config.allowed_instructions = + InstructionKinds::new(&[InstructionKind::Numeric, InstructionKind::Variable]); + } + // Choose an engine that Wasmtime will be differentially executed against. // The chosen engine is then created, which might update `config`, and // returned as a trait object. @@ -100,6 +120,12 @@ fn execute_one(data: &[u8]) -> Result<()> { "single-inst" => build_single_inst_module(&mut u, &config)?, _ => unreachable!(), }; + + #[cfg(feature = "fuzz-winch")] + if !winch_supports_module(&wasm) { + return Ok(()); + } + log_wasm(&wasm); // Instantiate the generated wasm file in the chosen differential engine. @@ -263,3 +289,54 @@ impl RuntimeStats { }; } } + +#[cfg(feature = "fuzz-winch")] +// Returns true if the module only contains operators supported by +// Winch. Winch's x86_64 target has broader support for Wasm operators +// than the aarch64 target. This list assumes fuzzing on the x86_64 +// target. +fn winch_supports_module(module: &[u8]) -> bool { + use wasmparser::{Operator::*, Parser, Payload}; + + let mut supported = true; + let mut parser = Parser::new(0).parse_all(module); + + 'main: while let Some(payload) = parser.next() { + match payload.unwrap() { + Payload::CodeSectionEntry(body) => { + let op_reader = body.get_operators_reader().unwrap(); + for op in op_reader { + match op.unwrap() { + I32Const { .. } + | I64Const { .. } + | I32Add { .. } + | I64Add { .. } + | I32Sub { .. } + | I32Mul { .. } + | I32DivS { .. } + | I32DivU { .. } + | I64DivS { .. } + | I64DivU { .. } + | I64RemU { .. } + | I64RemS { .. } + | I32RemU { .. } + | I32RemS { .. } + | I64Mul { .. } + | I64Sub { .. } + | LocalGet { .. } + | LocalSet { .. } + | Call { .. } + | End { .. } => {} + _ => { + supported = false; + break 'main; + } + } + } + } + _ => {} + } + } + + supported +}