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