Coverage Report

Created: 2026-08-28 08:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wasm-smith/src/lib.rs
Line
Count
Source
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 = "1024"]
59
60
#[cfg(feature = "component-model")]
61
mod component;
62
mod config;
63
mod core;
64
65
pub use crate::core::{InstructionKind, InstructionKinds, Module};
66
use arbitrary::{Result, Unstructured};
67
#[cfg(feature = "component-model")]
68
pub use component::Component;
69
pub use config::{Config, MemoryOffsetChoices};
70
use std::{collections::HashSet, fmt::Write, str};
71
72
#[doc(hidden)]
73
pub use config::InternalOptionalConfig;
74
75
/// Do something an arbitrary number of times.
76
///
77
/// The callback can return `false` to exit the loop early.
78
381k
pub(crate) fn arbitrary_loop<'a>(
79
381k
    u: &mut Unstructured<'a>,
80
381k
    min: usize,
81
381k
    max: usize,
82
381k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
381k
) -> Result<()> {
84
381k
    assert!(max >= min);
85
381k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
381k
    for _ in 0..(max - min) {
91
5.50M
        let keep_going = u.arbitrary().unwrap_or(false);
92
5.50M
        if !keep_going {
93
232k
            break;
94
5.27M
        }
95
96
5.27M
        if !f(u)? {
97
1.14k
            break;
98
5.27M
        }
99
    }
100
101
381k
    Ok(())
102
381k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#5}::{closure#2}>
Line
Count
Source
78
5.21k
pub(crate) fn arbitrary_loop<'a>(
79
5.21k
    u: &mut Unstructured<'a>,
80
5.21k
    min: usize,
81
5.21k
    max: usize,
82
5.21k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
5.21k
) -> Result<()> {
84
5.21k
    assert!(max >= min);
85
5.21k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
5.21k
    for _ in 0..(max - min) {
91
287k
        let keep_going = u.arbitrary().unwrap_or(false);
92
287k
        if !keep_going {
93
2.47k
            break;
94
284k
        }
95
96
284k
        if !f(u)? {
97
0
            break;
98
284k
        }
99
    }
100
101
5.21k
    Ok(())
102
5.21k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#5}::{closure#3}>
Line
Count
Source
78
18.3k
pub(crate) fn arbitrary_loop<'a>(
79
18.3k
    u: &mut Unstructured<'a>,
80
18.3k
    min: usize,
81
18.3k
    max: usize,
82
18.3k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
18.3k
) -> Result<()> {
84
18.3k
    assert!(max >= min);
85
18.3k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
18.3k
    for _ in 0..(max - min) {
91
665k
        let keep_going = u.arbitrary().unwrap_or(false);
92
665k
        if !keep_going {
93
7.55k
            break;
94
658k
        }
95
96
658k
        if !f(u)? {
97
0
            break;
98
658k
        }
99
    }
100
101
18.3k
    Ok(())
102
18.3k
}
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_instance_type::{closure#0}::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_component_type::{closure#0}::{closure#0}>
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_data::{closure#4}>
Line
Count
Source
78
3.80k
pub(crate) fn arbitrary_loop<'a>(
79
3.80k
    u: &mut Unstructured<'a>,
80
3.80k
    min: usize,
81
3.80k
    max: usize,
82
3.80k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
3.80k
) -> Result<()> {
84
3.80k
    assert!(max >= min);
85
3.80k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
3.80k
    for _ in 0..(max - min) {
91
25.4k
        let keep_going = u.arbitrary().unwrap_or(false);
92
25.4k
        if !keep_going {
93
3.63k
            break;
94
21.8k
        }
95
96
21.8k
        if !f(u)? {
97
0
            break;
98
21.8k
        }
99
    }
100
101
3.80k
    Ok(())
102
3.80k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_tags::{closure#0}>
Line
Count
Source
78
1.00k
pub(crate) fn arbitrary_loop<'a>(
79
1.00k
    u: &mut Unstructured<'a>,
80
1.00k
    min: usize,
81
1.00k
    max: usize,
82
1.00k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
1.00k
) -> Result<()> {
84
1.00k
    assert!(max >= min);
85
1.00k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
1.00k
    for _ in 0..(max - min) {
91
26.6k
        let keep_going = u.arbitrary().unwrap_or(false);
92
26.6k
        if !keep_going {
93
817
            break;
94
25.8k
        }
95
96
25.8k
        if !f(u)? {
97
76
            break;
98
25.7k
        }
99
    }
100
101
1.00k
    Ok(())
102
1.00k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#5}>
Line
Count
Source
78
3.50k
pub(crate) fn arbitrary_loop<'a>(
79
3.50k
    u: &mut Unstructured<'a>,
80
3.50k
    min: usize,
81
3.50k
    max: usize,
82
3.50k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
3.50k
) -> Result<()> {
84
3.50k
    assert!(max >= min);
85
3.50k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
3.50k
    for _ in 0..(max - min) {
91
32.5k
        let keep_going = u.arbitrary().unwrap_or(false);
92
32.5k
        if !keep_going {
93
3.20k
            break;
94
29.3k
        }
95
96
29.3k
        if !f(u)? {
97
0
            break;
98
29.3k
        }
99
    }
100
101
3.50k
    Ok(())
102
3.50k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_funcs::{closure#1}>
Line
Count
Source
78
3.28k
pub(crate) fn arbitrary_loop<'a>(
79
3.28k
    u: &mut Unstructured<'a>,
80
3.28k
    min: usize,
81
3.28k
    max: usize,
82
3.28k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
3.28k
) -> Result<()> {
84
3.28k
    assert!(max >= min);
85
3.28k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
3.28k
    for _ in 0..(max - min) {
91
144k
        let keep_going = u.arbitrary().unwrap_or(false);
92
144k
        if !keep_going {
93
3.02k
            break;
94
141k
        }
95
96
141k
        if !f(u)? {
97
116
            break;
98
141k
        }
99
    }
100
101
3.28k
    Ok(())
102
3.28k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_locals::{closure#0}>
Line
Count
Source
78
141k
pub(crate) fn arbitrary_loop<'a>(
79
141k
    u: &mut Unstructured<'a>,
80
141k
    min: usize,
81
141k
    max: usize,
82
141k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
141k
) -> Result<()> {
84
141k
    assert!(max >= min);
85
141k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
141k
    for _ in 0..(max - min) {
91
2.18M
        let keep_going = u.arbitrary().unwrap_or(false);
92
2.18M
        if !keep_going {
93
123k
            break;
94
2.06M
        }
95
96
2.06M
        if !f(u)? {
97
0
            break;
98
2.06M
        }
99
    }
100
101
141k
    Ok(())
102
141k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_tables::{closure#0}>
Line
Count
Source
78
4.34k
pub(crate) fn arbitrary_loop<'a>(
79
4.34k
    u: &mut Unstructured<'a>,
80
4.34k
    min: usize,
81
4.34k
    max: usize,
82
4.34k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
4.34k
) -> Result<()> {
84
4.34k
    assert!(max >= min);
85
4.34k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
4.34k
    for _ in 0..(max - min) {
91
18.7k
        let keep_going = u.arbitrary().unwrap_or(false);
92
18.7k
        if !keep_going {
93
2.53k
            break;
94
16.2k
        }
95
96
16.2k
        if !f(u)? {
97
522
            break;
98
15.7k
        }
99
    }
100
101
4.34k
    Ok(())
102
4.34k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_exports::{closure#4}>
Line
Count
Source
78
4.34k
pub(crate) fn arbitrary_loop<'a>(
79
4.34k
    u: &mut Unstructured<'a>,
80
4.34k
    min: usize,
81
4.34k
    max: usize,
82
4.34k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
4.34k
) -> Result<()> {
84
4.34k
    assert!(max >= min);
85
4.34k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
4.34k
    for _ in 0..(max - min) {
91
51.2k
        let keep_going = u.arbitrary().unwrap_or(false);
92
51.2k
        if !keep_going {
93
3.90k
            break;
94
47.3k
        }
95
96
47.3k
        if !f(u)? {
97
194
            break;
98
47.1k
        }
99
    }
100
101
4.34k
    Ok(())
102
4.34k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_globals::{closure#0}>
Line
Count
Source
78
4.34k
pub(crate) fn arbitrary_loop<'a>(
79
4.34k
    u: &mut Unstructured<'a>,
80
4.34k
    min: usize,
81
4.34k
    max: usize,
82
4.34k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
4.34k
) -> Result<()> {
84
4.34k
    assert!(max >= min);
85
4.34k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
4.34k
    for _ in 0..(max - min) {
91
86.2k
        let keep_going = u.arbitrary().unwrap_or(false);
92
86.2k
        if !keep_going {
93
4.08k
            break;
94
82.1k
        }
95
96
82.1k
        if !f(u)? {
97
41
            break;
98
82.1k
        }
99
    }
100
101
4.34k
    Ok(())
102
4.34k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_memories::{closure#0}>
Line
Count
Source
78
4.34k
pub(crate) fn arbitrary_loop<'a>(
79
4.34k
    u: &mut Unstructured<'a>,
80
4.34k
    min: usize,
81
4.34k
    max: usize,
82
4.34k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
4.34k
) -> Result<()> {
84
4.34k
    assert!(max >= min);
85
4.34k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
4.34k
    for _ in 0..(max - min) {
91
26.2k
        let keep_going = u.arbitrary().unwrap_or(false);
92
26.2k
        if !keep_going {
93
3.58k
            break;
94
22.6k
        }
95
96
22.6k
        if !f(u)? {
97
199
            break;
98
22.4k
        }
99
    }
100
101
4.34k
    Ok(())
102
4.34k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_func_type::{closure#0}>
Line
Count
Source
78
93.6k
pub(crate) fn arbitrary_loop<'a>(
79
93.6k
    u: &mut Unstructured<'a>,
80
93.6k
    min: usize,
81
93.6k
    max: usize,
82
93.6k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
93.6k
) -> Result<()> {
84
93.6k
    assert!(max >= min);
85
93.6k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
93.6k
    for _ in 0..(max - min) {
91
1.25M
        let keep_going = u.arbitrary().unwrap_or(false);
92
1.25M
        if !keep_going {
93
41.7k
            break;
94
1.21M
        }
95
96
1.21M
        if !f(u)? {
97
0
            break;
98
1.21M
        }
99
    }
100
101
93.6k
    Ok(())
102
93.6k
}
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_func_type::{closure#1}>
Line
Count
Source
78
93.6k
pub(crate) fn arbitrary_loop<'a>(
79
93.6k
    u: &mut Unstructured<'a>,
80
93.6k
    min: usize,
81
93.6k
    max: usize,
82
93.6k
    mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
83
93.6k
) -> Result<()> {
84
93.6k
    assert!(max >= min);
85
93.6k
    for _ in 0..min {
86
0
        if !f(u)? {
87
0
            return Err(arbitrary::Error::IncorrectFormat);
88
0
        }
89
    }
90
93.6k
    for _ in 0..(max - min) {
91
704k
        let keep_going = u.arbitrary().unwrap_or(false);
92
704k
        if !keep_going {
93
32.5k
            break;
94
671k
        }
95
96
671k
        if !f(u)? {
97
0
            break;
98
671k
        }
99
    }
100
101
93.6k
    Ok(())
102
93.6k
}
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_enum_type::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_func_type::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_func_type::{closure#1}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_flags_type::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_tuple_type::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_module_type::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_record_type::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_type_section::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_variant_type::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_import_section::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_canonical_section::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_core_type_section::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<wasm_smith::component::arbitrary_func_type::{closure#0}>
Unexecuted instantiation: wasm_smith::arbitrary_loop::<wasm_smith::component::arbitrary_func_type::{closure#1}>
103
104
// Mirror what happens in `Arbitrary for String`, but do so with a clamped size.
105
171k
pub(crate) fn limited_str<'a>(max_size: usize, u: &mut Unstructured<'a>) -> Result<&'a str> {
106
171k
    let size = u.arbitrary_len::<u8>()?;
107
171k
    let size = std::cmp::min(size, max_size);
108
171k
    match str::from_utf8(u.peek_bytes(size).unwrap()) {
109
8.51k
        Ok(s) => {
110
8.51k
            u.bytes(size).unwrap();
111
8.51k
            Ok(s)
112
        }
113
163k
        Err(e) => {
114
163k
            let i = e.valid_up_to();
115
163k
            let valid = u.bytes(i).unwrap();
116
163k
            let s = str::from_utf8(valid).unwrap();
117
163k
            Ok(s)
118
        }
119
    }
120
171k
}
121
122
171k
pub(crate) fn limited_string(max_size: usize, u: &mut Unstructured) -> Result<String> {
123
171k
    Ok(limited_str(max_size, u)?.into())
124
171k
}
125
126
50.1k
pub(crate) fn unique_string(
127
50.1k
    max_size: usize,
128
50.1k
    names: &mut HashSet<String>,
129
50.1k
    u: &mut Unstructured,
130
50.1k
) -> Result<String> {
131
50.1k
    let mut name = limited_string(max_size, u)?;
132
93.6k
    while names.contains(&name) {
133
43.4k
        write!(&mut name, "{}", names.len()).unwrap();
134
43.4k
    }
135
50.1k
    names.insert(name.clone());
136
50.1k
    Ok(name)
137
50.1k
}
138
139
#[cfg(feature = "component-model")]
140
0
pub(crate) fn unique_kebab_string(
141
0
    max_size: usize,
142
0
    names: &mut HashSet<String>,
143
0
    u: &mut Unstructured,
144
0
) -> Result<String> {
145
0
    let size = std::cmp::min(u.arbitrary_len::<u8>()?, max_size);
146
0
    let mut name = String::with_capacity(size);
147
0
    let mut empty_segment = true;
148
0
    for i in 0..size {
149
0
        name.push(match u.int_in_range::<u8>(0..=36)? {
150
0
            x if (0..26).contains(&x) => {
151
0
                empty_segment = false;
152
0
                (b'a' + x) as char
153
            }
154
0
            x if (26..36).contains(&x) => {
155
0
                empty_segment = false;
156
0
                if i == 0 {
157
0
                    (b'a' + (x - 26)) as char
158
                } else {
159
0
                    (b'0' + (x - 26)) as char
160
                }
161
            }
162
0
            x if x == 36 => {
163
0
                if empty_segment {
164
0
                    empty_segment = false;
165
0
                    'a'
166
                } else {
167
0
                    empty_segment = true;
168
0
                    '-'
169
                }
170
            }
171
0
            _ => unreachable!(),
172
        });
173
    }
174
175
0
    if name.is_empty() || name.ends_with('-') {
176
0
        name.push('a');
177
0
    }
178
179
0
    while names.contains(&name) {
180
0
        write!(&mut name, "{}", names.len()).unwrap();
181
0
    }
182
183
0
    names.insert(name.clone());
184
185
0
    Ok(name)
186
0
}
187
188
#[cfg(feature = "component-model")]
189
0
pub(crate) fn unique_url(
190
0
    max_size: usize,
191
0
    names: &mut HashSet<String>,
192
0
    u: &mut Unstructured,
193
0
) -> Result<String> {
194
0
    let path = unique_kebab_string(max_size, names, u)?;
195
0
    Ok(format!("https://example.com/{path}"))
196
0
}