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