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