fuzz: refactor fuzz generators (#4404)

Previously, much of the logic for generating the various objects needed
for fuzzing was concentrated primarily in `generators.rs`. In trying to
piece together what code does what, the size of the file and the light
documentation make it hard to discern what each part does. Since several
generator structures had been split out as separate modules in the
`generators/` directory, this change takes that refactoring further by
moving the structures in `generators.rs` to their own modules. No logic
changes were made, only the addition of documentation in a few places.
This commit is contained in:
Andrew Brown
2022-07-07 11:44:27 -07:00
committed by GitHub
parent e9727b9d4b
commit c227063ff6
8 changed files with 847 additions and 796 deletions

View File

@@ -0,0 +1,139 @@
//! Generate Cranelift compiler settings.
use arbitrary::{Arbitrary, Unstructured};
/// Choose between matching the host architecture or a cross-compilation target.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum CodegenSettings {
/// Use the host's feature set.
Native,
/// Generate a modified flag set for the current host.
#[allow(dead_code)]
Target {
/// The target triple of the host.
target: String,
/// A list of CPU features to enable, e.g., `("has_avx", "false")`.
flags: Vec<(String, String)>,
},
}
impl CodegenSettings {
/// Configure Wasmtime with these codegen settings.
pub 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);
}
}
}
}
}
}
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.
//
// Note that this specifically consumes bytes from the fuzz input for
// features for all targets, discarding anything which isn't applicable
// to the current target. The theory behind this is that most fuzz bugs
// won't be related to this feature selection so by consistently
// consuming input irrespective of the current platform reproducing fuzz
// bugs should be easier between different architectures.
macro_rules! target_features {
(
$(
$arch:tt => {
test:$test:ident,
$(std: $std:tt => clif: $clif:tt $(ratio: $a:tt in $b:tt)?,)*
},
)*
) => ({
let mut flags = Vec::new();
$( // for each `$arch`
$( // for each `$std`/`$clif` pair
// Use the input to generate whether `$clif` will be
// enabled. By default this is a 1 in 2 chance but each
// feature supports a custom ratio as well which shadows
// the (low, hi)
let (low, hi) = (1, 2);
$(let (low, hi) = ($a, $b);)?
let enable = u.ratio(low, hi)?;
// If we're actually on the relevant platform and the
// feature is enabled be sure to check that this host
// supports it. If the host doesn't support it then
// print a warning and return an error because this fuzz
// input must be discarded.
#[cfg(target_arch = $arch)]
if enable && !std::arch::$test!($std) {
log::warn!("want to enable clif `{}` but host doesn't support it",
$clif);
return Err(arbitrary::Error::EmptyChoose)
}
// And finally actually push the feature into the set of
// flags to enable, but only if we're on the right
// architecture.
if cfg!(target_arch = $arch) {
flags.push((
$clif.to_string(),
enable.to_string(),
));
}
)*
)*
flags
})
}
if u.ratio(1, 10)? {
let flags = target_features! {
"x86_64" => {
test: is_x86_feature_detected,
// These features are considered to be baseline required by
// Wasmtime. Currently some SIMD code generation will
// fail if these features are disabled, so unconditionally
// enable them as we're not interested in fuzzing without
// them.
std:"sse3" => clif:"has_sse3" ratio: 1 in 1,
std:"ssse3" => clif:"has_ssse3" ratio: 1 in 1,
std:"sse4.1" => clif:"has_sse41" ratio: 1 in 1,
std:"sse4.2" => clif:"has_sse42" ratio: 1 in 1,
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,
},
"aarch64" => {
test: is_aarch64_feature_detected,
std: "lse" => clif: "has_lse",
},
};
return Ok(CodegenSettings::Target {
target: target_lexicon::Triple::host().to_string(),
flags,
});
}
Ok(CodegenSettings::Native)
}
}