Coverage Report

Created: 2026-08-15 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmtime/crates/fuzzing/src/mutators.rs
Line
Count
Source
1
//! Custom fuzz input mutators.
2
//!
3
//! The functions in this module are intended to be used with [the
4
//! `libfuzzer_sys::fuzz_mutator!` macro][fuzz-mutator].
5
//!
6
//! [fuzz-mutator]: https://docs.rs/libfuzzer-sys/latest/libfuzzer_sys/macro.fuzz_mutator.html
7
8
use arbitrary::{Arbitrary, Unstructured};
9
use std::sync::Arc;
10
11
/// Use [`wasm-mutate`][wasm-mutate] to mutate a fuzz input.
12
///
13
/// [wasm-mutate]: https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-mutate
14
0
pub fn wasm_mutate(
15
0
    data: &mut [u8],
16
0
    size: usize,
17
0
    max_size: usize,
18
0
    seed: u32,
19
0
    libfuzzer_mutate: fn(data: &mut [u8], size: usize, max_size: usize) -> usize,
20
0
) -> usize {
21
    const MUTATION_FUEL: u64 = 100;
22
    const MUTATION_ITERS: usize = 100;
23
24
0
    let wasm = &data[..size];
25
26
0
    if wasmparser::validate(wasm).is_ok() {
27
0
        let mut wasm_mutate = wasm_mutate::WasmMutate::default();
28
0
        wasm_mutate
29
0
            .seed(seed.into())
30
0
            .fuel(MUTATION_FUEL)
31
0
            .reduce(max_size < size)
32
0
            .raw_mutate_func(Some(Arc::new(move |data, max_size| {
33
0
                let len = data.len();
34
35
                // The given max could be very large, so clamp it to no more
36
                // than `len * 2` in any single, given mutation. This way we
37
                // don't over-allocate a bunch of space.
38
0
                let max_size = std::cmp::min(max_size, len * 2);
39
                // Also, the max must always be greater than zero (`libfuzzer`
40
                // asserts this).
41
0
                let max_size = std::cmp::max(max_size, 1);
42
43
                // Make sure we have capacity in case `libfuzzer` decides to
44
                // grow this data.
45
0
                if max_size > len {
46
0
                    data.resize(max_size, 0);
47
0
                }
48
49
                // Finally, have `libfuzzer` mutate the data!
50
0
                let new_len = libfuzzer_mutate(data, len, max_size);
51
52
                // Resize the data to the mutated size, releasing any extra
53
                // capacity that we don't need anymore.
54
0
                data.resize(new_len, 0);
55
0
                data.shrink_to_fit();
56
57
0
                Ok(())
58
0
            })));
59
60
0
        let wasm = wasm.to_vec();
61
0
        let mutations = wasm_mutate.run(&wasm);
62
0
        if let Ok(mutations) = mutations {
63
0
            for mutation in mutations.take(MUTATION_ITERS) {
64
0
                if let Ok(mutated_wasm) = mutation {
65
0
                    if mutated_wasm.len() <= max_size {
66
0
                        data[..mutated_wasm.len()].copy_from_slice(&mutated_wasm);
67
0
                        return mutated_wasm.len();
68
0
                    }
69
0
                }
70
            }
71
0
        }
72
0
    }
73
74
    // If we can't mutate the input because it isn't valid Wasm or `wasm-mutate`
75
    // otherwise fails, try to use `wasm-smith` to generate a new, arbitrary
76
    // Wasm module that fits within the max-size limit.
77
0
    let mut u = Unstructured::new(&data[..max_size]);
78
0
    if let Ok(module) = wasm_smith::Module::arbitrary(&mut u) {
79
0
        let wasm = module.to_bytes();
80
0
        if wasm.len() <= max_size {
81
0
            data[..wasm.len()].copy_from_slice(&wasm);
82
0
            return wasm.len();
83
0
        }
84
0
    }
85
86
    // Otherwise, try to return an empty Wasm module:
87
    //
88
    // ```
89
    // (module)
90
    // ```
91
    static EMPTY_WASM: &[u8] = &[0x00, b'a', b's', b'm', 0x01, 0x00, 0x00, 0x00];
92
0
    if EMPTY_WASM.len() <= max_size {
93
0
        data[..EMPTY_WASM.len()].copy_from_slice(EMPTY_WASM);
94
0
        return EMPTY_WASM.len();
95
0
    }
96
97
    // If the max size is even smaller than an empty Wasm module, then just let
98
    // `libfuzzer` mutate the data.
99
0
    libfuzzer_mutate(data, size, max_size)
100
0
}