use crate::config::Config;1use crate::function_generator::FunctionGenerator;2use crate::settings::{Flags, OptLevel};3use anyhow::Result;4use arbitrary::{Arbitrary, Unstructured};5use cranelift::codegen::Context;6use cranelift::codegen::data_value::DataValue;7use cranelift::codegen::ir::{Function, LibCall};8use cranelift::codegen::ir::{UserExternalName, UserFuncName};9use cranelift::codegen::isa::Builder;10use cranelift::prelude::isa::{OwnedTargetIsa, TargetIsa};11use cranelift::prelude::settings::SettingKind;12use cranelift::prelude::*;13use cranelift_arbitrary::CraneliftArbitrary;14use cranelift_native::builder_with_options;15use rand::{Rng, SeedableRng, rngs::SmallRng};16use target_isa_extras::TargetIsaExtras;17use target_lexicon::Architecture;1819mod config;20mod cranelift_arbitrary;21mod function_generator;22mod passes;23mod print;24mod target_isa_extras;2526pub use print::PrintableTestCase;2728pub type TestCaseInput = Vec<DataValue>;2930pub enum IsaFlagGen {31/// When generating ISA flags, ensure that they are all supported by32/// the current host.33Host,34/// All flags available in cranelift are allowed to be generated.35/// We also allow generating all possible values for each enum flag.36All,37}3839pub struct FuzzGen<'r, 'data>40where41'data: 'r,42{43pub u: &'r mut Unstructured<'data>,44pub config: Config,45}4647impl<'r, 'data> FuzzGen<'r, 'data>48where49'data: 'r,50{51pub fn new(u: &'r mut Unstructured<'data>) -> Self {52Self {53u,54config: Config::default(),55}56}5758pub fn generate_signature(&mut self, isa: &dyn TargetIsa) -> Result<Signature> {59let max_params = self.u.int_in_range(self.config.signature_params.clone())?;60let max_rets = self.u.int_in_range(self.config.signature_rets.clone())?;61Ok(self.u.signature(62isa.supports_simd(),63isa.triple().architecture,64max_params,65max_rets,66)?)67}6869pub fn generate_test_inputs(mut self, signature: &Signature) -> Result<Vec<TestCaseInput>> {70let mut inputs = Vec::new();7172// Generate up to "max_test_case_inputs" inputs, we need an upper bound here since73// the fuzzer at some point starts trying to feed us way too many inputs. (I found one74// test case with 130k inputs!)75for _ in 0..self.config.max_test_case_inputs {76let last_len = self.u.len();7778let test_args = signature79.params80.iter()81.map(|p| self.u.datavalue(p.value_type))82.collect::<Result<TestCaseInput>>()?;8384inputs.push(test_args);8586// Continue generating input as long as we just consumed some of self.u. Otherwise87// we'll generate the same test input again and again, forever. Note that once self.u88// becomes empty we obviously can't consume any more of it, so this check is more89// general. Also note that we need to generate at least one input or the fuzz target90// won't actually test anything, so checking at the end of the loop is good, even if91// self.u is empty from the start and we end up with all zeros in test_args.92assert!(self.u.len() <= last_len);93if self.u.len() == last_len {94break;95}96}9798Ok(inputs)99}100101fn run_func_passes(&mut self, func: Function, isa: &dyn TargetIsa) -> Result<Function> {102// Do a NaN Canonicalization pass on the generated function.103//104// Both IEEE754 and the Wasm spec are somewhat loose about what is allowed105// to be returned from NaN producing operations. And in practice this changes106// from X86 to Aarch64 and others. Even in the same host machine, the107// interpreter may produce a code sequence different from cranelift that108// generates different NaN's but produces legal results according to the spec.109//110// These differences cause spurious failures in the fuzzer. To fix this111// we enable the NaN Canonicalization pass that replaces any NaN's produced112// with a single fixed canonical NaN value.113//114// This is something that we can enable via flags for the compiled version, however115// the interpreter won't get that version, so call that pass manually here.116117let mut ctx = Context::for_function(func);118119// We disable the verifier here, since if it fails it prevents a test case from120// being generated and formatted by `cargo fuzz fmt`.121// We run the verifier before compiling the code, so it always gets verified.122let flags = settings::Flags::new({123let mut builder = settings::builder();124builder.set("enable_verifier", "false").unwrap();125builder126});127128// Create a new TargetISA from the given ISA, this ensures that we copy all ISA129// flags, which may have an effect on the code generated by the passes below.130let isa = Builder::from_target_isa(isa)131.finish(flags)132.expect("Failed to build TargetISA");133134// Finally run the NaN canonicalization pass135ctx.canonicalize_nans(isa.as_ref())136.expect("Failed NaN canonicalization pass");137138// Run the int_divz pass139//140// This pass replaces divs and rems with sequences that do not trap141passes::do_int_divz_pass(self, &mut ctx.func)?;142143// This pass replaces fcvt* instructions with sequences that do not trap144passes::do_fcvt_trap_pass(self, &mut ctx.func)?;145146Ok(ctx.func)147}148149pub fn generate_func(150&mut self,151name: UserFuncName,152isa: OwnedTargetIsa,153usercalls: Vec<(UserExternalName, Signature)>,154libcalls: Vec<LibCall>,155) -> Result<Function> {156let sig = self.generate_signature(&*isa)?;157158let func = FunctionGenerator::new(159&mut self.u,160&self.config,161isa.clone(),162name,163sig,164usercalls,165libcalls,166)167.generate()?;168169self.run_func_passes(func, &*isa)170}171172/// Generate a random set of cranelift flags.173/// Only semantics preserving flags are considered174pub fn generate_flags(&mut self, target_arch: Architecture) -> arbitrary::Result<Flags> {175let mut builder = settings::builder();176177let opt = self.u.choose(OptLevel::all())?;178builder.set("opt_level", &format!("{opt}")[..]).unwrap();179180// Boolean flags181// TODO: enable_pinned_reg does not work with our current trampolines. See: #4376182// TODO: is_pic has issues:183// x86: https://github.com/bytecodealliance/wasmtime/issues/5005184// aarch64: https://github.com/bytecodealliance/wasmtime/issues/2735185let bool_settings = [186"enable_alias_analysis",187"enable_safepoints",188"unwind_info",189"preserve_frame_pointers",190"enable_jump_tables",191"enable_heap_access_spectre_mitigation",192"enable_table_access_spectre_mitigation",193"enable_incremental_compilation_cache_checks",194"regalloc_checker",195"enable_llvm_abi_extensions",196];197for flag_name in bool_settings {198let enabled = self199.config200.compile_flag_ratio201.get(&flag_name)202.map(|&(num, denum)| self.u.ratio(num, denum))203.unwrap_or_else(|| bool::arbitrary(self.u))?;204205let value = format!("{enabled}");206builder.set(flag_name, value.as_str()).unwrap();207}208209let supports_inline_probestack = match target_arch {210Architecture::X86_64 => true,211Architecture::Aarch64(_) => true,212Architecture::Riscv64(_) => true,213_ => false,214};215216// Optionally test inline stackprobes on supported platforms217// TODO: Test outlined stack probes.218if supports_inline_probestack && bool::arbitrary(self.u)? {219builder.enable("enable_probestack").unwrap();220builder.set("probestack_strategy", "inline").unwrap();221222let size = self223.u224.int_in_range(self.config.stack_probe_size_log2.clone())?;225builder226.set("probestack_size_log2", &format!("{size}"))227.unwrap();228}229230// Generate random basic block padding231let bb_padding = self232.u233.int_in_range(self.config.bb_padding_log2_size.clone())234.unwrap();235builder236.set("bb_padding_log2_minus_one", &format!("{bb_padding}"))237.unwrap();238239// Fixed settings240241// We need llvm ABI extensions for i128 values on x86, so enable it regardless of242// what we picked above.243if target_arch == Architecture::X86_64 {244builder.enable("enable_llvm_abi_extensions").unwrap();245}246247// FIXME(#9510) remove once this option is permanently disabled248builder.enable("enable_multi_ret_implicit_sret").unwrap();249250// This is the default, but we should ensure that it wasn't accidentally turned off anywhere.251builder.enable("enable_verifier").unwrap();252253// These settings just panic when they're not enabled and we try to use their respective functionality254// so they aren't very interesting to be automatically generated.255builder.enable("enable_atomics").unwrap();256builder.enable("enable_float").unwrap();257258// `machine_code_cfg_info` generates additional metadata for the embedder but this doesn't feed back259// into compilation anywhere, we leave it on unconditionally to make sure the generation doesn't panic.260builder.enable("machine_code_cfg_info").unwrap();261262// Differential fuzzing between the interpreter and the host will only263// really work if NaN payloads are canonicalized, so enable this.264builder.enable("enable_nan_canonicalization").unwrap();265266Ok(Flags::new(builder))267}268269/// Generate a random set of ISA flags and apply them to a Builder.270///271/// Based on `mode` we can either allow all flags, or just the subset that is272/// supported by the current host.273///274/// In all cases only a subset of the allowed flags is applied to the builder.275pub fn set_isa_flags(&mut self, builder: &mut Builder, mode: IsaFlagGen) -> Result<()> {276// `max_isa` is the maximal set of flags that we can use.277let max_builder = match mode {278IsaFlagGen::All => {279let mut max_builder = isa::lookup(builder.triple().clone())?;280281for flag in max_builder.iter() {282match flag.kind {283SettingKind::Bool => {284max_builder.enable(flag.name)?;285}286SettingKind::Enum => {287// Since these are enums there isn't a "max" value per se, pick one at random.288let value = self.u.choose(flag.values.unwrap())?;289max_builder.set(flag.name, value)?;290}291SettingKind::Preset => {292// Presets are just special flags that combine other flags, we don't293// want to enable them directly, just the underlying flags.294}295_ => todo!(),296};297}298max_builder299}300// Use `cranelift-native` to do feature detection for us.301IsaFlagGen::Host => builder_with_options(true)302.expect("Unable to build a TargetIsa for the current host"),303};304// Cranelift has a somewhat weird API for this, but we need to build the final `TargetIsa` to be able305// to extract the values for the ISA flags. We need that to use the `string_value()` that formats306// the values so that we can pass it into the builder again.307let max_isa = max_builder.finish(Flags::new(settings::builder()))?;308309// We give each of the flags a chance of being copied over. Otherwise we310// keep the default. Note that a constant amount of data is taken from311// `self.u` as a seed for a `SmallRng` which is then transitively used312// to make decisions about what flags to include. This is done to ensure313// that the same test case generates similarly across different machines314// with different CPUs when `Host` is used above.315let mut rng = SmallRng::from_seed(self.u.arbitrary()?);316for value in max_isa.isa_flags().iter() {317if rng.random() {318continue;319}320builder.set(value.name, &value.value_string())?;321}322323Ok(())324}325}326327328