From 0b4263333b72e090d3ee62e3685e0a63103ef505 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 15 Feb 2022 14:27:55 -0600 Subject: [PATCH] Fuzz cranelift cpu flag settings with Wasmtime (#3800) * Fuzz cranelift cpu flag settings with Wasmtime This commit updates the `Config` fuzz-generator to consume some of the input as configuration settings for codegen flags we pass to cranelift. This should allow for ideally some more coverage where settings are disabled or enabled, ideally finding possible bugs in feature-specific implementations or generic implementations that are rarely used if the feature-specific ones almost always take precedent. The technique used in this commit is to weight selection of codegen settings less frequently than using the native settings. Afterwards each listed feature is individually enabled or disabled depending on the input fuzz data, and if a feature is enabled but the host doesn't actually support it then the fuzz input is rejected with a log message. The goal here is to still have many fuzz inputs accepted but also ensure determinism across hosts. If there's a bug specifically related to enabling a flag then running it on a host without the flag should indicate that the flag isn't supported rather than silently leaving it disabled and reporting the fuzz case a success. * Use built-in `Unstructured::ratio` method * Tweak macro * Bump arbitrary dep version --- Cargo.lock | 9 ++-- crates/fuzzing/Cargo.toml | 3 +- crates/fuzzing/src/generators.rs | 92 ++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 531c3d4e4c..ea52ae6aeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,9 +99,9 @@ checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] name = "arbitrary" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237430fd6ed3740afe94eefcc278ae21e050285be882804e0d6e8695f0c94691" +checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490" dependencies = [ "derive_arbitrary", ] @@ -2819,9 +2819,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ae3b39281e4b14b8123bdbaddd472b7dfe215e444181f2f9d2443c2444f834" +checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" [[package]] name = "tempfile" @@ -3594,6 +3594,7 @@ dependencies = [ "env_logger 0.8.3", "log", "rayon", + "target-lexicon", "tempfile", "v8", "wasm-encoder", diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 6c89ab9962..05fe2d170c 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -9,10 +9,11 @@ license = "Apache-2.0 WITH LLVM-exception" [dependencies] anyhow = "1.0.22" -arbitrary = { version = "1.0.0", features = ["derive"] } +arbitrary = { version = "1.1.0", features = ["derive"] } env_logger = "0.8.1" log = "0.4.8" rayon = "1.2.1" +target-lexicon = "0.12.3" tempfile = "3.3.0" wasmparser = "0.82" wasmprinter = "0.2.32" diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index 7988cacea6..bf8b647e6e 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -251,6 +251,7 @@ pub struct WasmtimeConfig { use_precompiled_cwasm: bool, /// Configuration for the instance allocation strategy to use. pub strategy: InstanceAllocationStrategy, + codegen: CodegenSettings, } #[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)] @@ -306,6 +307,8 @@ impl Config { .memfd(self.wasmtime.memfd) .allocation_strategy(self.wasmtime.strategy.to_wasmtime()); + self.wasmtime.codegen.configure(&mut cfg); + // If the wasm-smith-generated module use nan canonicalization then we // don't need to enable it, but if it doesn't enable it already then we // enable this codegen option. @@ -569,3 +572,92 @@ impl<'a> Arbitrary<'a> for ModuleConfig { Ok(ModuleConfig { config }) } } + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +enum CodegenSettings { + Native, + #[allow(dead_code)] + Target { + target: String, + flags: Vec<(String, String)>, + }, +} + +impl CodegenSettings { + fn configure(&self, config: &mut wasmtime::Config) { + match self { + CodegenSettings::Native => {} + CodegenSettings::Target { target, flags } => { + config.target(target).unwrap(); + for (key, value) in flags { + unsafe { + config.cranelift_flag_set(key, value).unwrap(); + } + } + } + } + } +} + +impl<'a> Arbitrary<'a> for CodegenSettings { + #[allow(unused_macros, unused_variables)] + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + // Helper macro to enable clif features based on what the native host + // supports. If the input says to enable a feature and the host doesn't + // support it then that test case is rejected with a warning. + macro_rules! target_features { + ( + test:$test:ident, + $(std: $std:tt => clif: $clif:tt $(ratio: $a:tt in $b:tt)?,)* + ) => ({ + let mut flags = Vec::new(); + $( + let (low, hi) = (1, 2); + $(let (low, hi) = ($a, $b);)? + let enable = u.ratio(low, hi)?; + if enable && !std::$test!($std) { + log::error!("want to enable clif `{}` but host doesn't support it", + $clif); + return Err(arbitrary::Error::EmptyChoose) + } + flags.push(( + $clif.to_string(), + enable.to_string(), + )); + )* + flags + }) + } + #[cfg(target_arch = "x86_64")] + { + if u.ratio(1, 10)? { + let flags = target_features! { + test: is_x86_feature_detected, + std:"sse3" => clif:"has_sse3", + std:"ssse3" => clif:"has_ssse3", + std:"sse4.1" => clif:"has_sse41", + std:"sse4.2" => clif:"has_sse42", + std:"popcnt" => clif:"has_popcnt", + std:"avx" => clif:"has_avx", + std:"avx2" => clif:"has_avx2", + std:"bmi1" => clif:"has_bmi1", + std:"bmi2" => clif:"has_bmi2", + std:"lzcnt" => clif:"has_lzcnt", + + // not a lot of of cpus support avx512 so these are weighted + // to get enabled much less frequently. + std:"avx512bitalg" => clif:"has_avx512bitalg" ratio:1 in 1000, + std:"avx512dq" => clif:"has_avx512dq" ratio: 1 in 1000, + std:"avx512f" => clif:"has_avx512f" ratio: 1 in 1000, + std:"avx512vl" => clif:"has_avx512vl" ratio: 1 in 1000, + std:"avx512vbmi" => clif:"has_avx512vbmi" ratio: 1 in 1000, + }; + return Ok(CodegenSettings::Target { + target: target_lexicon::Triple::host().to_string(), + flags, + }); + } + } + Ok(CodegenSettings::Native) + } +}