1 //! Test case generators.
2 //!
3 //! Test case generators take raw, unstructured input from a fuzzer
4 //! (e.g. libFuzzer) and translate that into a structured test case (e.g. a
5 //! valid Wasm binary).
6 //!
7 //! These are generally implementations of the `Arbitrary` trait, or some
8 //! wrapper over an external tool, such that the wrapper implements the
9 //! `Arbitrary` trait for the wrapped external tool.
10 
11 pub mod api;
12 pub mod table_ops;
13 
14 use crate::oracles::{StoreLimits, Timeout};
15 use anyhow::Result;
16 use arbitrary::{Arbitrary, Unstructured};
17 use std::sync::Arc;
18 use std::time::Duration;
19 use wasm_smith::SwarmConfig;
20 use wasmtime::{Engine, LinearMemory, MemoryCreator, MemoryType, Module, Store};
21 
22 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
23 enum OptLevel {
24     None,
25     Speed,
26     SpeedAndSize,
27 }
28 
29 impl OptLevel {
30     fn to_wasmtime(&self) -> wasmtime::OptLevel {
31         match self {
32             OptLevel::None => wasmtime::OptLevel::None,
33             OptLevel::Speed => wasmtime::OptLevel::Speed,
34             OptLevel::SpeedAndSize => wasmtime::OptLevel::SpeedAndSize,
35         }
36     }
37 }
38 
39 /// Configuration for `wasmtime::PoolingAllocationStrategy`.
40 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
41 pub enum PoolingAllocationStrategy {
42     /// Use next available instance slot.
43     NextAvailable,
44     /// Use random instance slot.
45     Random,
46     /// Use an affinity-based strategy.
47     ReuseAffinity,
48 }
49 
50 impl PoolingAllocationStrategy {
51     fn to_wasmtime(&self) -> wasmtime::PoolingAllocationStrategy {
52         match self {
53             PoolingAllocationStrategy::NextAvailable => {
54                 wasmtime::PoolingAllocationStrategy::NextAvailable
55             }
56             PoolingAllocationStrategy::Random => wasmtime::PoolingAllocationStrategy::Random,
57             PoolingAllocationStrategy::ReuseAffinity => {
58                 wasmtime::PoolingAllocationStrategy::ReuseAffinity
59             }
60         }
61     }
62 }
63 
64 /// Configuration for `wasmtime::ModuleLimits`.
65 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
66 pub struct ModuleLimits {
67     imported_functions: u32,
68     imported_tables: u32,
69     imported_memories: u32,
70     imported_globals: u32,
71     types: u32,
72     functions: u32,
73     tables: u32,
74     memories: u32,
75     /// The maximum number of globals that can be defined in a module.
76     pub globals: u32,
77     table_elements: u32,
78     memory_pages: u64,
79 }
80 
81 impl ModuleLimits {
82     fn to_wasmtime(&self) -> wasmtime::ModuleLimits {
83         wasmtime::ModuleLimits {
84             imported_functions: self.imported_functions,
85             imported_tables: self.imported_tables,
86             imported_memories: self.imported_memories,
87             imported_globals: self.imported_globals,
88             types: self.types,
89             functions: self.functions,
90             tables: self.tables,
91             memories: self.memories,
92             globals: self.globals,
93             table_elements: self.table_elements,
94             memory_pages: self.memory_pages,
95         }
96     }
97 }
98 
99 impl<'a> Arbitrary<'a> for ModuleLimits {
100     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
101         const MAX_IMPORTS: u32 = 1000;
102         const MAX_TYPES: u32 = 1000;
103         const MAX_FUNCTIONS: u32 = 1000;
104         const MAX_TABLES: u32 = 10;
105         const MAX_MEMORIES: u32 = 10;
106         const MAX_GLOBALS: u32 = 1000;
107         const MAX_ELEMENTS: u32 = 1000;
108         const MAX_MEMORY_PAGES: u64 = 160; // 10 MiB
109 
110         Ok(Self {
111             imported_functions: u.int_in_range(0..=MAX_IMPORTS)?,
112             imported_tables: u.int_in_range(0..=MAX_IMPORTS)?,
113             imported_memories: u.int_in_range(0..=MAX_IMPORTS)?,
114             imported_globals: u.int_in_range(0..=MAX_IMPORTS)?,
115             types: u.int_in_range(0..=MAX_TYPES)?,
116             functions: u.int_in_range(0..=MAX_FUNCTIONS)?,
117             tables: u.int_in_range(0..=MAX_TABLES)?,
118             memories: u.int_in_range(0..=MAX_MEMORIES)?,
119             globals: u.int_in_range(0..=MAX_GLOBALS)?,
120             table_elements: u.int_in_range(0..=MAX_ELEMENTS)?,
121             memory_pages: u.int_in_range(0..=MAX_MEMORY_PAGES)?,
122         })
123     }
124 }
125 
126 /// Configuration for `wasmtime::PoolingAllocationStrategy`.
127 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
128 pub struct InstanceLimits {
129     /// The maximum number of instances that can be instantiated in the pool at a time.
130     pub count: u32,
131 }
132 
133 impl InstanceLimits {
134     fn to_wasmtime(&self) -> wasmtime::InstanceLimits {
135         wasmtime::InstanceLimits { count: self.count }
136     }
137 }
138 
139 impl<'a> Arbitrary<'a> for InstanceLimits {
140     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
141         const MAX_COUNT: u32 = 100;
142 
143         Ok(Self {
144             count: u.int_in_range(1..=MAX_COUNT)?,
145         })
146     }
147 }
148 
149 /// Configuration for `wasmtime::InstanceAllocationStrategy`.
150 #[derive(Arbitrary, Clone, Debug, Eq, PartialEq, Hash)]
151 pub enum InstanceAllocationStrategy {
152     /// Use the on-demand instance allocation strategy.
153     OnDemand,
154     /// Use the pooling instance allocation strategy.
155     Pooling {
156         /// The pooling strategy to use.
157         strategy: PoolingAllocationStrategy,
158         /// The module limits.
159         module_limits: ModuleLimits,
160         /// The instance limits.
161         instance_limits: InstanceLimits,
162     },
163 }
164 
165 impl InstanceAllocationStrategy {
166     fn to_wasmtime(&self) -> wasmtime::InstanceAllocationStrategy {
167         match self {
168             InstanceAllocationStrategy::OnDemand => wasmtime::InstanceAllocationStrategy::OnDemand,
169             InstanceAllocationStrategy::Pooling {
170                 strategy,
171                 module_limits,
172                 instance_limits,
173             } => wasmtime::InstanceAllocationStrategy::Pooling {
174                 strategy: strategy.to_wasmtime(),
175                 module_limits: module_limits.to_wasmtime(),
176                 instance_limits: instance_limits.to_wasmtime(),
177             },
178         }
179     }
180 }
181 
182 /// Configuration for `wasmtime::Config` and generated modules for a session of
183 /// fuzzing.
184 ///
185 /// This configuration guides what modules are generated, how wasmtime
186 /// configuration is generated, and is typically itself generated through a call
187 /// to `Arbitrary` which allows for a form of "swarm testing".
188 #[derive(Debug, Clone)]
189 pub struct Config {
190     /// Configuration related to the `wasmtime::Config`.
191     pub wasmtime: WasmtimeConfig,
192     /// Configuration related to generated modules.
193     pub module_config: ModuleConfig,
194 }
195 
196 impl<'a> Arbitrary<'a> for Config {
197     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
198         let mut config = Self {
199             wasmtime: u.arbitrary()?,
200             module_config: u.arbitrary()?,
201         };
202 
203         // If using the pooling allocator, constrain the memory and module configurations
204         // to the module limits.
205         if let InstanceAllocationStrategy::Pooling {
206             module_limits: limits,
207             ..
208         } = &config.wasmtime.strategy
209         {
210             // Force the use of a normal memory config when using the pooling allocator and
211             // limit the static memory maximum to be the same as the pooling allocator's memory
212             // page limit.
213             config.wasmtime.memory_config = match config.wasmtime.memory_config {
214                 MemoryConfig::Normal(mut config) => {
215                     config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
216                     MemoryConfig::Normal(config)
217                 }
218                 MemoryConfig::CustomUnaligned => {
219                     let mut config: NormalMemoryConfig = u.arbitrary()?;
220                     config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
221                     MemoryConfig::Normal(config)
222                 }
223             };
224 
225             let cfg = &mut config.module_config.config;
226             cfg.max_imports = limits.imported_functions.min(
227                 limits
228                     .imported_globals
229                     .min(limits.imported_memories.min(limits.imported_tables)),
230             ) as usize;
231             cfg.max_types = limits.types as usize;
232             cfg.max_funcs = limits.functions as usize;
233             cfg.max_globals = limits.globals as usize;
234             cfg.max_memories = limits.memories as usize;
235             cfg.max_tables = limits.tables as usize;
236             cfg.max_memory_pages = limits.memory_pages;
237 
238             // Force no aliases in any generated modules as they might count against the
239             // import limits above.
240             cfg.max_aliases = 0;
241         }
242 
243         Ok(config)
244     }
245 }
246 
247 /// Configuration related to `wasmtime::Config` and the various settings which
248 /// can be tweaked from within.
249 #[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)]
250 pub struct WasmtimeConfig {
251     opt_level: OptLevel,
252     debug_info: bool,
253     canonicalize_nans: bool,
254     interruptable: bool,
255     pub(crate) consume_fuel: bool,
256     /// The Wasmtime memory configuration to use.
257     pub memory_config: MemoryConfig,
258     force_jump_veneers: bool,
259     memfd: bool,
260     use_precompiled_cwasm: bool,
261     /// Configuration for the instance allocation strategy to use.
262     pub strategy: InstanceAllocationStrategy,
263     codegen: CodegenSettings,
264 }
265 
266 /// Configuration for linear memories in Wasmtime.
267 #[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)]
268 pub enum MemoryConfig {
269     /// Configuration for linear memories which correspond to normal
270     /// configuration settings in `wasmtime` itself. This will tweak various
271     /// parameters about static/dynamic memories.
272     Normal(NormalMemoryConfig),
273 
274     /// Configuration to force use of a linear memory that's unaligned at its
275     /// base address to force all wasm addresses to be unaligned at the hardware
276     /// level, even if the wasm itself correctly aligns everything internally.
277     CustomUnaligned,
278 }
279 
280 /// Represents a normal memory configuration for Wasmtime with the given
281 /// static and dynamic memory sizes.
282 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
283 pub struct NormalMemoryConfig {
284     static_memory_maximum_size: Option<u64>,
285     static_memory_guard_size: Option<u64>,
286     dynamic_memory_guard_size: Option<u64>,
287     guard_before_linear_memory: bool,
288 }
289 
290 impl<'a> Arbitrary<'a> for NormalMemoryConfig {
291     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
292         // This attempts to limit memory and guard sizes to 32-bit ranges so
293         // we don't exhaust a 64-bit address space easily.
294         Ok(Self {
295             static_memory_maximum_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
296             static_memory_guard_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
297             dynamic_memory_guard_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
298             guard_before_linear_memory: u.arbitrary()?,
299         })
300     }
301 }
302 
303 impl Config {
304     /// Indicates that this configuration is being used for differential
305     /// execution so only a single function should be generated since that's all
306     /// that's going to be exercised.
307     pub fn set_differential_config(&mut self) {
308         let config = &mut self.module_config.config;
309 
310         config.allow_start_export = false;
311         // Make sure there's a type available for the function.
312         config.min_types = 1;
313         config.max_types = 1;
314 
315         // Generate one and only one function
316         config.min_funcs = 1;
317         config.max_funcs = 1;
318 
319         // Give the function a memory, but keep it small
320         config.min_memories = 1;
321         config.max_memories = 1;
322         config.max_memory_pages = 1;
323         config.memory_max_size_required = true;
324 
325         // Don't allow any imports
326         config.max_imports = 0;
327 
328         // Try to get the function and the memory exported
329         config.min_exports = 2;
330         config.max_exports = 4;
331 
332         // NaN is canonicalized at the wasm level for differential fuzzing so we
333         // can paper over NaN differences between engines.
334         config.canonicalize_nans = true;
335 
336         // When diffing against a non-wasmtime engine then disable wasm
337         // features to get selectively re-enabled against each differential
338         // engine.
339         config.bulk_memory_enabled = false;
340         config.reference_types_enabled = false;
341         config.simd_enabled = false;
342         config.memory64_enabled = false;
343 
344         // If using the pooling allocator, update the module limits too
345         if let InstanceAllocationStrategy::Pooling {
346             module_limits: limits,
347             ..
348         } = &mut self.wasmtime.strategy
349         {
350             // No imports
351             limits.imported_functions = 0;
352             limits.imported_tables = 0;
353             limits.imported_memories = 0;
354             limits.imported_globals = 0;
355 
356             // One type, one function, and one single-page memory
357             limits.types = 1;
358             limits.functions = 1;
359             limits.memories = 1;
360             limits.memory_pages = 1;
361 
362             match &mut self.wasmtime.memory_config {
363                 MemoryConfig::Normal(config) => {
364                     config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
365                 }
366                 MemoryConfig::CustomUnaligned => unreachable!(), // Arbitrary impl for `Config` should have prevented this
367             }
368         }
369     }
370 
371     /// Uses this configuration and the supplied source of data to generate
372     /// a wasm module.
373     ///
374     /// If a `default_fuel` is provided, the resulting module will be configured
375     /// to ensure termination; as doing so will add an additional global to the module,
376     /// the pooling allocator, if configured, will also have its globals limit updated.
377     pub fn generate(
378         &mut self,
379         input: &mut Unstructured<'_>,
380         default_fuel: Option<u32>,
381     ) -> arbitrary::Result<wasm_smith::Module> {
382         let mut module = wasm_smith::Module::new(self.module_config.config.clone(), input)?;
383 
384         if let Some(default_fuel) = default_fuel {
385             module.ensure_termination(default_fuel);
386 
387             // Bump the allowed global count by 1
388             if let InstanceAllocationStrategy::Pooling { module_limits, .. } =
389                 &mut self.wasmtime.strategy
390             {
391                 module_limits.globals += 1;
392             }
393         }
394 
395         Ok(module)
396     }
397 
398     /// Indicates that this configuration should be spec-test-compliant,
399     /// disabling various features the spec tests assert are disabled.
400     pub fn set_spectest_compliant(&mut self) {
401         let config = &mut self.module_config.config;
402         config.memory64_enabled = false;
403         config.simd_enabled = false;
404         config.bulk_memory_enabled = true;
405         config.reference_types_enabled = true;
406         config.max_memories = 1;
407 
408         if let InstanceAllocationStrategy::Pooling { module_limits, .. } =
409             &mut self.wasmtime.strategy
410         {
411             module_limits.memories = 1;
412         }
413     }
414 
415     /// Converts this to a `wasmtime::Config` object
416     pub fn to_wasmtime(&self) -> wasmtime::Config {
417         crate::init_fuzzing();
418 
419         let mut cfg = wasmtime::Config::new();
420         cfg.wasm_bulk_memory(true)
421             .wasm_reference_types(true)
422             .wasm_module_linking(self.module_config.config.module_linking_enabled)
423             .wasm_multi_memory(self.module_config.config.max_memories > 1)
424             .wasm_simd(self.module_config.config.simd_enabled)
425             .wasm_memory64(self.module_config.config.memory64_enabled)
426             .cranelift_nan_canonicalization(self.wasmtime.canonicalize_nans)
427             .cranelift_opt_level(self.wasmtime.opt_level.to_wasmtime())
428             .interruptable(self.wasmtime.interruptable)
429             .consume_fuel(self.wasmtime.consume_fuel)
430             .memfd(self.wasmtime.memfd)
431             .allocation_strategy(self.wasmtime.strategy.to_wasmtime());
432 
433         self.wasmtime.codegen.configure(&mut cfg);
434 
435         // If the wasm-smith-generated module use nan canonicalization then we
436         // don't need to enable it, but if it doesn't enable it already then we
437         // enable this codegen option.
438         cfg.cranelift_nan_canonicalization(!self.module_config.config.canonicalize_nans);
439 
440         // Enabling the verifier will at-least-double compilation time, which
441         // with a 20-30x slowdown in fuzzing can cause issues related to
442         // timeouts. If generated modules can have more than a small handful of
443         // functions then disable the verifier when fuzzing to try to lessen the
444         // impact of timeouts.
445         if self.module_config.config.max_funcs > 10 {
446             cfg.cranelift_debug_verifier(false);
447         }
448 
449         if self.wasmtime.force_jump_veneers {
450             unsafe {
451                 cfg.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true")
452                     .unwrap();
453             }
454         }
455 
456         match &self.wasmtime.memory_config {
457             MemoryConfig::Normal(memory_config) => {
458                 cfg.static_memory_maximum_size(
459                     memory_config.static_memory_maximum_size.unwrap_or(0),
460                 )
461                 .static_memory_guard_size(memory_config.static_memory_guard_size.unwrap_or(0))
462                 .dynamic_memory_guard_size(memory_config.dynamic_memory_guard_size.unwrap_or(0))
463                 .guard_before_linear_memory(memory_config.guard_before_linear_memory);
464             }
465             MemoryConfig::CustomUnaligned => {
466                 cfg.with_host_memory(Arc::new(UnalignedMemoryCreator))
467                     .static_memory_maximum_size(0)
468                     .dynamic_memory_guard_size(0)
469                     .static_memory_guard_size(0)
470                     .guard_before_linear_memory(false);
471             }
472         }
473 
474         return cfg;
475     }
476 
477     /// Convenience function for generating a `Store<T>` using this
478     /// configuration.
479     pub fn to_store(&self) -> Store<StoreLimits> {
480         let engine = Engine::new(&self.to_wasmtime()).unwrap();
481         let mut store = Store::new(&engine, StoreLimits::new());
482         self.configure_store(&mut store);
483         store
484     }
485 
486     /// Configures a store based on this configuration.
487     pub fn configure_store(&self, store: &mut Store<StoreLimits>) {
488         store.limiter(|s| s as &mut dyn wasmtime::ResourceLimiter);
489         if self.wasmtime.consume_fuel {
490             store.add_fuel(u64::max_value()).unwrap();
491         }
492     }
493 
494     /// Generates an arbitrary method of timing out an instance, ensuring that
495     /// this configuration supports the returned timeout.
496     pub fn generate_timeout(&mut self, u: &mut Unstructured<'_>) -> arbitrary::Result<Timeout> {
497         if u.arbitrary()? {
498             self.wasmtime.interruptable = true;
499             Ok(Timeout::Time(Duration::from_secs(20)))
500         } else {
501             self.wasmtime.consume_fuel = true;
502             Ok(Timeout::Fuel(100_000))
503         }
504     }
505 
506     /// Compiles the `wasm` within the `engine` provided.
507     ///
508     /// This notably will use `Module::{serialize,deserialize_file}` to
509     /// round-trip if configured in the fuzzer.
510     pub fn compile(&self, engine: &Engine, wasm: &[u8]) -> Result<Module> {
511         // Propagate this error in case the caller wants to handle
512         // valid-vs-invalid wasm.
513         let module = Module::new(engine, wasm)?;
514         if !self.wasmtime.use_precompiled_cwasm {
515             return Ok(module);
516         }
517 
518         // Don't propagate these errors to prevent them from accidentally being
519         // interpreted as invalid wasm, these should never fail on a
520         // well-behaved host system.
521         let file = tempfile::NamedTempFile::new().unwrap();
522         std::fs::write(file.path(), module.serialize().unwrap()).unwrap();
523         unsafe { Ok(Module::deserialize_file(engine, file.path()).unwrap()) }
524     }
525 }
526 
527 struct UnalignedMemoryCreator;
528 
529 unsafe impl MemoryCreator for UnalignedMemoryCreator {
530     fn new_memory(
531         &self,
532         _ty: MemoryType,
533         minimum: usize,
534         maximum: Option<usize>,
535         reserved_size_in_bytes: Option<usize>,
536         guard_size_in_bytes: usize,
537     ) -> Result<Box<dyn LinearMemory>, String> {
538         assert_eq!(guard_size_in_bytes, 0);
539         assert!(reserved_size_in_bytes.is_none() || reserved_size_in_bytes == Some(0));
540         Ok(Box::new(UnalignedMemory {
541             src: vec![0; minimum + 1],
542             maximum,
543         }))
544     }
545 }
546 
547 /// A custom "linear memory allocator" for wasm which only works with the
548 /// "dynamic" mode of configuration where wasm always does explicit bounds
549 /// checks.
550 ///
551 /// This memory attempts to always use unaligned host addresses for the base
552 /// address of linear memory with wasm. This means that all jit loads/stores
553 /// should be unaligned, which is a "big hammer way" of testing that all our JIT
554 /// code works with unaligned addresses since alignment is not required for
555 /// correctness in wasm itself.
556 struct UnalignedMemory {
557     /// This memory is always one byte larger than the actual size of linear
558     /// memory.
559     src: Vec<u8>,
560     maximum: Option<usize>,
561 }
562 
563 unsafe impl LinearMemory for UnalignedMemory {
564     fn byte_size(&self) -> usize {
565         // Chop off the extra byte reserved for the true byte size of this
566         // linear memory.
567         self.src.len() - 1
568     }
569 
570     fn maximum_byte_size(&self) -> Option<usize> {
571         self.maximum
572     }
573 
574     fn grow_to(&mut self, new_size: usize) -> Result<()> {
575         // Make sure to allocate an extra byte for our "unalignment"
576         self.src.resize(new_size + 1, 0);
577         Ok(())
578     }
579 
580     fn as_ptr(&self) -> *mut u8 {
581         // Return our allocated memory, offset by one, so that the base address
582         // of memory is always unaligned.
583         self.src[1..].as_ptr() as *mut _
584     }
585 }
586 
587 include!(concat!(env!("OUT_DIR"), "/spectests.rs"));
588 
589 /// A spec test from the upstream wast testsuite, arbitrarily chosen from the
590 /// list of known spec tests.
591 #[derive(Debug)]
592 pub struct SpecTest {
593     /// The filename of the spec test
594     pub file: &'static str,
595     /// The `*.wast` contents of the spec test
596     pub contents: &'static str,
597 }
598 
599 impl<'a> Arbitrary<'a> for SpecTest {
600     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
601         // NB: this does get a uniform value in the provided range.
602         let i = u.int_in_range(0..=FILES.len() - 1)?;
603         let (file, contents) = FILES[i];
604         Ok(SpecTest { file, contents })
605     }
606 
607     fn size_hint(_depth: usize) -> (usize, Option<usize>) {
608         (1, Some(std::mem::size_of::<usize>()))
609     }
610 }
611 
612 /// Default module-level configuration for fuzzing Wasmtime.
613 ///
614 /// Internally this uses `wasm-smith`'s own `SwarmConfig` but we further refine
615 /// the defaults here as well.
616 #[derive(Debug, Clone)]
617 pub struct ModuleConfig {
618     #[allow(missing_docs)]
619     pub config: SwarmConfig,
620 }
621 
622 impl<'a> Arbitrary<'a> for ModuleConfig {
623     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<ModuleConfig> {
624         let mut config = SwarmConfig::arbitrary(u)?;
625 
626         // Allow multi-memory by default.
627         config.max_memories = config.max_memories.max(2);
628 
629         // Allow multi-table by default.
630         config.max_tables = config.max_tables.max(4);
631 
632         // Allow enabling some various wasm proposals by default.
633         config.bulk_memory_enabled = u.arbitrary()?;
634         config.reference_types_enabled = u.arbitrary()?;
635         config.simd_enabled = u.arbitrary()?;
636         config.memory64_enabled = u.arbitrary()?;
637 
638         Ok(ModuleConfig { config })
639     }
640 }
641 
642 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
643 enum CodegenSettings {
644     Native,
645     #[allow(dead_code)]
646     Target {
647         target: String,
648         flags: Vec<(String, String)>,
649     },
650 }
651 
652 impl CodegenSettings {
653     fn configure(&self, config: &mut wasmtime::Config) {
654         match self {
655             CodegenSettings::Native => {}
656             CodegenSettings::Target { target, flags } => {
657                 config.target(target).unwrap();
658                 for (key, value) in flags {
659                     unsafe {
660                         config.cranelift_flag_set(key, value).unwrap();
661                     }
662                 }
663             }
664         }
665     }
666 }
667 
668 impl<'a> Arbitrary<'a> for CodegenSettings {
669     #[allow(unused_macros, unused_variables)]
670     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
671         // Helper macro to enable clif features based on what the native host
672         // supports. If the input says to enable a feature and the host doesn't
673         // support it then that test case is rejected with a warning.
674         macro_rules! target_features {
675             (
676                 test:$test:ident,
677                 $(std: $std:tt => clif: $clif:tt $(ratio: $a:tt in $b:tt)?,)*
678             ) => ({
679                 let mut flags = Vec::new();
680                 $(
681                     let (low, hi) = (1, 2);
682                     $(let (low, hi) = ($a, $b);)?
683                     let enable = u.ratio(low, hi)?;
684                     if enable && !std::$test!($std) {
685                         log::error!("want to enable clif `{}` but host doesn't support it",
686                             $clif);
687                         return Err(arbitrary::Error::EmptyChoose)
688                     }
689                     flags.push((
690                         $clif.to_string(),
691                         enable.to_string(),
692                     ));
693                 )*
694                 flags
695             })
696         }
697         #[cfg(target_arch = "x86_64")]
698         {
699             if u.ratio(1, 10)? {
700                 let flags = target_features! {
701                     test: is_x86_feature_detected,
702                     std:"sse3" => clif:"has_sse3",
703                     std:"ssse3" => clif:"has_ssse3",
704                     std:"sse4.1" => clif:"has_sse41",
705                     std:"sse4.2" => clif:"has_sse42",
706                     std:"popcnt" => clif:"has_popcnt",
707                     std:"avx" => clif:"has_avx",
708                     std:"avx2" => clif:"has_avx2",
709                     std:"bmi1" => clif:"has_bmi1",
710                     std:"bmi2" => clif:"has_bmi2",
711                     std:"lzcnt" => clif:"has_lzcnt",
712 
713                     // not a lot of of cpus support avx512 so these are weighted
714                     // to get enabled much less frequently.
715                     std:"avx512bitalg" => clif:"has_avx512bitalg" ratio:1 in 1000,
716                     std:"avx512dq" => clif:"has_avx512dq" ratio: 1 in 1000,
717                     std:"avx512f" => clif:"has_avx512f" ratio: 1 in 1000,
718                     std:"avx512vl" => clif:"has_avx512vl" ratio: 1 in 1000,
719                     std:"avx512vbmi" => clif:"has_avx512vbmi" ratio: 1 in 1000,
720                 };
721                 return Ok(CodegenSettings::Target {
722                     target: target_lexicon::Triple::host().to_string(),
723                     flags,
724                 });
725             }
726         }
727         Ok(CodegenSettings::Native)
728     }
729 }
730