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