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 /// Configuration for `wasmtime::PoolingAllocationStrategy`.
64 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
65 #[allow(missing_docs)]
66 pub struct InstanceLimits {
67     pub count: u32,
68     pub memories: u32,
69     pub tables: u32,
70     pub memory_pages: u64,
71     pub table_elements: u32,
72     pub size: usize,
73 }
74 
75 impl InstanceLimits {
76     fn to_wasmtime(&self) -> wasmtime::InstanceLimits {
77         wasmtime::InstanceLimits {
78             count: self.count,
79             memories: self.memories,
80             tables: self.tables,
81             memory_pages: self.memory_pages,
82             table_elements: self.table_elements,
83             size: self.size,
84         }
85     }
86 }
87 
88 impl<'a> Arbitrary<'a> for InstanceLimits {
89     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
90         const MAX_COUNT: u32 = 100;
91 
92         const MAX_TABLES: u32 = 10;
93         const MAX_MEMORIES: u32 = 10;
94         const MAX_ELEMENTS: u32 = 1000;
95         const MAX_MEMORY_PAGES: u64 = 160; // 10 MiB
96         const MAX_SIZE: usize = 1 << 20; // 1 MiB
97 
98         Ok(Self {
99             tables: u.int_in_range(0..=MAX_TABLES)?,
100             memories: u.int_in_range(0..=MAX_MEMORIES)?,
101             table_elements: u.int_in_range(0..=MAX_ELEMENTS)?,
102             memory_pages: u.int_in_range(0..=MAX_MEMORY_PAGES)?,
103             count: u.int_in_range(1..=MAX_COUNT)?,
104             size: u.int_in_range(0..=MAX_SIZE)?,
105         })
106     }
107 }
108 
109 /// Configuration for `wasmtime::InstanceAllocationStrategy`.
110 #[derive(Arbitrary, Clone, Debug, Eq, PartialEq, Hash)]
111 pub enum InstanceAllocationStrategy {
112     /// Use the on-demand instance allocation strategy.
113     OnDemand,
114     /// Use the pooling instance allocation strategy.
115     Pooling {
116         /// The pooling strategy to use.
117         strategy: PoolingAllocationStrategy,
118         /// The instance limits.
119         instance_limits: InstanceLimits,
120     },
121 }
122 
123 impl InstanceAllocationStrategy {
124     fn to_wasmtime(&self) -> wasmtime::InstanceAllocationStrategy {
125         match self {
126             InstanceAllocationStrategy::OnDemand => wasmtime::InstanceAllocationStrategy::OnDemand,
127             InstanceAllocationStrategy::Pooling {
128                 strategy,
129                 instance_limits,
130             } => wasmtime::InstanceAllocationStrategy::Pooling {
131                 strategy: strategy.to_wasmtime(),
132                 instance_limits: instance_limits.to_wasmtime(),
133             },
134         }
135     }
136 }
137 
138 /// Configuration for `wasmtime::Config` and generated modules for a session of
139 /// fuzzing.
140 ///
141 /// This configuration guides what modules are generated, how wasmtime
142 /// configuration is generated, and is typically itself generated through a call
143 /// to `Arbitrary` which allows for a form of "swarm testing".
144 #[derive(Debug, Clone)]
145 pub struct Config {
146     /// Configuration related to the `wasmtime::Config`.
147     pub wasmtime: WasmtimeConfig,
148     /// Configuration related to generated modules.
149     pub module_config: ModuleConfig,
150 }
151 
152 impl<'a> Arbitrary<'a> for Config {
153     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
154         let mut config = Self {
155             wasmtime: u.arbitrary()?,
156             module_config: u.arbitrary()?,
157         };
158 
159         // If using the pooling allocator, constrain the memory and module configurations
160         // to the module limits.
161         if let InstanceAllocationStrategy::Pooling {
162             instance_limits: limits,
163             ..
164         } = &config.wasmtime.strategy
165         {
166             // If the pooling allocator is used, do not allow shared memory to
167             // be created. FIXME: see
168             // https://github.com/bytecodealliance/wasmtime/issues/4244.
169             config.module_config.config.threads_enabled = false;
170 
171             // Force the use of a normal memory config when using the pooling allocator and
172             // limit the static memory maximum to be the same as the pooling allocator's memory
173             // page limit.
174             config.wasmtime.memory_config = match config.wasmtime.memory_config {
175                 MemoryConfig::Normal(mut config) => {
176                     config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
177                     MemoryConfig::Normal(config)
178                 }
179                 MemoryConfig::CustomUnaligned => {
180                     let mut config: NormalMemoryConfig = u.arbitrary()?;
181                     config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
182                     MemoryConfig::Normal(config)
183                 }
184             };
185 
186             let cfg = &mut config.module_config.config;
187             cfg.max_memories = limits.memories as usize;
188             cfg.max_tables = limits.tables as usize;
189             cfg.max_memory_pages = limits.memory_pages;
190 
191             // Force no aliases in any generated modules as they might count against the
192             // import limits above.
193             cfg.max_aliases = 0;
194         }
195 
196         Ok(config)
197     }
198 }
199 
200 /// Configuration related to `wasmtime::Config` and the various settings which
201 /// can be tweaked from within.
202 #[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)]
203 pub struct WasmtimeConfig {
204     opt_level: OptLevel,
205     debug_info: bool,
206     canonicalize_nans: bool,
207     interruptable: bool,
208     pub(crate) consume_fuel: bool,
209     epoch_interruption: bool,
210     /// The Wasmtime memory configuration to use.
211     pub memory_config: MemoryConfig,
212     force_jump_veneers: bool,
213     memory_init_cow: bool,
214     memory_guaranteed_dense_image_size: u64,
215     use_precompiled_cwasm: bool,
216     /// Configuration for the instance allocation strategy to use.
217     pub strategy: InstanceAllocationStrategy,
218     codegen: CodegenSettings,
219     padding_between_functions: Option<u16>,
220     generate_address_map: bool,
221     wasm_backtraces: bool,
222 }
223 
224 /// Configuration for linear memories in Wasmtime.
225 #[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)]
226 pub enum MemoryConfig {
227     /// Configuration for linear memories which correspond to normal
228     /// configuration settings in `wasmtime` itself. This will tweak various
229     /// parameters about static/dynamic memories.
230     Normal(NormalMemoryConfig),
231 
232     /// Configuration to force use of a linear memory that's unaligned at its
233     /// base address to force all wasm addresses to be unaligned at the hardware
234     /// level, even if the wasm itself correctly aligns everything internally.
235     CustomUnaligned,
236 }
237 
238 /// Represents a normal memory configuration for Wasmtime with the given
239 /// static and dynamic memory sizes.
240 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
241 pub struct NormalMemoryConfig {
242     static_memory_maximum_size: Option<u64>,
243     static_memory_guard_size: Option<u64>,
244     dynamic_memory_guard_size: Option<u64>,
245     guard_before_linear_memory: bool,
246 }
247 
248 impl<'a> Arbitrary<'a> for NormalMemoryConfig {
249     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
250         // This attempts to limit memory and guard sizes to 32-bit ranges so
251         // we don't exhaust a 64-bit address space easily.
252         Ok(Self {
253             static_memory_maximum_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
254             static_memory_guard_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
255             dynamic_memory_guard_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
256             guard_before_linear_memory: u.arbitrary()?,
257         })
258     }
259 }
260 
261 impl Config {
262     /// Indicates that this configuration is being used for differential
263     /// execution so only a single function should be generated since that's all
264     /// that's going to be exercised.
265     pub fn set_differential_config(&mut self) {
266         let config = &mut self.module_config.config;
267 
268         config.allow_start_export = false;
269         // Make sure there's a type available for the function.
270         config.min_types = 1;
271         config.max_types = 1;
272 
273         // Generate one and only one function
274         config.min_funcs = 1;
275         config.max_funcs = 1;
276 
277         // Give the function a memory, but keep it small
278         config.min_memories = 1;
279         config.max_memories = 1;
280         config.max_memory_pages = 1;
281         config.memory_max_size_required = true;
282 
283         // While reference types are disabled below, only allow one table
284         config.max_tables = 1;
285 
286         // Don't allow any imports
287         config.max_imports = 0;
288 
289         // Try to get the function and the memory exported
290         config.min_exports = 2;
291         config.max_exports = 4;
292 
293         // NaN is canonicalized at the wasm level for differential fuzzing so we
294         // can paper over NaN differences between engines.
295         config.canonicalize_nans = true;
296 
297         // When diffing against a non-wasmtime engine then disable wasm
298         // features to get selectively re-enabled against each differential
299         // engine.
300         config.bulk_memory_enabled = false;
301         config.reference_types_enabled = false;
302         config.simd_enabled = false;
303         config.memory64_enabled = false;
304         config.threads_enabled = false;
305 
306         // If using the pooling allocator, update the instance limits too
307         if let InstanceAllocationStrategy::Pooling {
308             instance_limits: limits,
309             ..
310         } = &mut self.wasmtime.strategy
311         {
312             // One single-page memory
313             limits.memories = 1;
314             limits.memory_pages = 1;
315 
316             limits.tables = 1;
317 
318             match &mut self.wasmtime.memory_config {
319                 MemoryConfig::Normal(config) => {
320                     config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
321                 }
322                 MemoryConfig::CustomUnaligned => unreachable!(), // Arbitrary impl for `Config` should have prevented this
323             }
324         }
325     }
326 
327     /// Uses this configuration and the supplied source of data to generate
328     /// a wasm module.
329     ///
330     /// If a `default_fuel` is provided, the resulting module will be configured
331     /// to ensure termination; as doing so will add an additional global to the module,
332     /// the pooling allocator, if configured, will also have its globals limit updated.
333     pub fn generate(
334         &mut self,
335         input: &mut Unstructured<'_>,
336         default_fuel: Option<u32>,
337     ) -> arbitrary::Result<wasm_smith::Module> {
338         let mut module = wasm_smith::Module::new(self.module_config.config.clone(), input)?;
339 
340         if let Some(default_fuel) = default_fuel {
341             module.ensure_termination(default_fuel);
342         }
343 
344         Ok(module)
345     }
346 
347     /// Indicates that this configuration should be spec-test-compliant,
348     /// disabling various features the spec tests assert are disabled.
349     pub fn set_spectest_compliant(&mut self) {
350         let config = &mut self.module_config.config;
351         config.memory64_enabled = false;
352         config.bulk_memory_enabled = true;
353         config.reference_types_enabled = true;
354         config.multi_value_enabled = true;
355         config.simd_enabled = true;
356         config.threads_enabled = false;
357         config.max_memories = 1;
358         config.max_tables = 5;
359 
360         if let InstanceAllocationStrategy::Pooling {
361             instance_limits: limits,
362             ..
363         } = &mut self.wasmtime.strategy
364         {
365             // Configure the lower bound of a number of limits to what's
366             // required to actually run the spec tests. Fuzz-generated inputs
367             // may have limits less than these thresholds which would cause the
368             // spec tests to fail which isn't particularly interesting.
369             limits.memories = limits.memories.max(1);
370             limits.tables = limits.memories.max(5);
371             limits.table_elements = limits.memories.max(1_000);
372             limits.memory_pages = limits.memory_pages.max(900);
373             limits.count = limits.count.max(500);
374             limits.size = limits.size.max(64 * 1024);
375 
376             match &mut self.wasmtime.memory_config {
377                 MemoryConfig::Normal(config) => {
378                     config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
379                 }
380                 MemoryConfig::CustomUnaligned => unreachable!(), // Arbitrary impl for `Config` should have prevented this
381             }
382         }
383     }
384 
385     /// Converts this to a `wasmtime::Config` object
386     pub fn to_wasmtime(&self) -> wasmtime::Config {
387         crate::init_fuzzing();
388         log::debug!("creating wasmtime config with {:#?}", self.wasmtime);
389 
390         let mut cfg = wasmtime::Config::new();
391         cfg.wasm_bulk_memory(true)
392             .wasm_reference_types(true)
393             .wasm_multi_value(self.module_config.config.multi_value_enabled)
394             .wasm_multi_memory(self.module_config.config.max_memories > 1)
395             .wasm_simd(self.module_config.config.simd_enabled)
396             .wasm_memory64(self.module_config.config.memory64_enabled)
397             .wasm_threads(self.module_config.config.threads_enabled)
398             .wasm_backtrace(self.wasmtime.wasm_backtraces)
399             .cranelift_nan_canonicalization(self.wasmtime.canonicalize_nans)
400             .cranelift_opt_level(self.wasmtime.opt_level.to_wasmtime())
401             .consume_fuel(self.wasmtime.consume_fuel)
402             .epoch_interruption(self.wasmtime.epoch_interruption)
403             .memory_init_cow(self.wasmtime.memory_init_cow)
404             .memory_guaranteed_dense_image_size(std::cmp::min(
405                 // Clamp this at 16MiB so we don't get huge in-memory
406                 // images during fuzzing.
407                 16 << 20,
408                 self.wasmtime.memory_guaranteed_dense_image_size,
409             ))
410             .allocation_strategy(self.wasmtime.strategy.to_wasmtime())
411             .generate_address_map(self.wasmtime.generate_address_map);
412 
413         self.wasmtime.codegen.configure(&mut cfg);
414 
415         // If the wasm-smith-generated module use nan canonicalization then we
416         // don't need to enable it, but if it doesn't enable it already then we
417         // enable this codegen option.
418         cfg.cranelift_nan_canonicalization(!self.module_config.config.canonicalize_nans);
419 
420         // Enabling the verifier will at-least-double compilation time, which
421         // with a 20-30x slowdown in fuzzing can cause issues related to
422         // timeouts. If generated modules can have more than a small handful of
423         // functions then disable the verifier when fuzzing to try to lessen the
424         // impact of timeouts.
425         if self.module_config.config.max_funcs > 10 {
426             cfg.cranelift_debug_verifier(false);
427         }
428 
429         if self.wasmtime.force_jump_veneers {
430             unsafe {
431                 cfg.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true");
432             }
433         }
434 
435         if let Some(pad) = self.wasmtime.padding_between_functions {
436             unsafe {
437                 cfg.cranelift_flag_set(
438                     "wasmtime_linkopt_padding_between_functions",
439                     &pad.to_string(),
440                 );
441             }
442         }
443 
444         // Vary the memory configuration, but only if threads are not enabled.
445         // When the threads proposal is enabled we might generate shared memory,
446         // which is less amenable to different memory configurations:
447         // - shared memories are required to be "static" so fuzzing the various
448         //   memory configurations will mostly result in uninteresting errors.
449         //   The interesting part about shared memories is the runtime so we
450         //   don't fuzz non-default settings.
451         // - shared memories are required to be aligned which means that the
452         //   `CustomUnaligned` variant isn't actually safe to use with a shared
453         //   memory.
454         if !self.module_config.config.threads_enabled {
455             match &self.wasmtime.memory_config {
456                 MemoryConfig::Normal(memory_config) => {
457                     cfg.static_memory_maximum_size(
458                         memory_config.static_memory_maximum_size.unwrap_or(0),
459                     )
460                     .static_memory_guard_size(memory_config.static_memory_guard_size.unwrap_or(0))
461                     .dynamic_memory_guard_size(memory_config.dynamic_memory_guard_size.unwrap_or(0))
462                     .guard_before_linear_memory(memory_config.guard_before_linear_memory);
463                 }
464                 MemoryConfig::CustomUnaligned => {
465                     cfg.with_host_memory(Arc::new(UnalignedMemoryCreator))
466                         .static_memory_maximum_size(0)
467                         .dynamic_memory_guard_size(0)
468                         .static_memory_guard_size(0)
469                         .guard_before_linear_memory(false);
470                 }
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         if self.wasmtime.epoch_interruption {
493             // Without fuzzing of async execution, we can't test the
494             // "update deadline and continue" behavior, but we can at
495             // least test the codegen paths and checks with the
496             // trapping behavior, which works synchronously too. We'll
497             // set the deadline one epoch tick in the future; then
498             // this works exactly like an interrupt flag. We expect no
499             // traps/interrupts unless we bump the epoch, which we do
500             // as one particular Timeout mode (`Timeout::Epoch`).
501             store.epoch_deadline_trap();
502             store.set_epoch_deadline(1);
503         }
504     }
505 
506     /// Generates an arbitrary method of timing out an instance, ensuring that
507     /// this configuration supports the returned timeout.
508     pub fn generate_timeout(&mut self, u: &mut Unstructured<'_>) -> arbitrary::Result<Timeout> {
509         let time_duration = Duration::from_secs(20);
510         let timeout = u
511             .choose(&[Timeout::Fuel(100_000), Timeout::Epoch(time_duration)])?
512             .clone();
513         match &timeout {
514             Timeout::Fuel(..) => {
515                 self.wasmtime.consume_fuel = true;
516             }
517             Timeout::Epoch(..) => {
518                 self.wasmtime.epoch_interruption = true;
519             }
520             Timeout::None => unreachable!("Not an option given to choose()"),
521         }
522         Ok(timeout)
523     }
524 
525     /// Compiles the `wasm` within the `engine` provided.
526     ///
527     /// This notably will use `Module::{serialize,deserialize_file}` to
528     /// round-trip if configured in the fuzzer.
529     pub fn compile(&self, engine: &Engine, wasm: &[u8]) -> Result<Module> {
530         // Propagate this error in case the caller wants to handle
531         // valid-vs-invalid wasm.
532         let module = Module::new(engine, wasm)?;
533         if !self.wasmtime.use_precompiled_cwasm {
534             return Ok(module);
535         }
536 
537         // Don't propagate these errors to prevent them from accidentally being
538         // interpreted as invalid wasm, these should never fail on a
539         // well-behaved host system.
540         let file = tempfile::NamedTempFile::new().unwrap();
541         std::fs::write(file.path(), module.serialize().unwrap()).unwrap();
542         unsafe { Ok(Module::deserialize_file(engine, file.path()).unwrap()) }
543     }
544 }
545 
546 struct UnalignedMemoryCreator;
547 
548 unsafe impl MemoryCreator for UnalignedMemoryCreator {
549     fn new_memory(
550         &self,
551         _ty: MemoryType,
552         minimum: usize,
553         maximum: Option<usize>,
554         reserved_size_in_bytes: Option<usize>,
555         guard_size_in_bytes: usize,
556     ) -> Result<Box<dyn LinearMemory>, String> {
557         assert_eq!(guard_size_in_bytes, 0);
558         assert!(reserved_size_in_bytes.is_none() || reserved_size_in_bytes == Some(0));
559         Ok(Box::new(UnalignedMemory {
560             src: vec![0; minimum + 1],
561             maximum,
562         }))
563     }
564 }
565 
566 /// A custom "linear memory allocator" for wasm which only works with the
567 /// "dynamic" mode of configuration where wasm always does explicit bounds
568 /// checks.
569 ///
570 /// This memory attempts to always use unaligned host addresses for the base
571 /// address of linear memory with wasm. This means that all jit loads/stores
572 /// should be unaligned, which is a "big hammer way" of testing that all our JIT
573 /// code works with unaligned addresses since alignment is not required for
574 /// correctness in wasm itself.
575 struct UnalignedMemory {
576     /// This memory is always one byte larger than the actual size of linear
577     /// memory.
578     src: Vec<u8>,
579     maximum: Option<usize>,
580 }
581 
582 unsafe impl LinearMemory for UnalignedMemory {
583     fn byte_size(&self) -> usize {
584         // Chop off the extra byte reserved for the true byte size of this
585         // linear memory.
586         self.src.len() - 1
587     }
588 
589     fn maximum_byte_size(&self) -> Option<usize> {
590         self.maximum
591     }
592 
593     fn grow_to(&mut self, new_size: usize) -> Result<()> {
594         // Make sure to allocate an extra byte for our "unalignment"
595         self.src.resize(new_size + 1, 0);
596         Ok(())
597     }
598 
599     fn as_ptr(&self) -> *mut u8 {
600         // Return our allocated memory, offset by one, so that the base address
601         // of memory is always unaligned.
602         self.src[1..].as_ptr() as *mut _
603     }
604 }
605 
606 include!(concat!(env!("OUT_DIR"), "/spectests.rs"));
607 
608 /// A spec test from the upstream wast testsuite, arbitrarily chosen from the
609 /// list of known spec tests.
610 #[derive(Debug)]
611 pub struct SpecTest {
612     /// The filename of the spec test
613     pub file: &'static str,
614     /// The `*.wast` contents of the spec test
615     pub contents: &'static str,
616 }
617 
618 impl<'a> Arbitrary<'a> for SpecTest {
619     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
620         // NB: this does get a uniform value in the provided range.
621         let i = u.int_in_range(0..=FILES.len() - 1)?;
622         let (file, contents) = FILES[i];
623         Ok(SpecTest { file, contents })
624     }
625 
626     fn size_hint(_depth: usize) -> (usize, Option<usize>) {
627         (1, Some(std::mem::size_of::<usize>()))
628     }
629 }
630 
631 /// Default module-level configuration for fuzzing Wasmtime.
632 ///
633 /// Internally this uses `wasm-smith`'s own `SwarmConfig` but we further refine
634 /// the defaults here as well.
635 #[derive(Debug, Clone)]
636 pub struct ModuleConfig {
637     #[allow(missing_docs)]
638     pub config: SwarmConfig,
639 }
640 
641 impl<'a> Arbitrary<'a> for ModuleConfig {
642     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<ModuleConfig> {
643         let mut config = SwarmConfig::arbitrary(u)?;
644 
645         // Allow multi-memory by default.
646         config.max_memories = config.max_memories.max(2);
647 
648         // Allow multi-table by default.
649         config.max_tables = config.max_tables.max(4);
650 
651         // Allow enabling some various wasm proposals by default. Note that
652         // these are all unconditionally turned off even with
653         // `SwarmConfig::arbitrary`.
654         config.memory64_enabled = u.arbitrary()?;
655 
656         // Allow the threads proposal if memory64 is not already enabled. FIXME:
657         // to allow threads and memory64 to coexist, see
658         // https://github.com/bytecodealliance/wasmtime/issues/4267.
659         config.threads_enabled = !config.memory64_enabled && u.arbitrary()?;
660 
661         Ok(ModuleConfig { config })
662     }
663 }
664 
665 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
666 enum CodegenSettings {
667     Native,
668     #[allow(dead_code)]
669     Target {
670         target: String,
671         flags: Vec<(String, String)>,
672     },
673 }
674 
675 impl CodegenSettings {
676     fn configure(&self, config: &mut wasmtime::Config) {
677         match self {
678             CodegenSettings::Native => {}
679             CodegenSettings::Target { target, flags } => {
680                 config.target(target).unwrap();
681                 for (key, value) in flags {
682                     unsafe {
683                         config.cranelift_flag_set(key, value);
684                     }
685                 }
686             }
687         }
688     }
689 }
690 
691 impl<'a> Arbitrary<'a> for CodegenSettings {
692     #[allow(unused_macros, unused_variables)]
693     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
694         // Helper macro to enable clif features based on what the native host
695         // supports. If the input says to enable a feature and the host doesn't
696         // support it then that test case is rejected with a warning.
697         //
698         // Note that this specifically consumes bytes from the fuzz input for
699         // features for all targets, discarding anything which isn't applicable
700         // to the current target. The theory behind this is that most fuzz bugs
701         // won't be related to this feature selection so by consistently
702         // consuming input irrespective of the current platform reproducing fuzz
703         // bugs should be easier between different architectures.
704         macro_rules! target_features {
705             (
706                 $(
707                     $arch:tt => {
708                         test:$test:ident,
709                         $(std: $std:tt => clif: $clif:tt $(ratio: $a:tt in $b:tt)?,)*
710                     },
711                 )*
712             ) => ({
713                 let mut flags = Vec::new();
714                 $( // for each `$arch`
715                     $( // for each `$std`/`$clif` pair
716                         // Use the input to generate whether `$clif` will be
717                         // enabled. By default this is a 1 in 2 chance but each
718                         // feature supports a custom ratio as well which shadows
719                         // the (low, hi)
720                         let (low, hi) = (1, 2);
721                         $(let (low, hi) = ($a, $b);)?
722                         let enable = u.ratio(low, hi)?;
723 
724                         // If we're actually on the relevant platform and the
725                         // feature is enabled be sure to check that this host
726                         // supports it. If the host doesn't support it then
727                         // print a warning and return an error because this fuzz
728                         // input must be discarded.
729                         #[cfg(target_arch = $arch)]
730                         if enable && !std::arch::$test!($std) {
731                             log::warn!("want to enable clif `{}` but host doesn't support it",
732                                 $clif);
733                             return Err(arbitrary::Error::EmptyChoose)
734                         }
735 
736                         // And finally actually push the feature into the set of
737                         // flags to enable, but only if we're on the right
738                         // architecture.
739                         if cfg!(target_arch = $arch) {
740                             flags.push((
741                                 $clif.to_string(),
742                                 enable.to_string(),
743                             ));
744                         }
745                     )*
746                 )*
747                 flags
748             })
749         }
750         if u.ratio(1, 10)? {
751             let flags = target_features! {
752                 "x86_64" => {
753                     test: is_x86_feature_detected,
754 
755                     // These features are considered to be baseline required by
756                     // Wasmtime. Currently some SIMD code generation will
757                     // fail if these features are disabled, so unconditionally
758                     // enable them as we're not interested in fuzzing without
759                     // them.
760                     std:"sse3" => clif:"has_sse3" ratio: 1 in 1,
761                     std:"ssse3" => clif:"has_ssse3" ratio: 1 in 1,
762                     std:"sse4.1" => clif:"has_sse41" ratio: 1 in 1,
763                     std:"sse4.2" => clif:"has_sse42" ratio: 1 in 1,
764 
765                     std:"popcnt" => clif:"has_popcnt",
766                     std:"avx" => clif:"has_avx",
767                     std:"avx2" => clif:"has_avx2",
768                     std:"bmi1" => clif:"has_bmi1",
769                     std:"bmi2" => clif:"has_bmi2",
770                     std:"lzcnt" => clif:"has_lzcnt",
771 
772                     // not a lot of of cpus support avx512 so these are weighted
773                     // to get enabled much less frequently.
774                     std:"avx512bitalg" => clif:"has_avx512bitalg" ratio:1 in 1000,
775                     std:"avx512dq" => clif:"has_avx512dq" ratio: 1 in 1000,
776                     std:"avx512f" => clif:"has_avx512f" ratio: 1 in 1000,
777                     std:"avx512vl" => clif:"has_avx512vl" ratio: 1 in 1000,
778                     std:"avx512vbmi" => clif:"has_avx512vbmi" ratio: 1 in 1000,
779                 },
780                 "aarch64" => {
781                     test: is_aarch64_feature_detected,
782 
783                     std: "lse" => clif: "has_lse",
784                 },
785             };
786             return Ok(CodegenSettings::Target {
787                 target: target_lexicon::Triple::host().to_string(),
788                 flags,
789             });
790         }
791         Ok(CodegenSettings::Native)
792     }
793 }
794