//! A WebAssembly test case generator. //! //! ## Usage //! //! First, use [`cargo fuzz`](https://github.com/rust-fuzz/cargo-fuzz) to define //! a new fuzz target: //! //! ```shell //! $ cargo fuzz add my_wasm_smith_fuzz_target //! ``` //! //! Next, add `wasm-smith` to your dependencies: //! //! ```shell //! $ cargo add wasm-smith //! ``` //! //! Then, define your fuzz target so that it takes arbitrary //! `wasm_smith::Module`s as an argument, convert the module into serialized //! Wasm bytes via the `to_bytes` method, and then feed it into your system: //! //! ```no_run //! // fuzz/fuzz_targets/my_wasm_smith_fuzz_target.rs //! //! #![no_main] //! //! use libfuzzer_sys::fuzz_target; //! use wasm_smith::Module; //! //! fuzz_target!(|module: Module| { //! let wasm_bytes = module.to_bytes(); //! //! // Your code here... //! }); //! ``` //! //! Finally, start fuzzing: //! //! ```shell //! $ cargo fuzz run my_wasm_smith_fuzz_target //! ``` //! //! > **Note:** For a real world example, also check out [the `validate` fuzz //! > target](https://github.com/fitzgen/wasm-smith/blob/main/fuzz/fuzz_targets/validate.rs) //! > defined in this repository. Using the `wasmparser` crate, it checks that //! > every module generated by `wasm-smith` validates successfully. //! //! ## Design //! //! The design and implementation strategy of wasm-smith is outlined in //! [this article](https://fitzgeraldnick.com/2020/08/24/writing-a-test-case-generator.html). #![deny(missing_docs, missing_debug_implementations)] // Needed for the `instructions!` macro in `src/code_builder.rs`. #![recursion_limit = "512"] mod component; mod config; mod core; pub use crate::core::{ ConfiguredModule, InstructionKind, InstructionKinds, MaybeInvalidModule, Module, }; use arbitrary::{Result, Unstructured}; pub use component::{Component, ConfiguredComponent}; pub use config::{Config, DefaultConfig, SwarmConfig}; use std::{collections::HashSet, fmt::Write, str}; use wasmparser::types::{KebabStr, KebabString}; /// Do something an arbitrary number of times. /// /// The callback can return `false` to exit the loop early. pub(crate) fn arbitrary_loop<'a>( u: &mut Unstructured<'a>, min: usize, max: usize, mut f: impl FnMut(&mut Unstructured<'a>) -> Result, ) -> Result<()> { assert!(max >= min); for _ in 0..min { if !f(u)? { return Err(arbitrary::Error::IncorrectFormat); } } for _ in 0..(max - min) { let keep_going = u.arbitrary().unwrap_or(false); if !keep_going { break; } if !f(u)? { break; } } Ok(()) } // Mirror what happens in `Arbitrary for String`, but do so with a clamped size. pub(crate) fn limited_str<'a>(max_size: usize, u: &mut Unstructured<'a>) -> Result<&'a str> { let size = u.arbitrary_len::()?; let size = std::cmp::min(size, max_size); match str::from_utf8(u.peek_bytes(size).unwrap()) { Ok(s) => { u.bytes(size).unwrap(); Ok(s) } Err(e) => { let i = e.valid_up_to(); let valid = u.bytes(i).unwrap(); let s = unsafe { debug_assert!(str::from_utf8(valid).is_ok()); str::from_utf8_unchecked(valid) }; Ok(s) } } } pub(crate) fn limited_string(max_size: usize, u: &mut Unstructured) -> Result { Ok(limited_str(max_size, u)?.into()) } pub(crate) fn unique_string( max_size: usize, names: &mut HashSet, u: &mut Unstructured, ) -> Result { let mut name = limited_string(max_size, u)?; while names.contains(&name) { write!(&mut name, "{}", names.len()).unwrap(); } names.insert(name.clone()); Ok(name) } pub(crate) fn unique_kebab_string( max_size: usize, names: &mut HashSet, u: &mut Unstructured, ) -> Result { let size = std::cmp::min(u.arbitrary_len::()?, max_size); let mut name = String::with_capacity(size); let mut require_alpha = true; for _ in 0..size { name.push(match u.int_in_range::(0..=36)? { x if (0..26).contains(&x) => { require_alpha = false; (b'a' + x) as char } x if (26..36).contains(&x) => { if require_alpha { require_alpha = false; (b'a' + (x - 26)) as char } else { (b'0' + (x - 26)) as char } } x if x == 36 => { if require_alpha { require_alpha = false; 'a' } else { require_alpha = true; '-' } } _ => unreachable!(), }); } if name.is_empty() || name.ends_with('-') { name.push('a'); } while names.contains(KebabStr::new(&name).unwrap()) { write!(&mut name, "{}", names.len()).unwrap(); } let name = KebabString::new(name).unwrap(); names.insert(name.clone()); Ok(name) } pub(crate) fn unique_url( max_size: usize, names: &mut HashSet, u: &mut Unstructured, ) -> Result { let path = unique_kebab_string(max_size, names, u)?; Ok(format!("https://example.com/{path}")) }