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
This commit is contained in:
Alex Crichton
2022-02-15 14:27:55 -06:00
committed by GitHub
parent 85cf4b042a
commit 0b4263333b
3 changed files with 99 additions and 5 deletions

9
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"

View File

@@ -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<Self> {
// 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)
}
}