1 use crate::config::Config; 2 use crate::function_generator::FunctionGenerator; 3 use crate::settings::{Flags, OptLevel}; 4 use anyhow::Result; 5 use arbitrary::{Arbitrary, Unstructured}; 6 use cranelift::codegen::Context; 7 use cranelift::codegen::data_value::DataValue; 8 use cranelift::codegen::ir::{Function, LibCall}; 9 use cranelift::codegen::ir::{UserExternalName, UserFuncName}; 10 use cranelift::codegen::isa::Builder; 11 use cranelift::prelude::isa::{OwnedTargetIsa, TargetIsa}; 12 use cranelift::prelude::settings::SettingKind; 13 use cranelift::prelude::*; 14 use cranelift_arbitrary::CraneliftArbitrary; 15 use cranelift_native::builder_with_options; 16 use rand::{Rng, SeedableRng, rngs::SmallRng}; 17 use target_isa_extras::TargetIsaExtras; 18 use target_lexicon::Architecture; 19 20 mod config; 21 mod cranelift_arbitrary; 22 mod function_generator; 23 mod passes; 24 mod print; 25 mod target_isa_extras; 26 27 pub use print::PrintableTestCase; 28 29 pub type TestCaseInput = Vec<DataValue>; 30 31 pub enum IsaFlagGen { 32 /// When generating ISA flags, ensure that they are all supported by 33 /// the current host. 34 Host, 35 /// All flags available in cranelift are allowed to be generated. 36 /// We also allow generating all possible values for each enum flag. 37 All, 38 } 39 40 pub struct FuzzGen<'r, 'data> 41 where 42 'data: 'r, 43 { 44 pub u: &'r mut Unstructured<'data>, 45 pub config: Config, 46 } 47 48 impl<'r, 'data> FuzzGen<'r, 'data> 49 where 50 'data: 'r, 51 { new(u: &'r mut Unstructured<'data>) -> Self52 pub fn new(u: &'r mut Unstructured<'data>) -> Self { 53 Self { 54 u, 55 config: Config::default(), 56 } 57 } 58 generate_signature(&mut self, isa: &dyn TargetIsa) -> Result<Signature>59 pub fn generate_signature(&mut self, isa: &dyn TargetIsa) -> Result<Signature> { 60 let max_params = self.u.int_in_range(self.config.signature_params.clone())?; 61 let max_rets = self.u.int_in_range(self.config.signature_rets.clone())?; 62 Ok(self.u.signature( 63 isa.supports_simd(), 64 isa.triple().architecture, 65 max_params, 66 max_rets, 67 )?) 68 } 69 generate_test_inputs(mut self, signature: &Signature) -> Result<Vec<TestCaseInput>>70 pub fn generate_test_inputs(mut self, signature: &Signature) -> Result<Vec<TestCaseInput>> { 71 let mut inputs = Vec::new(); 72 73 // Generate up to "max_test_case_inputs" inputs, we need an upper bound here since 74 // the fuzzer at some point starts trying to feed us way too many inputs. (I found one 75 // test case with 130k inputs!) 76 for _ in 0..self.config.max_test_case_inputs { 77 let last_len = self.u.len(); 78 79 let test_args = signature 80 .params 81 .iter() 82 .map(|p| self.u.datavalue(p.value_type)) 83 .collect::<Result<TestCaseInput>>()?; 84 85 inputs.push(test_args); 86 87 // Continue generating input as long as we just consumed some of self.u. Otherwise 88 // we'll generate the same test input again and again, forever. Note that once self.u 89 // becomes empty we obviously can't consume any more of it, so this check is more 90 // general. Also note that we need to generate at least one input or the fuzz target 91 // won't actually test anything, so checking at the end of the loop is good, even if 92 // self.u is empty from the start and we end up with all zeros in test_args. 93 assert!(self.u.len() <= last_len); 94 if self.u.len() == last_len { 95 break; 96 } 97 } 98 99 Ok(inputs) 100 } 101 run_func_passes(&mut self, func: Function, isa: &dyn TargetIsa) -> Result<Function>102 fn run_func_passes(&mut self, func: Function, isa: &dyn TargetIsa) -> Result<Function> { 103 // Do a NaN Canonicalization pass on the generated function. 104 // 105 // Both IEEE754 and the Wasm spec are somewhat loose about what is allowed 106 // to be returned from NaN producing operations. And in practice this changes 107 // from X86 to Aarch64 and others. Even in the same host machine, the 108 // interpreter may produce a code sequence different from cranelift that 109 // generates different NaN's but produces legal results according to the spec. 110 // 111 // These differences cause spurious failures in the fuzzer. To fix this 112 // we enable the NaN Canonicalization pass that replaces any NaN's produced 113 // with a single fixed canonical NaN value. 114 // 115 // This is something that we can enable via flags for the compiled version, however 116 // the interpreter won't get that version, so call that pass manually here. 117 118 let mut ctx = Context::for_function(func); 119 120 // We disable the verifier here, since if it fails it prevents a test case from 121 // being generated and formatted by `cargo fuzz fmt`. 122 // We run the verifier before compiling the code, so it always gets verified. 123 let flags = settings::Flags::new({ 124 let mut builder = settings::builder(); 125 builder.set("enable_verifier", "false").unwrap(); 126 builder 127 }); 128 129 // Create a new TargetISA from the given ISA, this ensures that we copy all ISA 130 // flags, which may have an effect on the code generated by the passes below. 131 let isa = Builder::from_target_isa(isa) 132 .finish(flags) 133 .expect("Failed to build TargetISA"); 134 135 // Finally run the NaN canonicalization pass 136 ctx.canonicalize_nans(isa.as_ref()) 137 .expect("Failed NaN canonicalization pass"); 138 139 // Run the int_divz pass 140 // 141 // This pass replaces divs and rems with sequences that do not trap 142 passes::do_int_divz_pass(self, &mut ctx.func)?; 143 144 // This pass replaces fcvt* instructions with sequences that do not trap 145 passes::do_fcvt_trap_pass(self, &mut ctx.func)?; 146 147 Ok(ctx.func) 148 } 149 generate_func( &mut self, name: UserFuncName, isa: OwnedTargetIsa, usercalls: Vec<(UserExternalName, Signature)>, libcalls: Vec<LibCall>, ) -> Result<Function>150 pub fn generate_func( 151 &mut self, 152 name: UserFuncName, 153 isa: OwnedTargetIsa, 154 usercalls: Vec<(UserExternalName, Signature)>, 155 libcalls: Vec<LibCall>, 156 ) -> Result<Function> { 157 let sig = self.generate_signature(&*isa)?; 158 159 let func = FunctionGenerator::new( 160 &mut self.u, 161 &self.config, 162 isa.clone(), 163 name, 164 sig, 165 usercalls, 166 libcalls, 167 ) 168 .generate()?; 169 170 self.run_func_passes(func, &*isa) 171 } 172 173 /// Generate a random set of cranelift flags. 174 /// Only semantics preserving flags are considered generate_flags(&mut self, target_arch: Architecture) -> arbitrary::Result<Flags>175 pub fn generate_flags(&mut self, target_arch: Architecture) -> arbitrary::Result<Flags> { 176 let mut builder = settings::builder(); 177 178 let opt = self.u.choose(OptLevel::all())?; 179 builder.set("opt_level", &format!("{opt}")[..]).unwrap(); 180 181 // Boolean flags 182 // TODO: enable_pinned_reg does not work with our current trampolines. See: #4376 183 // TODO: is_pic has issues: 184 // x86: https://github.com/bytecodealliance/wasmtime/issues/5005 185 // aarch64: https://github.com/bytecodealliance/wasmtime/issues/2735 186 let bool_settings = [ 187 "enable_alias_analysis", 188 "unwind_info", 189 "preserve_frame_pointers", 190 "enable_heap_access_spectre_mitigation", 191 "enable_table_access_spectre_mitigation", 192 "enable_incremental_compilation_cache_checks", 193 "regalloc_checker", 194 "enable_llvm_abi_extensions", 195 ]; 196 for flag_name in bool_settings { 197 let enabled = self 198 .config 199 .compile_flag_ratio 200 .get(&flag_name) 201 .map(|&(num, denum)| self.u.ratio(num, denum)) 202 .unwrap_or_else(|| bool::arbitrary(self.u))?; 203 204 let value = format!("{enabled}"); 205 builder.set(flag_name, value.as_str()).unwrap(); 206 } 207 208 let supports_inline_probestack = match target_arch { 209 Architecture::X86_64 => true, 210 Architecture::Aarch64(_) => true, 211 Architecture::Riscv64(_) => true, 212 _ => false, 213 }; 214 215 // Optionally test inline stackprobes on supported platforms 216 // TODO: Test outlined stack probes. 217 if supports_inline_probestack && bool::arbitrary(self.u)? { 218 builder.enable("enable_probestack").unwrap(); 219 builder.set("probestack_strategy", "inline").unwrap(); 220 221 let size = self 222 .u 223 .int_in_range(self.config.stack_probe_size_log2.clone())?; 224 builder 225 .set("probestack_size_log2", &format!("{size}")) 226 .unwrap(); 227 } 228 229 // Generate random basic block padding 230 let bb_padding = self 231 .u 232 .int_in_range(self.config.bb_padding_log2_size.clone()) 233 .unwrap(); 234 builder 235 .set("bb_padding_log2_minus_one", &format!("{bb_padding}")) 236 .unwrap(); 237 238 // Fixed settings 239 240 // We need llvm ABI extensions for i128 values on x86, so enable it regardless of 241 // what we picked above. 242 if target_arch == Architecture::X86_64 { 243 builder.enable("enable_llvm_abi_extensions").unwrap(); 244 } 245 246 // FIXME(#9510) remove once this option is permanently disabled 247 builder.enable("enable_multi_ret_implicit_sret").unwrap(); 248 249 // This is the default, but we should ensure that it wasn't accidentally turned off anywhere. 250 builder.enable("enable_verifier").unwrap(); 251 252 // `machine_code_cfg_info` generates additional metadata for the embedder but this doesn't feed back 253 // into compilation anywhere, we leave it on unconditionally to make sure the generation doesn't panic. 254 builder.enable("machine_code_cfg_info").unwrap(); 255 256 // Differential fuzzing between the interpreter and the host will only 257 // really work if NaN payloads are canonicalized, so enable this. 258 builder.enable("enable_nan_canonicalization").unwrap(); 259 260 Ok(Flags::new(builder)) 261 } 262 263 /// Generate a random set of ISA flags and apply them to a Builder. 264 /// 265 /// Based on `mode` we can either allow all flags, or just the subset that is 266 /// supported by the current host. 267 /// 268 /// In all cases only a subset of the allowed flags is applied to the builder. set_isa_flags(&mut self, builder: &mut Builder, mode: IsaFlagGen) -> Result<()>269 pub fn set_isa_flags(&mut self, builder: &mut Builder, mode: IsaFlagGen) -> Result<()> { 270 // `max_isa` is the maximal set of flags that we can use. 271 let max_builder = match mode { 272 IsaFlagGen::All => { 273 let mut max_builder = isa::lookup(builder.triple().clone())?; 274 275 for flag in max_builder.iter() { 276 match flag.kind { 277 SettingKind::Bool => { 278 max_builder.enable(flag.name)?; 279 } 280 SettingKind::Enum => { 281 // Since these are enums there isn't a "max" value per se, pick one at random. 282 let value = self.u.choose(flag.values.unwrap())?; 283 max_builder.set(flag.name, value)?; 284 } 285 SettingKind::Preset => { 286 // Presets are just special flags that combine other flags, we don't 287 // want to enable them directly, just the underlying flags. 288 } 289 _ => todo!(), 290 }; 291 } 292 max_builder 293 } 294 // Use `cranelift-native` to do feature detection for us. 295 IsaFlagGen::Host => builder_with_options(true) 296 .expect("Unable to build a TargetIsa for the current host"), 297 }; 298 // Cranelift has a somewhat weird API for this, but we need to build the final `TargetIsa` to be able 299 // to extract the values for the ISA flags. We need that to use the `string_value()` that formats 300 // the values so that we can pass it into the builder again. 301 let max_isa = max_builder.finish(Flags::new(settings::builder()))?; 302 303 // We give each of the flags a chance of being copied over. Otherwise we 304 // keep the default. Note that a constant amount of data is taken from 305 // `self.u` as a seed for a `SmallRng` which is then transitively used 306 // to make decisions about what flags to include. This is done to ensure 307 // that the same test case generates similarly across different machines 308 // with different CPUs when `Host` is used above. 309 let mut rng = SmallRng::from_seed(self.u.arbitrary()?); 310 for value in max_isa.isa_flags().iter() { 311 if rng.random() { 312 continue; 313 } 314 builder.set(value.name, &value.value_string())?; 315 } 316 317 Ok(()) 318 } 319 } 320