1 //! Generate a Wasm module and the configuration for generating it.
2 
3 use arbitrary::{Arbitrary, Unstructured};
4 use wasm_smith::SwarmConfig;
5 
6 /// Default module-level configuration for fuzzing Wasmtime.
7 ///
8 /// Internally this uses `wasm-smith`'s own `SwarmConfig` but we further refine
9 /// the defaults here as well.
10 #[derive(Debug, Clone)]
11 pub struct ModuleConfig {
12     #[allow(missing_docs)]
13     pub config: SwarmConfig,
14 }
15 
16 impl<'a> Arbitrary<'a> for ModuleConfig {
17     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<ModuleConfig> {
18         let mut config = SwarmConfig::arbitrary(u)?;
19 
20         // Allow multi-memory but make it unlikely
21         if u.ratio(1, 20)? {
22             config.max_memories = config.max_memories.max(2);
23         } else {
24             config.max_memories = 1;
25         }
26 
27         // Allow multi-table by default.
28         if config.reference_types_enabled {
29             config.max_tables = config.max_tables.max(4);
30         }
31 
32         // Allow enabling some various wasm proposals by default. Note that
33         // these are all unconditionally turned off even with
34         // `SwarmConfig::arbitrary`.
35         config.memory64_enabled = u.ratio(1, 20)?;
36 
37         // Allow the threads proposal if memory64 is not already enabled. FIXME:
38         // to allow threads and memory64 to coexist, see
39         // https://github.com/bytecodealliance/wasmtime/issues/4267.
40         config.threads_enabled = !config.memory64_enabled && u.ratio(1, 20)?;
41 
42         // We get better differential execution when we disallow traps, so we'll
43         // do that most of the time.
44         config.disallow_traps = u.ratio(9, 10)?;
45 
46         Ok(ModuleConfig { config })
47     }
48 }
49 
50 impl ModuleConfig {
51     /// Uses this configuration and the supplied source of data to generate a
52     /// Wasm module.
53     ///
54     /// If a `default_fuel` is provided, the resulting module will be configured
55     /// to ensure termination; as doing so will add an additional global to the
56     /// module, the pooling allocator, if configured, must also have its globals
57     /// limit updated.
58     pub fn generate(
59         &self,
60         input: &mut Unstructured<'_>,
61         default_fuel: Option<u32>,
62     ) -> arbitrary::Result<wasm_smith::Module> {
63         let mut module = wasm_smith::Module::new(self.config.clone(), input)?;
64 
65         if let Some(default_fuel) = default_fuel {
66             module.ensure_termination(default_fuel);
67         }
68 
69         Ok(module)
70     }
71 }
72