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