cranelift_fuzzgen/
lib.rs

1use crate::config::Config;
2use crate::function_generator::FunctionGenerator;
3use crate::settings::{Flags, OptLevel};
4use anyhow::Result;
5use arbitrary::{Arbitrary, Unstructured};
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::codegen::Context;
11use cranelift::prelude::isa::{OwnedTargetIsa, TargetIsa};
12use cranelift::prelude::settings::SettingKind;
13use cranelift::prelude::*;
14use cranelift_arbitrary::CraneliftArbitrary;
15use cranelift_native::builder_with_options;
16use target_isa_extras::TargetIsaExtras;
17use target_lexicon::Architecture;
18
19mod config;
20mod cranelift_arbitrary;
21mod function_generator;
22mod passes;
23mod print;
24mod target_isa_extras;
25
26pub use print::PrintableTestCase;
27
28pub type TestCaseInput = Vec<DataValue>;
29
30pub 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
39pub struct FuzzGen<'r, 'data>
40where
41    'data: 'r,
42{
43    pub u: &'r mut Unstructured<'data>,
44    pub config: Config,
45}
46
47impl<'r, 'data> FuzzGen<'r, 'data>
48where
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        // FIXME(#9510) remove once this option is permanently disabled
243        builder.enable("enable_multi_ret_implicit_sret")?;
244
245        // This is the default, but we should ensure that it wasn't accidentally turned off anywhere.
246        builder.enable("enable_verifier")?;
247
248        // These settings just panic when they're not enabled and we try to use their respective functionality
249        // so they aren't very interesting to be automatically generated.
250        builder.enable("enable_atomics")?;
251        builder.enable("enable_float")?;
252
253        // `machine_code_cfg_info` generates additional metadata for the embedder but this doesn't feed back
254        // into compilation anywhere, we leave it on unconditionally to make sure the generation doesn't panic.
255        builder.enable("machine_code_cfg_info")?;
256
257        Ok(Flags::new(builder))
258    }
259
260    /// Generate a random set of ISA flags and apply them to a Builder.
261    ///
262    /// Based on `mode` we can either allow all flags, or just the subset that is
263    /// supported by the current host.
264    ///
265    /// In all cases only a subset of the allowed flags is applied to the builder.
266    pub fn set_isa_flags(&mut self, builder: &mut Builder, mode: IsaFlagGen) -> Result<()> {
267        // `max_isa` is the maximal set of flags that we can use.
268        let max_builder = match mode {
269            IsaFlagGen::All => {
270                let mut max_builder = isa::lookup(builder.triple().clone())?;
271
272                for flag in max_builder.iter() {
273                    match flag.kind {
274                        SettingKind::Bool => {
275                            max_builder.enable(flag.name)?;
276                        }
277                        SettingKind::Enum => {
278                            // Since these are enums there isn't a "max" value per se, pick one at random.
279                            let value = self.u.choose(flag.values.unwrap())?;
280                            max_builder.set(flag.name, value)?;
281                        }
282                        SettingKind::Preset => {
283                            // Presets are just special flags that combine other flags, we don't
284                            // want to enable them directly, just the underlying flags.
285                        }
286                        _ => todo!(),
287                    };
288                }
289                max_builder
290            }
291            // Use `cranelift-native` to do feature detection for us.
292            IsaFlagGen::Host => builder_with_options(true)
293                .expect("Unable to build a TargetIsa for the current host"),
294        };
295        // Cranelift has a somewhat weird API for this, but we need to build the final `TargetIsa` to be able
296        // to extract the values for the ISA flags. We need that to use the `string_value()` that formats
297        // the values so that we can pass it into the builder again.
298        let max_isa = max_builder.finish(Flags::new(settings::builder()))?;
299
300        // We give each of the flags a chance of being copied over. Otherwise we keep the default.
301        for value in max_isa.isa_flags().iter() {
302            let should_copy = bool::arbitrary(self.u)?;
303            if !should_copy {
304                continue;
305            }
306            builder.set(value.name, &value.value_string())?;
307        }
308
309        Ok(())
310    }
311}