wasmtime_fuzzing/generators/
module.rs

1//! Generate a Wasm module and the configuration for generating it.
2
3use 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#[expect(missing_docs, reason = "self-describing fields")]
11pub struct ModuleConfig {
12    pub config: wasm_smith::Config,
13
14    // These knobs aren't exposed in `wasm-smith` at this time but are exposed
15    // in our `*.wast` testing so keep knobs here so they can be read during
16    // config-to-`wasmtime::Config` translation.
17    pub function_references_enabled: bool,
18    pub component_model_async: bool,
19}
20
21impl<'a> Arbitrary<'a> for ModuleConfig {
22    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<ModuleConfig> {
23        let mut config = wasm_smith::Config::arbitrary(u)?;
24
25        // This list is intended to be the definitive source of truth for
26        // what's at least possible to fuzz within Wasmtime. This is a
27        // combination of features in `wasm-smith` where some proposals are
28        // on-by-default (as determined by fuzz input) and others are
29        // off-by-default (as they aren't stage4+). Wasmtime will default-fuzz
30        // proposals that a pre-stage-4 to test our own implementation. Wasmtime
31        // might also unconditionally disable proposals that it doesn't
32        // implement yet which are stage4+. This is intended to be an exhaustive
33        // list of all the wasm proposals that `wasm-smith` supports and the
34        // fuzzing status within Wasmtime too.
35        let _ = config.multi_value_enabled;
36        let _ = config.saturating_float_to_int_enabled;
37        let _ = config.sign_extension_ops_enabled;
38        let _ = config.bulk_memory_enabled;
39        let _ = config.reference_types_enabled;
40        let _ = config.simd_enabled;
41        let _ = config.relaxed_simd_enabled;
42        let _ = config.tail_call_enabled;
43        let _ = config.extended_const_enabled;
44        let _ = config.gc_enabled;
45        config.exceptions_enabled = false;
46        config.custom_page_sizes_enabled = u.arbitrary()?;
47        config.wide_arithmetic_enabled = u.arbitrary()?;
48        config.memory64_enabled = u.ratio(1, 20)?;
49        config.threads_enabled = u.ratio(1, 20)?;
50        // Allow multi-memory but make it unlikely
51        if u.ratio(1, 20)? {
52            config.max_memories = config.max_memories.max(2);
53        } else {
54            config.max_memories = 1;
55        }
56        // ... NB: if you add something above this line please be sure to update
57        // `docs/stability-wasm-proposals.md`
58
59        // We get better differential execution when we disallow traps, so we'll
60        // do that most of the time.
61        config.disallow_traps = u.ratio(9, 10)?;
62
63        Ok(ModuleConfig {
64            component_model_async: false,
65            function_references_enabled: config.gc_enabled,
66            config,
67        })
68    }
69}
70
71impl ModuleConfig {
72    /// Uses this configuration and the supplied source of data to generate a
73    /// Wasm module.
74    ///
75    /// If a `default_fuel` is provided, the resulting module will be configured
76    /// to ensure termination; as doing so will add an additional global to the
77    /// module, the pooling allocator, if configured, must also have its globals
78    /// limit updated.
79    pub fn generate(
80        &self,
81        input: &mut Unstructured<'_>,
82        default_fuel: Option<u32>,
83    ) -> arbitrary::Result<wasm_smith::Module> {
84        let mut module = wasm_smith::Module::new(self.config.clone(), input)?;
85
86        if let Some(default_fuel) = default_fuel {
87            module.ensure_termination(default_fuel).unwrap();
88        }
89
90        Ok(module)
91    }
92}