Coverage Report

Created: 2026-10-12 08:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/fuzz/src/no_traps.rs
Line
Count
Source
1
use arbitrary::{Result, Unstructured};
2
use wasmparser::WasmFeatures;
3
#[cfg(feature = "wasmtime")]
4
use wasmtime::*;
5
6
#[cfg(feature = "wasmtime")]
7
#[path = "../../crates/fuzz-stats/src/lib.rs"]
8
pub mod fuzz_stats;
9
10
// Define a fuzz target that accepts arbitrary
11
// `Module`s as input.
12
1.59k
pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
13
    // Use data to generate a random wasm module
14
1.59k
    let (wasm_bytes, config) = crate::generate_valid_module(u, |config, _| {
15
1.59k
        config.disallow_traps = true;
16
1.59k
        config.threads_enabled = false;
17
1.59k
        config.exceptions_enabled = false;
18
1.59k
        config.gc_enabled = false;
19
1.59k
        config.max_memory32_bytes = config.max_memory32_bytes.min(1 << 18);
20
1.59k
        config.max_memory64_bytes = config.max_memory64_bytes.min(1 << 18);
21
22
1.59k
        Ok(())
23
1.59k
    })?;
24
1.58k
    validate_module(&wasm_bytes);
25
26
    // Not implemented in wasmtime at this time.
27
1.58k
    if config.compact_imports_enabled {
28
493
        return Ok(());
29
1.09k
    }
30
31
    #[cfg(feature = "wasmtime")]
32
    {
33
        // Configure the engine, module, and store
34
        let mut eng_conf = wasmtime::Config::new();
35
        eng_conf.wasm_wide_arithmetic(true);
36
        eng_conf.wasm_custom_page_sizes(true);
37
        eng_conf.consume_fuel(true);
38
        let engine = Engine::new(&eng_conf).unwrap();
39
        let module = match Module::from_binary(&engine, &wasm_bytes) {
40
            Ok(m) => m,
41
            // Waiting on wasmtime to get published to fix this.
42
            Err(e) if format!("{e:?}").contains("unsupported init expr in element section") => {
43
                return Ok(());
44
            }
45
            Err(e) => panic!("failed to compile module {e:?}"),
46
        };
47
48
        // Call all exported functions
49
        for export in module.exports() {
50
            match export.ty() {
51
                ExternType::Func(func_ty) => {
52
                    let mut store = Store::new(
53
                        &engine,
54
                        fuzz_stats::limits::StoreLimits {
55
                            remaining_memory: 1 << 30,
56
                            oom: false,
57
                        },
58
                    );
59
                    store.limiter(|s| s as &mut dyn ResourceLimiter);
60
                    store.set_fuel(1_000).unwrap();
61
62
                    // Instantiate the module
63
                    let inst_result = fuzz_stats::dummy::dummy_imports(&mut store, &module)
64
                        .and_then(|imports| Instance::new(&mut store, &module, &imports));
65
                    let instance = match inst_result {
66
                        Ok(r) => r,
67
                        Err(err) => return Ok(check_err(err)),
68
                    };
69
70
                    let args = fuzz_stats::dummy::dummy_values(func_ty.params());
71
                    let mut results = fuzz_stats::dummy::dummy_values(func_ty.results());
72
                    let func = instance.get_func(&mut store, export.name()).unwrap();
73
                    match func.call(&mut store, &args, &mut results) {
74
                        Ok(_) => {}
75
                        Err(err) => check_err(err),
76
                    }
77
                }
78
                _ => continue,
79
            }
80
        }
81
82
        fn check_err(err: wasmtime::Error) {
83
            // Allow stack overflow since this generally can't be protected
84
            // against as it's an implementation detail of cranelift we could
85
            // expose regardless of the limits placed on the function.
86
            if let Some(wasmtime::Trap::StackOverflow) = err.downcast_ref::<wasmtime::Trap>() {
87
                return;
88
            }
89
90
            // Allow out of fuel on module instantiation
91
            if let Some(wasmtime::Trap::OutOfFuel) = err.downcast_ref::<wasmtime::Trap>() {
92
                return;
93
            }
94
95
            let s = err.to_string();
96
            // Allow "nominal" traps such as running out of fuel and the
97
            // module trying to allocate more resources than we'd like to
98
            // allow it (e.g. lots of memories or lots of tables).
99
            if s.contains("all fuel consumed") || s.contains("Insufficient resources") {
100
                return;
101
            }
102
103
            // Otherwise though this is a bug.
104
            panic!("generated wasm trapped in non-trapping mode: {}", err)
105
        }
106
    }
107
1.09k
    Ok(())
108
1.59k
}
109
110
1.58k
fn validate_module(wasm_bytes: &Vec<u8>) {
111
    // Validate the module or component and assert that it passes validation.
112
1.58k
    let mut validator = wasmparser::Validator::new_with_features(WasmFeatures::all());
113
1.58k
    if let Err(e) = validator.validate_all(wasm_bytes) {
114
0
        panic!("Invalid module: {e}");
115
1.58k
    }
116
1.58k
}
117
118
#[test]
119
fn smoke() {
120
    super::test::test_n_times(100, run);
121
}