wasm_smith/
lib.rs

1//! A WebAssembly test case generator.
2//!
3//! ## Usage
4//!
5//! First, use [`cargo fuzz`](https://github.com/rust-fuzz/cargo-fuzz) to define
6//! a new fuzz target:
7//!
8//! ```shell
9//! $ cargo fuzz add my_wasm_smith_fuzz_target
10//! ```
11//!
12//! Next, add `wasm-smith` to your dependencies:
13//!
14//! ```shell
15//! $ cargo add wasm-smith
16//! ```
17//!
18//! Then, define your fuzz target so that it takes arbitrary
19//! `wasm_smith::Module`s as an argument, convert the module into serialized
20//! Wasm bytes via the `to_bytes` method, and then feed it into your system:
21//!
22//! ```no_run
23//! // fuzz/fuzz_targets/my_wasm_smith_fuzz_target.rs
24//!
25//! #![no_main]
26//!
27//! # #[cfg(not(target_family = "wasm"))] mod x {
28//! use libfuzzer_sys::fuzz_target;
29//! use wasm_smith::Module;
30//!
31//! fuzz_target!(|module: Module| {
32//!     let wasm_bytes = module.to_bytes();
33//!
34//!     // Your code here...
35//! });
36//! # }
37//! ```
38//!
39//! Finally, start fuzzing:
40//!
41//! ```shell
42//! $ cargo fuzz run my_wasm_smith_fuzz_target
43//! ```
44//!
45//! > **Note:** For a real world example, also check out [the `validate` fuzz
46//! > target](https://github.com/bytecodealliance/wasm-tools/blob/main/fuzz/src/validate.rs)
47//! > defined in this repository. Using the `wasmparser` crate, it checks that
48//! > every module generated by `wasm-smith` validates successfully.
49//!
50//! ## Design
51//!
52//! The design and implementation strategy of wasm-smith is outlined in
53//! [this article](https://fitzgeraldnick.com/2020/08/24/writing-a-test-case-generator.html).
54
55#![cfg_attr(docsrs, feature(doc_cfg))]
56#![deny(missing_docs, missing_debug_implementations)]
57// Needed for the `instructions!` macro in `src/code_builder.rs`.
58#![recursion_limit = "512"]
59
60#[cfg(feature = "component-model")]
61mod component;
62mod config;
63mod core;
64
65pub use crate::core::{InstructionKind, InstructionKinds, Module};
66use arbitrary::{Result, Unstructured};
67#[cfg(feature = "component-model")]
68pub use component::Component;
69pub use config::{Config, MemoryOffsetChoices};
70use std::{collections::HashSet, fmt::Write, str};
71use wasm_encoder::MemoryType;
72
73#[doc(hidden)]
74pub use config::InternalOptionalConfig;
75
76pub(crate) fn page_size(mem: &MemoryType) -> u32 {
77    const DEFAULT_WASM_PAGE_SIZE_LOG2: u32 = 16;
78    1 << mem.page_size_log2.unwrap_or(DEFAULT_WASM_PAGE_SIZE_LOG2)
79}
80
81/// Do something an arbitrary number of times.
82///
83/// The callback can return `false` to exit the loop early.
84pub(crate) fn arbitrary_loop<'a>(
85    u: &mut Unstructured<'a>,
86    min: usize,
87    max: usize,
88    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
89) -> Result<()> {
90    assert!(max >= min);
91    for _ in 0..min {
92        if !f(u)? {
93            return Err(arbitrary::Error::IncorrectFormat);
94        }
95    }
96    for _ in 0..(max - min) {
97        let keep_going = u.arbitrary().unwrap_or(false);
98        if !keep_going {
99            break;
100        }
101
102        if !f(u)? {
103            break;
104        }
105    }
106
107    Ok(())
108}
109
110// Mirror what happens in `Arbitrary for String`, but do so with a clamped size.
111pub(crate) fn limited_str<'a>(max_size: usize, u: &mut Unstructured<'a>) -> Result<&'a str> {
112    let size = u.arbitrary_len::<u8>()?;
113    let size = std::cmp::min(size, max_size);
114    match str::from_utf8(u.peek_bytes(size).unwrap()) {
115        Ok(s) => {
116            u.bytes(size).unwrap();
117            Ok(s)
118        }
119        Err(e) => {
120            let i = e.valid_up_to();
121            let valid = u.bytes(i).unwrap();
122            let s = str::from_utf8(valid).unwrap();
123            Ok(s)
124        }
125    }
126}
127
128pub(crate) fn limited_string(max_size: usize, u: &mut Unstructured) -> Result<String> {
129    Ok(limited_str(max_size, u)?.into())
130}
131
132pub(crate) fn unique_string(
133    max_size: usize,
134    names: &mut HashSet<String>,
135    u: &mut Unstructured,
136) -> Result<String> {
137    let mut name = limited_string(max_size, u)?;
138    while names.contains(&name) {
139        write!(&mut name, "{}", names.len()).unwrap();
140    }
141    names.insert(name.clone());
142    Ok(name)
143}
144
145#[cfg(feature = "component-model")]
146pub(crate) fn unique_kebab_string(
147    max_size: usize,
148    names: &mut HashSet<String>,
149    u: &mut Unstructured,
150) -> Result<String> {
151    let size = std::cmp::min(u.arbitrary_len::<u8>()?, max_size);
152    let mut name = String::with_capacity(size);
153    let mut require_alpha = true;
154    for _ in 0..size {
155        name.push(match u.int_in_range::<u8>(0..=36)? {
156            x if (0..26).contains(&x) => {
157                require_alpha = false;
158                (b'a' + x) as char
159            }
160            x if (26..36).contains(&x) => {
161                if require_alpha {
162                    require_alpha = false;
163                    (b'a' + (x - 26)) as char
164                } else {
165                    (b'0' + (x - 26)) as char
166                }
167            }
168            x if x == 36 => {
169                if require_alpha {
170                    require_alpha = false;
171                    'a'
172                } else {
173                    require_alpha = true;
174                    '-'
175                }
176            }
177            _ => unreachable!(),
178        });
179    }
180
181    if name.is_empty() || name.ends_with('-') {
182        name.push('a');
183    }
184
185    while names.contains(&name) {
186        write!(&mut name, "{}", names.len()).unwrap();
187    }
188
189    names.insert(name.clone());
190
191    Ok(name)
192}
193
194#[cfg(feature = "component-model")]
195pub(crate) fn unique_url(
196    max_size: usize,
197    names: &mut HashSet<String>,
198    u: &mut Unstructured,
199) -> Result<String> {
200    let path = unique_kebab_string(max_size, names, u)?;
201    Ok(format!("https://example.com/{path}"))
202}