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