winch(fuzz): Initial support for differential fuzzing (#6281)

* winch(fuzz): Initial support for differential fuzzing

This commit introduces initial support for differential fuzzing for Winch. In
order to fuzz winch, this change introduces the `winch` cargo feature. When the
`winch` cargo feature is enabled the differential fuzz target uses `wasmi` as
the differential engine and `wasm-smith` and `single-inst` as the module sources.

The intention behind this change is to have a *local* approach for fuzzing and
verifying programs generated by Winch and to have an initial implementation that
will allow us to eventually enable this change by default. Currently it's not
worth it to enable this change by default given all the filtering that needs to
happen to ensure that the generated modules are supported by Winch.

It's worth noting that the Wasm filtering code will be temporary, until Winch
reaches feature parity in terms of Wasm operators.

* Check build targets with the `winch` feature flag

* Rename fuzz target feature to `fuzz-winch`
This commit is contained in:
Saúl Cabrera
2023-04-25 05:56:24 +02:00
committed by GitHub
parent bd473dba31
commit a1732b2906
7 changed files with 119 additions and 8 deletions

View File

@@ -351,6 +351,8 @@ jobs:
- run: cargo fuzz build --dev -s none - run: cargo fuzz build --dev -s none
# Check that the ISLE fuzz targets build too. # Check that the ISLE fuzz targets build too.
- run: cargo fuzz build --dev -s none --fuzz-dir ./cranelift/isle/fuzz - 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 # common logic to cancel the entire run if this job fails
- run: gh run cancel ${{ github.run_id }} - run: gh run cancel ${{ github.run_id }}

1
Cargo.lock generated
View File

@@ -3984,6 +3984,7 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
"smallvec", "smallvec",
"target-lexicon", "target-lexicon",
"wasmparser",
"wasmtime", "wasmtime",
"wasmtime-fuzzing", "wasmtime-fuzzing",
] ]

View File

@@ -46,3 +46,4 @@ wasm-spec-interpreter = { path = "./wasm-spec-interpreter", optional = true, fea
[features] [features]
fuzz-spec-interpreter = ['wasm-spec-interpreter'] fuzz-spec-interpreter = ['wasm-spec-interpreter']
winch = ["wasmtime/winch"]

View File

@@ -23,6 +23,8 @@ pub mod table_ops;
mod value; mod value;
pub use codegen_settings::CodegenSettings; pub use codegen_settings::CodegenSettings;
#[cfg(feature = "winch")]
pub use config::CompilerStrategy;
pub use config::{Config, WasmtimeConfig}; pub use config::{Config, WasmtimeConfig};
pub use instance_allocation_strategy::InstanceAllocationStrategy; pub use instance_allocation_strategy::InstanceAllocationStrategy;
pub use memory::{MemoryConfig, NormalMemoryConfig, UnalignedMemory, UnalignedMemoryCreator}; pub use memory::{MemoryConfig, NormalMemoryConfig, UnalignedMemory, UnalignedMemoryCreator};

View File

@@ -168,6 +168,9 @@ impl Config {
.allocation_strategy(self.wasmtime.strategy.to_wasmtime()) .allocation_strategy(self.wasmtime.strategy.to_wasmtime())
.generate_address_map(self.wasmtime.generate_address_map); .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); self.wasmtime.codegen.configure(&mut cfg);
// If the wasm-smith-generated module use nan canonicalization then we // If the wasm-smith-generated module use nan canonicalization then we
@@ -392,6 +395,9 @@ pub struct WasmtimeConfig {
padding_between_functions: Option<u16>, padding_between_functions: Option<u16>,
generate_address_map: bool, generate_address_map: bool,
native_unwind_info: bool, native_unwind_info: bool,
#[cfg(feature = "winch")]
/// Configuration for the compiler to use.
pub compiler_strategy: CompilerStrategy,
} }
impl WasmtimeConfig { 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,
}
}
}

View File

@@ -21,6 +21,7 @@ cranelift-control = { workspace = true }
libfuzzer-sys = { version = "0.4.0", features = ["arbitrary-derive"] } libfuzzer-sys = { version = "0.4.0", features = ["arbitrary-derive"] }
target-lexicon = { workspace = true } target-lexicon = { workspace = true }
smallvec = { workspace = true } smallvec = { workspace = true }
wasmparser = { workspace = true, optional = true }
wasmtime = { workspace = true } wasmtime = { workspace = true }
wasmtime-fuzzing = { workspace = true } wasmtime-fuzzing = { workspace = true }
component-test-util = { workspace = true } component-test-util = { workspace = true }
@@ -35,6 +36,7 @@ quote = "1.0"
component-fuzz-util = { workspace = true } component-fuzz-util = { workspace = true }
[features] [features]
fuzz-winch = ["wasmtime/winch", "wasmtime-fuzzing/winch", "dep:wasmparser"]
default = ['fuzz-spec-interpreter'] default = ['fuzz-spec-interpreter']
fuzz-spec-interpreter = ['wasmtime-fuzzing/fuzz-spec-interpreter'] fuzz-spec-interpreter = ['wasmtime-fuzzing/fuzz-spec-interpreter']
chaos = ["cranelift-control/chaos"] chaos = ["cranelift-control/chaos"]

View File

@@ -6,10 +6,14 @@ use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst; use std::sync::atomic::Ordering::SeqCst;
use std::sync::Once; use std::sync::Once;
use wasmtime::Trap; use wasmtime::Trap;
#[cfg(feature = "fuzz-winch")]
use wasmtime_fuzzing::generators::CompilerStrategy;
use wasmtime_fuzzing::generators::{Config, DiffValue, DiffValueType, SingleInstModule}; use wasmtime_fuzzing::generators::{Config, DiffValue, DiffValueType, SingleInstModule};
use wasmtime_fuzzing::oracles::diff_wasmtime::WasmtimeInstance; use wasmtime_fuzzing::oracles::diff_wasmtime::WasmtimeInstance;
use wasmtime_fuzzing::oracles::engine::{build_allowed_env_list, parse_env_list}; use wasmtime_fuzzing::oracles::engine::{build_allowed_env_list, parse_env_list};
use wasmtime_fuzzing::oracles::{differential, engine, log_wasm}; 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 // Upper limit on the number of invocations for each WebAssembly function
// executed by this fuzz target. // executed by this fuzz target.
@@ -35,16 +39,22 @@ fuzz_target!(|data: &[u8]| {
// `setup_ocaml_runtime`. // `setup_ocaml_runtime`.
engine::setup_engine_runtimes(); 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_*` // Retrieve the configuration for this fuzz target from `ALLOWED_*`
// environment variables. // environment variables.
let allowed_engines = build_allowed_env_list( let allowed_engines =
parse_env_list("ALLOWED_ENGINES"), build_allowed_env_list(parse_env_list("ALLOWED_ENGINES"), &default_engines);
&["wasmtime", "wasmi", "spec", "v8"], let allowed_modules =
); build_allowed_env_list(parse_env_list("ALLOWED_MODULES"), &default_modules);
let allowed_modules = build_allowed_env_list(
parse_env_list("ALLOWED_MODULES"),
&["wasm-smith", "single-inst"],
);
unsafe { unsafe {
ALLOWED_ENGINES = allowed_engines; ALLOWED_ENGINES = allowed_engines;
ALLOWED_MODULES = allowed_modules; ALLOWED_MODULES = allowed_modules;
@@ -68,6 +78,16 @@ fn execute_one(data: &[u8]) -> Result<()> {
let mut config: Config = u.arbitrary()?; let mut config: Config = u.arbitrary()?;
config.set_differential_config(); 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. // Choose an engine that Wasmtime will be differentially executed against.
// The chosen engine is then created, which might update `config`, and // The chosen engine is then created, which might update `config`, and
// returned as a trait object. // returned as a trait object.
@@ -100,6 +120,12 @@ fn execute_one(data: &[u8]) -> Result<()> {
"single-inst" => build_single_inst_module(&mut u, &config)?, "single-inst" => build_single_inst_module(&mut u, &config)?,
_ => unreachable!(), _ => unreachable!(),
}; };
#[cfg(feature = "fuzz-winch")]
if !winch_supports_module(&wasm) {
return Ok(());
}
log_wasm(&wasm); log_wasm(&wasm);
// Instantiate the generated wasm file in the chosen differential engine. // 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
}