15ec92d59SAndrew Brown //! Generate a Wasm module and the configuration for generating it. 25ec92d59SAndrew Brown 35ec92d59SAndrew Brown use arbitrary::{Arbitrary, Unstructured}; 4e6759845SAlex Crichton use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; 55ec92d59SAndrew Brown 65ec92d59SAndrew Brown /// Default module-level configuration for fuzzing Wasmtime. 75ec92d59SAndrew Brown /// 85660a88bSAlex Crichton /// Internally this uses `wasm-smith`'s own `Config` but we further refine 95ec92d59SAndrew Brown /// the defaults here as well. 105ec92d59SAndrew Brown #[derive(Debug, Clone)] 1145b60bd6SAlex Crichton #[expect(missing_docs, reason = "self-describing fields")] 125ec92d59SAndrew Brown pub struct ModuleConfig { 135660a88bSAlex Crichton pub config: wasm_smith::Config, 14f406347aSAlex Crichton 15f406347aSAlex Crichton // These knobs aren't exposed in `wasm-smith` at this time but are exposed 16f406347aSAlex Crichton // in our `*.wast` testing so keep knobs here so they can be read during 17f406347aSAlex Crichton // config-to-`wasmtime::Config` translation. 18f406347aSAlex Crichton pub function_references_enabled: bool, 193ba13d1bSJoel Dice pub component_model_async: bool, 20c7e092d8SAndrew Brown pub component_model_async_builtins: bool, 21c7e092d8SAndrew Brown pub component_model_async_stackful: bool, 22e06fbf70SSy Brand pub component_model_threading: bool, 23c857f687SAlex Crichton pub component_model_error_context: bool, 248801023bSNick Fitzgerald pub component_model_gc: bool, 25*1b59b579SYordis Prieto pub component_model_map: bool, 26caf0f752SChristof Petig pub component_model_fixed_length_lists: bool, 27366f320dSAlex Crichton pub legacy_exceptions: bool, 280a55f804SAlex Crichton pub shared_memory: bool, 295ec92d59SAndrew Brown } 305ec92d59SAndrew Brown 315ec92d59SAndrew Brown impl<'a> Arbitrary<'a> for ModuleConfig { arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<ModuleConfig>325ec92d59SAndrew Brown fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<ModuleConfig> { 335660a88bSAlex Crichton let mut config = wasm_smith::Config::arbitrary(u)?; 345ec92d59SAndrew Brown 351e5d77d6Sshenpengfeng // This list is intended to be the definitive source of truth for 36292f136cSAlex Crichton // what's at least possible to fuzz within Wasmtime. This is a 37292f136cSAlex Crichton // combination of features in `wasm-smith` where some proposals are 38292f136cSAlex Crichton // on-by-default (as determined by fuzz input) and others are 39292f136cSAlex Crichton // off-by-default (as they aren't stage4+). Wasmtime will default-fuzz 40292f136cSAlex Crichton // proposals that a pre-stage-4 to test our own implementation. Wasmtime 41292f136cSAlex Crichton // might also unconditionally disable proposals that it doesn't 42292f136cSAlex Crichton // implement yet which are stage4+. This is intended to be an exhaustive 43292f136cSAlex Crichton // list of all the wasm proposals that `wasm-smith` supports and the 44292f136cSAlex Crichton // fuzzing status within Wasmtime too. 45292f136cSAlex Crichton let _ = config.multi_value_enabled; 46292f136cSAlex Crichton let _ = config.saturating_float_to_int_enabled; 47292f136cSAlex Crichton let _ = config.sign_extension_ops_enabled; 48292f136cSAlex Crichton let _ = config.bulk_memory_enabled; 49292f136cSAlex Crichton let _ = config.reference_types_enabled; 50292f136cSAlex Crichton let _ = config.simd_enabled; 51292f136cSAlex Crichton let _ = config.relaxed_simd_enabled; 52292f136cSAlex Crichton let _ = config.tail_call_enabled; 533aac2af4SAlex Crichton let _ = config.extended_const_enabled; 545b9e8765SNick Fitzgerald let _ = config.gc_enabled; 553aa39239SChris Fallin let _ = config.exceptions_enabled; 56edad0bbcSNick Fitzgerald config.custom_page_sizes_enabled = u.arbitrary()?; 57292f136cSAlex Crichton config.wide_arithmetic_enabled = u.arbitrary()?; 58292f136cSAlex Crichton config.memory64_enabled = u.ratio(1, 20)?; 590a55f804SAlex Crichton // Fuzzing threads is an open question. Even without actual parallel 600a55f804SAlex Crichton // threads `SharedMemory` still poses a problem where it isn't hooked 610a55f804SAlex Crichton // into resource limits the same way `Memory` is. Overall not clear what 620a55f804SAlex Crichton // to do so it's disabled for now. 630a55f804SAlex Crichton config.threads_enabled = false; 6410dbb199SAlex Crichton // Allow multi-memory but make it unlikely 6510dbb199SAlex Crichton if u.ratio(1, 20)? { 665ec92d59SAndrew Brown config.max_memories = config.max_memories.max(2); 6710dbb199SAlex Crichton } else { 6810dbb199SAlex Crichton config.max_memories = 1; 6910dbb199SAlex Crichton } 70292f136cSAlex Crichton // ... NB: if you add something above this line please be sure to update 71292f136cSAlex Crichton // `docs/stability-wasm-proposals.md` 725ec92d59SAndrew Brown 7351b6a043SRainy Sinclair // We get better differential execution when we disallow traps, so we'll 7451b6a043SRainy Sinclair // do that most of the time. 7551b6a043SRainy Sinclair config.disallow_traps = u.ratio(9, 10)?; 7651b6a043SRainy Sinclair 77f406347aSAlex Crichton Ok(ModuleConfig { 783ba13d1bSJoel Dice component_model_async: false, 79c7e092d8SAndrew Brown component_model_async_builtins: false, 80c7e092d8SAndrew Brown component_model_async_stackful: false, 81e06fbf70SSy Brand component_model_threading: false, 82c857f687SAlex Crichton component_model_error_context: false, 838801023bSNick Fitzgerald component_model_gc: false, 84*1b59b579SYordis Prieto component_model_map: false, 85caf0f752SChristof Petig component_model_fixed_length_lists: false, 86366f320dSAlex Crichton legacy_exceptions: false, 870a55f804SAlex Crichton shared_memory: false, 88f406347aSAlex Crichton function_references_enabled: config.gc_enabled, 89f406347aSAlex Crichton config, 90f406347aSAlex Crichton }) 915ec92d59SAndrew Brown } 925ec92d59SAndrew Brown } 935ec92d59SAndrew Brown 945ec92d59SAndrew Brown impl ModuleConfig { 955ec92d59SAndrew Brown /// Uses this configuration and the supplied source of data to generate a 965ec92d59SAndrew Brown /// Wasm module. 975ec92d59SAndrew Brown /// 985ec92d59SAndrew Brown /// If a `default_fuel` is provided, the resulting module will be configured 995ec92d59SAndrew Brown /// to ensure termination; as doing so will add an additional global to the 1005ec92d59SAndrew Brown /// module, the pooling allocator, if configured, must also have its globals 1015ec92d59SAndrew Brown /// limit updated. generate( &self, input: &mut Unstructured<'_>, default_fuel: Option<u32>, ) -> arbitrary::Result<wasm_smith::Module>1025ec92d59SAndrew Brown pub fn generate( 1035ec92d59SAndrew Brown &self, 1045ec92d59SAndrew Brown input: &mut Unstructured<'_>, 1055ec92d59SAndrew Brown default_fuel: Option<u32>, 1065ec92d59SAndrew Brown ) -> arbitrary::Result<wasm_smith::Module> { 107e6759845SAlex Crichton crate::init_fuzzing(); 108e6759845SAlex Crichton 109e6759845SAlex Crichton // If requested, save `*.{dna,json}` files for recreating this module 110e6759845SAlex Crichton // in wasm-tools alone. 111e6759845SAlex Crichton let input_before = if log::log_enabled!(log::Level::Debug) { 112e6759845SAlex Crichton let len = input.len(); 113e6759845SAlex Crichton Some(input.peek_bytes(len).unwrap().to_vec()) 114e6759845SAlex Crichton } else { 115e6759845SAlex Crichton None 116e6759845SAlex Crichton }; 117e6759845SAlex Crichton 1185ec92d59SAndrew Brown let mut module = wasm_smith::Module::new(self.config.clone(), input)?; 1195ec92d59SAndrew Brown 120e6759845SAlex Crichton if let Some(before) = input_before { 121e6759845SAlex Crichton static GEN_CNT: AtomicUsize = AtomicUsize::new(0); 122e6759845SAlex Crichton let used = before.len() - input.len(); 123e6759845SAlex Crichton let i = GEN_CNT.fetch_add(1, Relaxed); 124e6759845SAlex Crichton let dna = format!("testcase{i}.dna"); 125e6759845SAlex Crichton let config = format!("testcase{i}.json"); 126e6759845SAlex Crichton log::debug!("writing `{dna}` and `{config}`"); 127e6759845SAlex Crichton std::fs::write(&dna, &before[..used]).unwrap(); 128e6759845SAlex Crichton std::fs::write(&config, serde_json::to_string_pretty(&self.config).unwrap()).unwrap(); 129e6759845SAlex Crichton } 130e6759845SAlex Crichton 1315ec92d59SAndrew Brown if let Some(default_fuel) = default_fuel { 13204c03b31SAlex Crichton module.ensure_termination(default_fuel).unwrap(); 1335ec92d59SAndrew Brown } 1345ec92d59SAndrew Brown 1355ec92d59SAndrew Brown Ok(module) 1365ec92d59SAndrew Brown } 1375ec92d59SAndrew Brown } 138