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         // This list is intended to be the definintive source of truth for
20         // what's at least possible to fuzz within Wasmtime. This is a
21         // combination of features in `wasm-smith` where some proposals are
22         // on-by-default (as determined by fuzz input) and others are
23         // off-by-default (as they aren't stage4+). Wasmtime will default-fuzz
24         // proposals that a pre-stage-4 to test our own implementation. Wasmtime
25         // might also unconditionally disable proposals that it doesn't
26         // implement yet which are stage4+. This is intended to be an exhaustive
27         // list of all the wasm proposals that `wasm-smith` supports and the
28         // fuzzing status within Wasmtime too.
29         let _ = config.multi_value_enabled;
30         let _ = config.saturating_float_to_int_enabled;
31         let _ = config.sign_extension_ops_enabled;
32         let _ = config.bulk_memory_enabled;
33         let _ = config.reference_types_enabled;
34         let _ = config.simd_enabled;
35         let _ = config.relaxed_simd_enabled;
36         let _ = config.tail_call_enabled;
37         config.exceptions_enabled = false;
38         config.gc_enabled = false;
39         config.wide_arithmetic_enabled = u.arbitrary()?;
40         config.memory64_enabled = u.ratio(1, 20)?;
41         // Allow the threads proposal if memory64 is not already enabled. FIXME:
42         // to allow threads and memory64 to coexist, see
43         // https://github.com/bytecodealliance/wasmtime/issues/4267.
44         config.threads_enabled = !config.memory64_enabled && u.ratio(1, 20)?;
45         // FIXME: this may be safe to enable
46         config.custom_page_sizes_enabled = false;
47         // Allow multi-memory but make it unlikely
48         if u.ratio(1, 20)? {
49             config.max_memories = config.max_memories.max(2);
50         } else {
51             config.max_memories = 1;
52         }
53         // ... NB: if you add something above this line please be sure to update
54         // `docs/stability-wasm-proposals.md`
55 
56         // We get better differential execution when we disallow traps, so we'll
57         // do that most of the time.
58         config.disallow_traps = u.ratio(9, 10)?;
59 
60         Ok(ModuleConfig { config })
61     }
62 }
63 
64 impl ModuleConfig {
65     /// Uses this configuration and the supplied source of data to generate a
66     /// Wasm module.
67     ///
68     /// If a `default_fuel` is provided, the resulting module will be configured
69     /// to ensure termination; as doing so will add an additional global to the
70     /// module, the pooling allocator, if configured, must also have its globals
71     /// limit updated.
72     pub fn generate(
73         &self,
74         input: &mut Unstructured<'_>,
75         default_fuel: Option<u32>,
76     ) -> arbitrary::Result<wasm_smith::Module> {
77         let mut module = wasm_smith::Module::new(self.config.clone(), input)?;
78 
79         if let Some(default_fuel) = default_fuel {
80             module.ensure_termination(default_fuel).unwrap();
81         }
82 
83         Ok(module)
84     }
85 }
86