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