Path: blob/main/crates/fuzzing/src/generators/module.rs
3063 views
//! Generate a Wasm module and the configuration for generating it.12use arbitrary::{Arbitrary, Unstructured};3use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};45/// Default module-level configuration for fuzzing Wasmtime.6///7/// Internally this uses `wasm-smith`'s own `Config` but we further refine8/// the defaults here as well.9#[derive(Debug, Clone)]10#[expect(missing_docs, reason = "self-describing fields")]11pub struct ModuleConfig {12pub config: wasm_smith::Config,1314// These knobs aren't exposed in `wasm-smith` at this time but are exposed15// in our `*.wast` testing so keep knobs here so they can be read during16// config-to-`wasmtime::Config` translation.17pub function_references_enabled: bool,18pub component_model_async: bool,19pub component_model_async_builtins: bool,20pub component_model_async_stackful: bool,21pub component_model_threading: bool,22pub component_model_error_context: bool,23pub component_model_gc: bool,24pub component_model_fixed_length_lists: bool,25pub legacy_exceptions: bool,26pub shared_memory: bool,27}2829impl<'a> Arbitrary<'a> for ModuleConfig {30fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<ModuleConfig> {31let mut config = wasm_smith::Config::arbitrary(u)?;3233// This list is intended to be the definitive source of truth for34// what's at least possible to fuzz within Wasmtime. This is a35// combination of features in `wasm-smith` where some proposals are36// on-by-default (as determined by fuzz input) and others are37// off-by-default (as they aren't stage4+). Wasmtime will default-fuzz38// proposals that a pre-stage-4 to test our own implementation. Wasmtime39// might also unconditionally disable proposals that it doesn't40// implement yet which are stage4+. This is intended to be an exhaustive41// list of all the wasm proposals that `wasm-smith` supports and the42// fuzzing status within Wasmtime too.43let _ = config.multi_value_enabled;44let _ = config.saturating_float_to_int_enabled;45let _ = config.sign_extension_ops_enabled;46let _ = config.bulk_memory_enabled;47let _ = config.reference_types_enabled;48let _ = config.simd_enabled;49let _ = config.relaxed_simd_enabled;50let _ = config.tail_call_enabled;51let _ = config.extended_const_enabled;52let _ = config.gc_enabled;53let _ = config.exceptions_enabled;54config.custom_page_sizes_enabled = u.arbitrary()?;55config.wide_arithmetic_enabled = u.arbitrary()?;56config.memory64_enabled = u.ratio(1, 20)?;57// Fuzzing threads is an open question. Even without actual parallel58// threads `SharedMemory` still poses a problem where it isn't hooked59// into resource limits the same way `Memory` is. Overall not clear what60// to do so it's disabled for now.61config.threads_enabled = false;62// Allow multi-memory but make it unlikely63if u.ratio(1, 20)? {64config.max_memories = config.max_memories.max(2);65} else {66config.max_memories = 1;67}68// ... NB: if you add something above this line please be sure to update69// `docs/stability-wasm-proposals.md`7071// We get better differential execution when we disallow traps, so we'll72// do that most of the time.73config.disallow_traps = u.ratio(9, 10)?;7475Ok(ModuleConfig {76component_model_async: false,77component_model_async_builtins: false,78component_model_async_stackful: false,79component_model_threading: false,80component_model_error_context: false,81component_model_gc: false,82component_model_fixed_length_lists: false,83legacy_exceptions: false,84shared_memory: false,85function_references_enabled: config.gc_enabled,86config,87})88}89}9091impl ModuleConfig {92/// Uses this configuration and the supplied source of data to generate a93/// Wasm module.94///95/// If a `default_fuel` is provided, the resulting module will be configured96/// to ensure termination; as doing so will add an additional global to the97/// module, the pooling allocator, if configured, must also have its globals98/// limit updated.99pub fn generate(100&self,101input: &mut Unstructured<'_>,102default_fuel: Option<u32>,103) -> arbitrary::Result<wasm_smith::Module> {104crate::init_fuzzing();105106// If requested, save `*.{dna,json}` files for recreating this module107// in wasm-tools alone.108let input_before = if log::log_enabled!(log::Level::Debug) {109let len = input.len();110Some(input.peek_bytes(len).unwrap().to_vec())111} else {112None113};114115let mut module = wasm_smith::Module::new(self.config.clone(), input)?;116117if let Some(before) = input_before {118static GEN_CNT: AtomicUsize = AtomicUsize::new(0);119let used = before.len() - input.len();120let i = GEN_CNT.fetch_add(1, Relaxed);121let dna = format!("testcase{i}.dna");122let config = format!("testcase{i}.json");123log::debug!("writing `{dna}` and `{config}`");124std::fs::write(&dna, &before[..used]).unwrap();125std::fs::write(&config, serde_json::to_string_pretty(&self.config).unwrap()).unwrap();126}127128if let Some(default_fuel) = default_fuel {129module.ensure_termination(default_fuel).unwrap();130}131132Ok(module)133}134}135136137