Coverage Report

Created: 2026-09-28 08:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/fuzz/src/lib.rs
Line
Count
Source
1
use arbitrary::{Result, Unstructured};
2
use std::fmt::Debug;
3
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
4
use wasm_smith::{Component, Config, Module};
5
6
pub mod incremental_parse;
7
pub mod mutate;
8
pub mod no_traps;
9
pub mod print;
10
pub mod reencode;
11
pub mod roundtrip;
12
pub mod roundtrip_wit;
13
pub mod text_parser;
14
pub mod validate;
15
pub mod validate_valid_module;
16
pub mod wit64;
17
18
10.7k
pub fn generate_valid_module(
19
10.7k
    u: &mut Unstructured,
20
10.7k
    configure: impl FnOnce(&mut Config, &mut Unstructured<'_>) -> Result<()>,
21
10.7k
) -> Result<(Vec<u8>, Config)> {
22
10.7k
    let mut config: Config = u.arbitrary()?;
23
24
    // These are disabled in the swarm config by default, but we want to test
25
    // them. Use the input data to determine whether these features are enabled.
26
10.7k
    config.canonicalize_nans = u.arbitrary()?;
27
10.7k
    config.custom_page_sizes_enabled = u.arbitrary()?;
28
10.7k
    config.wide_arithmetic_enabled = u.arbitrary()?;
29
10.7k
    config.shared_everything_threads_enabled = u.arbitrary()?;
30
31
10.7k
    configure(&mut config, u)?;
32
33
    // Use wasm-smith to generate an arbitrary module and convert it to wasm
34
    // bytes.
35
10.7k
    let mut module = Module::new(config.clone(), u)?;
36
10.7k
    let bytes = module.to_bytes();
37
38
    // 10% of the time, ish, test that the `ensure_termination` method will
39
    // still produce a valid module.
40
10.7k
    if u.ratio(1, 10)? {
41
8.57k
        log::debug!("ensuring termination with 100 fuel");
42
8.57k
        let _ = module.ensure_termination(100);
43
2.13k
    }
44
45
10.7k
    log_wasm(&bytes, &config);
46
47
10.7k
    Ok((bytes, config))
48
10.7k
}
wasm_tools_fuzz::generate_valid_module::<wasm_tools_fuzz::validate_valid_module::run::{closure#1}>
Line
Count
Source
18
3.50k
pub fn generate_valid_module(
19
3.50k
    u: &mut Unstructured,
20
3.50k
    configure: impl FnOnce(&mut Config, &mut Unstructured<'_>) -> Result<()>,
21
3.50k
) -> Result<(Vec<u8>, Config)> {
22
3.50k
    let mut config: Config = u.arbitrary()?;
23
24
    // These are disabled in the swarm config by default, but we want to test
25
    // them. Use the input data to determine whether these features are enabled.
26
3.50k
    config.canonicalize_nans = u.arbitrary()?;
27
3.50k
    config.custom_page_sizes_enabled = u.arbitrary()?;
28
3.50k
    config.wide_arithmetic_enabled = u.arbitrary()?;
29
3.50k
    config.shared_everything_threads_enabled = u.arbitrary()?;
30
31
3.50k
    configure(&mut config, u)?;
32
33
    // Use wasm-smith to generate an arbitrary module and convert it to wasm
34
    // bytes.
35
3.50k
    let mut module = Module::new(config.clone(), u)?;
36
3.47k
    let bytes = module.to_bytes();
37
38
    // 10% of the time, ish, test that the `ensure_termination` method will
39
    // still produce a valid module.
40
3.47k
    if u.ratio(1, 10)? {
41
2.83k
        log::debug!("ensuring termination with 100 fuel");
42
2.83k
        let _ = module.ensure_termination(100);
43
645
    }
44
45
3.47k
    log_wasm(&bytes, &config);
46
47
3.47k
    Ok((bytes, config))
48
3.50k
}
wasm_tools_fuzz::generate_valid_module::<wasm_tools_fuzz::mutate::run::{closure#0}>
Line
Count
Source
18
3.91k
pub fn generate_valid_module(
19
3.91k
    u: &mut Unstructured,
20
3.91k
    configure: impl FnOnce(&mut Config, &mut Unstructured<'_>) -> Result<()>,
21
3.91k
) -> Result<(Vec<u8>, Config)> {
22
3.91k
    let mut config: Config = u.arbitrary()?;
23
24
    // These are disabled in the swarm config by default, but we want to test
25
    // them. Use the input data to determine whether these features are enabled.
26
3.91k
    config.canonicalize_nans = u.arbitrary()?;
27
3.91k
    config.custom_page_sizes_enabled = u.arbitrary()?;
28
3.91k
    config.wide_arithmetic_enabled = u.arbitrary()?;
29
3.91k
    config.shared_everything_threads_enabled = u.arbitrary()?;
30
31
3.91k
    configure(&mut config, u)?;
32
33
    // Use wasm-smith to generate an arbitrary module and convert it to wasm
34
    // bytes.
35
3.91k
    let mut module = Module::new(config.clone(), u)?;
36
3.86k
    let bytes = module.to_bytes();
37
38
    // 10% of the time, ish, test that the `ensure_termination` method will
39
    // still produce a valid module.
40
3.86k
    if u.ratio(1, 10)? {
41
3.03k
        log::debug!("ensuring termination with 100 fuel");
42
3.03k
        let _ = module.ensure_termination(100);
43
834
    }
44
45
3.86k
    log_wasm(&bytes, &config);
46
47
3.86k
    Ok((bytes, config))
48
3.91k
}
wasm_tools_fuzz::generate_valid_module::<wasm_tools_fuzz::no_traps::run::{closure#0}>
Line
Count
Source
18
1.54k
pub fn generate_valid_module(
19
1.54k
    u: &mut Unstructured,
20
1.54k
    configure: impl FnOnce(&mut Config, &mut Unstructured<'_>) -> Result<()>,
21
1.54k
) -> Result<(Vec<u8>, Config)> {
22
1.54k
    let mut config: Config = u.arbitrary()?;
23
24
    // These are disabled in the swarm config by default, but we want to test
25
    // them. Use the input data to determine whether these features are enabled.
26
1.54k
    config.canonicalize_nans = u.arbitrary()?;
27
1.54k
    config.custom_page_sizes_enabled = u.arbitrary()?;
28
1.54k
    config.wide_arithmetic_enabled = u.arbitrary()?;
29
1.54k
    config.shared_everything_threads_enabled = u.arbitrary()?;
30
31
1.54k
    configure(&mut config, u)?;
32
33
    // Use wasm-smith to generate an arbitrary module and convert it to wasm
34
    // bytes.
35
1.54k
    let mut module = Module::new(config.clone(), u)?;
36
1.53k
    let bytes = module.to_bytes();
37
38
    // 10% of the time, ish, test that the `ensure_termination` method will
39
    // still produce a valid module.
40
1.53k
    if u.ratio(1, 10)? {
41
1.25k
        log::debug!("ensuring termination with 100 fuel");
42
1.25k
        let _ = module.ensure_termination(100);
43
278
    }
44
45
1.53k
    log_wasm(&bytes, &config);
46
47
1.53k
    Ok((bytes, config))
48
1.54k
}
wasm_tools_fuzz::generate_valid_module::<wasm_tools_fuzz::reencode::run::{closure#0}>
Line
Count
Source
18
1.32k
pub fn generate_valid_module(
19
1.32k
    u: &mut Unstructured,
20
1.32k
    configure: impl FnOnce(&mut Config, &mut Unstructured<'_>) -> Result<()>,
21
1.32k
) -> Result<(Vec<u8>, Config)> {
22
1.32k
    let mut config: Config = u.arbitrary()?;
23
24
    // These are disabled in the swarm config by default, but we want to test
25
    // them. Use the input data to determine whether these features are enabled.
26
1.32k
    config.canonicalize_nans = u.arbitrary()?;
27
1.32k
    config.custom_page_sizes_enabled = u.arbitrary()?;
28
1.32k
    config.wide_arithmetic_enabled = u.arbitrary()?;
29
1.32k
    config.shared_everything_threads_enabled = u.arbitrary()?;
30
31
1.32k
    configure(&mut config, u)?;
32
33
    // Use wasm-smith to generate an arbitrary module and convert it to wasm
34
    // bytes.
35
1.32k
    let mut module = Module::new(config.clone(), u)?;
36
1.32k
    let bytes = module.to_bytes();
37
38
    // 10% of the time, ish, test that the `ensure_termination` method will
39
    // still produce a valid module.
40
1.32k
    if u.ratio(1, 10)? {
41
1.06k
        log::debug!("ensuring termination with 100 fuel");
42
1.06k
        let _ = module.ensure_termination(100);
43
264
    }
44
45
1.32k
    log_wasm(&bytes, &config);
46
47
1.32k
    Ok((bytes, config))
48
1.32k
}
wasm_tools_fuzz::generate_valid_module::<wasm_tools_fuzz::validate::validate_maybe_invalid_module::{closure#0}>
Line
Count
Source
18
498
pub fn generate_valid_module(
19
498
    u: &mut Unstructured,
20
498
    configure: impl FnOnce(&mut Config, &mut Unstructured<'_>) -> Result<()>,
21
498
) -> Result<(Vec<u8>, Config)> {
22
498
    let mut config: Config = u.arbitrary()?;
23
24
    // These are disabled in the swarm config by default, but we want to test
25
    // them. Use the input data to determine whether these features are enabled.
26
498
    config.canonicalize_nans = u.arbitrary()?;
27
498
    config.custom_page_sizes_enabled = u.arbitrary()?;
28
498
    config.wide_arithmetic_enabled = u.arbitrary()?;
29
498
    config.shared_everything_threads_enabled = u.arbitrary()?;
30
31
498
    configure(&mut config, u)?;
32
33
    // Use wasm-smith to generate an arbitrary module and convert it to wasm
34
    // bytes.
35
498
    let mut module = Module::new(config.clone(), u)?;
36
498
    let bytes = module.to_bytes();
37
38
    // 10% of the time, ish, test that the `ensure_termination` method will
39
    // still produce a valid module.
40
498
    if u.ratio(1, 10)? {
41
388
        log::debug!("ensuring termination with 100 fuel");
42
388
        let _ = module.ensure_termination(100);
43
110
    }
44
45
498
    log_wasm(&bytes, &config);
46
47
498
    Ok((bytes, config))
48
498
}
49
50
0
pub fn generate_valid_component(
51
0
    u: &mut Unstructured,
52
0
    configure: impl FnOnce(&mut Config, &mut Unstructured<'_>) -> Result<()>,
53
0
) -> Result<(Vec<u8>, Config)> {
54
0
    let mut config: Config = u.arbitrary()?;
55
56
    // These are disabled in the swarm config by default, but we want to test
57
    // them. Use the input data to determine whether these features are enabled.
58
0
    config.simd_enabled = u.arbitrary()?;
59
0
    config.relaxed_simd_enabled = config.simd_enabled && u.arbitrary()?;
60
0
    config.exceptions_enabled = u.arbitrary()?;
61
0
    config.canonicalize_nans = u.arbitrary()?;
62
63
0
    configure(&mut config, u)?;
64
65
    // Use wasm-smith to generate an arbitrary component and convert it to wasm
66
    // bytes.
67
0
    let component = Component::new(config.clone(), u)?;
68
0
    let bytes = component.to_bytes();
69
70
0
    log_wasm(&bytes, &config);
71
72
0
    Ok((bytes, config))
73
0
}
74
75
3.97k
pub fn validator_for_config(config: &Config) -> wasmparser::Validator {
76
3.97k
    wasmparser::Validator::new_with_features(config.features())
77
3.97k
}
78
79
// Optionally log the module and its configuration if we've gotten this
80
// far. Note that we don't do this unconditionally to avoid slowing down
81
// fuzzing, but this is expected to be enabled when debugging a failing
82
// fuzzer.
83
17.6k
pub fn log_wasm(wasm: &[u8], config: impl Debug) {
84
17.6k
    drop(env_logger::try_init());
85
86
17.6k
    if !log::log_enabled!(log::Level::Debug) {
87
17.6k
        return;
88
0
    }
89
90
    static CNT: AtomicUsize = AtomicUsize::new(0);
91
92
0
    let i = CNT.fetch_add(1, SeqCst);
93
94
0
    let wasm_file = format!("test{i}.wasm");
95
0
    let config_file = format!("test{i}.config");
96
0
    let wat_file = format!("test{i}.wat");
97
98
0
    log::debug!("writing test case to `{wasm_file}` ...");
99
0
    std::fs::write(&wasm_file, wasm).unwrap();
100
0
    std::fs::write(&config_file, format!("{config:#?}")).unwrap();
101
0
    if let Ok(wat) = wasmprinter::print_bytes(wasm) {
102
0
        log::debug!("writing text format to `{wat_file}` ...");
103
0
        std::fs::write(&wat_file, wat).unwrap();
104
0
    } else {
105
0
        drop(std::fs::remove_file(&wat_file));
106
0
    }
107
17.6k
}
wasm_tools_fuzz::log_wasm::<&wasmprinter::Config>
Line
Count
Source
83
5
pub fn log_wasm(wasm: &[u8], config: impl Debug) {
84
5
    drop(env_logger::try_init());
85
86
5
    if !log::log_enabled!(log::Level::Debug) {
87
5
        return;
88
0
    }
89
90
    static CNT: AtomicUsize = AtomicUsize::new(0);
91
92
0
    let i = CNT.fetch_add(1, SeqCst);
93
94
0
    let wasm_file = format!("test{i}.wasm");
95
0
    let config_file = format!("test{i}.config");
96
0
    let wat_file = format!("test{i}.wat");
97
98
0
    log::debug!("writing test case to `{wasm_file}` ...");
99
0
    std::fs::write(&wasm_file, wasm).unwrap();
100
0
    std::fs::write(&config_file, format!("{config:#?}")).unwrap();
101
0
    if let Ok(wat) = wasmprinter::print_bytes(wasm) {
102
0
        log::debug!("writing text format to `{wat_file}` ...");
103
0
        std::fs::write(&wat_file, wat).unwrap();
104
0
    } else {
105
0
        drop(std::fs::remove_file(&wat_file));
106
0
    }
107
5
}
wasm_tools_fuzz::log_wasm::<&wasm_smith::config::Config>
Line
Count
Source
83
17.6k
pub fn log_wasm(wasm: &[u8], config: impl Debug) {
84
17.6k
    drop(env_logger::try_init());
85
86
17.6k
    if !log::log_enabled!(log::Level::Debug) {
87
17.6k
        return;
88
0
    }
89
90
    static CNT: AtomicUsize = AtomicUsize::new(0);
91
92
0
    let i = CNT.fetch_add(1, SeqCst);
93
94
0
    let wasm_file = format!("test{i}.wasm");
95
0
    let config_file = format!("test{i}.config");
96
0
    let wat_file = format!("test{i}.wat");
97
98
0
    log::debug!("writing test case to `{wasm_file}` ...");
99
0
    std::fs::write(&wasm_file, wasm).unwrap();
100
0
    std::fs::write(&config_file, format!("{config:#?}")).unwrap();
101
0
    if let Ok(wat) = wasmprinter::print_bytes(wasm) {
102
0
        log::debug!("writing text format to `{wat_file}` ...");
103
0
        std::fs::write(&wat_file, wat).unwrap();
104
0
    } else {
105
0
        drop(std::fs::remove_file(&wat_file));
106
0
    }
107
17.6k
}
wasm_tools_fuzz::log_wasm::<&str>
Line
Count
Source
83
4
pub fn log_wasm(wasm: &[u8], config: impl Debug) {
84
4
    drop(env_logger::try_init());
85
86
4
    if !log::log_enabled!(log::Level::Debug) {
87
4
        return;
88
0
    }
89
90
    static CNT: AtomicUsize = AtomicUsize::new(0);
91
92
0
    let i = CNT.fetch_add(1, SeqCst);
93
94
0
    let wasm_file = format!("test{i}.wasm");
95
0
    let config_file = format!("test{i}.config");
96
0
    let wat_file = format!("test{i}.wat");
97
98
0
    log::debug!("writing test case to `{wasm_file}` ...");
99
0
    std::fs::write(&wasm_file, wasm).unwrap();
100
0
    std::fs::write(&config_file, format!("{config:#?}")).unwrap();
101
0
    if let Ok(wat) = wasmprinter::print_bytes(wasm) {
102
0
        log::debug!("writing text format to `{wat_file}` ...");
103
0
        std::fs::write(&wat_file, wat).unwrap();
104
0
    } else {
105
0
        drop(std::fs::remove_file(&wat_file));
106
0
    }
107
4
}
108
#[cfg(test)]
109
mod test {
110
    use arbitrary::{Result, Unstructured};
111
    use rand::{Rng, SeedableRng, rngs::SmallRng};
112
113
    fn gen_until_pass(mut f: impl FnMut(&mut Unstructured<'_>) -> Result<bool>) -> bool {
114
        let mut rng = SmallRng::seed_from_u64(0);
115
        let mut buf = vec![0; 2048];
116
        let n = 3000;
117
        for _ in 0..n {
118
            rng.fill_bytes(&mut buf);
119
            let mut u = Unstructured::new(&buf);
120
121
            if f(&mut u).unwrap() {
122
                return true;
123
            }
124
        }
125
        false
126
    }
127
128
    /// Runs `f` with random data until it returns `Ok(())` `iters` times.
129
    pub fn test_n_times(
130
        iters: u32,
131
        mut f: impl FnMut(&mut Unstructured<'_>) -> arbitrary::Result<()>,
132
    ) {
133
        let mut to_test = 0..iters;
134
        let ok = gen_until_pass(|b| {
135
            if f(b).is_ok() {
136
                Ok(to_test.next().is_none())
137
            } else {
138
                Ok(false)
139
            }
140
        });
141
        assert!(ok);
142
    }
143
}