Coverage Report

Created: 2026-02-26 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmi/fuzz/fuzz_targets/execute.rs
Line
Count
Source
1
#![no_main]
2
3
use arbitrary::{Arbitrary, Unstructured};
4
use libfuzzer_sys::fuzz_target;
5
use wasmi::{Config, Engine, Export, Linker, Module, Store, StoreLimitsBuilder, Val, ValType};
6
use wasmi_fuzz::{
7
    FuzzModule,
8
    FuzzSmithConfig,
9
    FuzzVal,
10
    FuzzValType,
11
    FuzzWasmiConfig,
12
    config::ValidationMode,
13
};
14
15
#[derive(Debug)]
16
pub struct FuzzInput<'a> {
17
    config: FuzzWasmiConfig,
18
    module: FuzzModule,
19
    u: Unstructured<'a>,
20
}
21
22
impl<'a> Arbitrary<'a> for FuzzInput<'a> {
23
17.3k
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
24
17.3k
        let config = FuzzWasmiConfig::arbitrary(u)?;
25
17.3k
        let mut fuzz_config = FuzzSmithConfig::arbitrary(u)?;
26
17.3k
        fuzz_config.allow_execution();
27
17.3k
        fuzz_config.export_everything();
28
17.3k
        let module = FuzzModule::new(fuzz_config, u)?;
29
17.2k
        Ok(Self {
30
17.2k
            config,
31
17.2k
            module,
32
17.2k
            u: Unstructured::new(&[]),
33
17.2k
        })
34
17.3k
    }
35
36
17.3k
    fn arbitrary_take_rest(mut u: Unstructured<'a>) -> arbitrary::Result<Self> {
37
17.3k
        Self::arbitrary(&mut u).map(|mut input| {
38
17.2k
            input.u = u;
39
17.2k
            input
40
17.2k
        })
41
17.3k
    }
42
}
43
44
fuzz_target!(|input: FuzzInput| {
45
    let FuzzInput {
46
        config,
47
        module,
48
        mut u,
49
    } = input;
50
    let wasm_bytes = module.wasm().into_bytes();
51
    let wasm = &wasm_bytes[..];
52
53
    let engine_config = {
54
        let mut config = Config::from(config);
55
        // We use Wasmi's built-in fuel metering since it is way faster
56
        // than `wasm_smith`'s fuel metering and thus allows the fuzzer
57
        // to expand its test coverage faster.
58
        config.consume_fuel(true);
59
        config
60
    };
61
    let engine = Engine::new(&engine_config);
62
    let linker = Linker::new(&engine);
63
    let limiter = StoreLimitsBuilder::new()
64
        .memory_size(1000 * 0x10000)
65
        .build();
66
    let mut store = Store::new(&engine, limiter);
67
    store.limiter(|lim| lim);
68
    let Ok(_) = store.set_fuel(1000) else {
69
        return;
70
    };
71
    if matches!(config.validation_mode, ValidationMode::Unchecked) {
72
        // We validate the Wasm module before handing it over to Wasmi
73
        // despite `wasm_smith` stating to only produce valid Wasm.
74
        // Translating an invalid Wasm module is undefined behavior.
75
        if Module::validate(&engine, wasm).is_err() {
76
            return;
77
        }
78
    }
79
    let status = match config.validation_mode {
80
        ValidationMode::Checked => Module::new(&engine, wasm),
81
        ValidationMode::Unchecked => {
82
            // Safety: we have just checked Wasm validity above.
83
            unsafe { Module::new_unchecked(&engine, wasm) }
84
        }
85
    };
86
    let module = status.unwrap();
87
    let Ok(instance) = linker.instantiate_and_start(&mut store, &module) else {
88
        return;
89
    };
90
91
    let mut params = Vec::new();
92
    let mut results = Vec::new();
93
94
    let funcs = instance
95
        .exports(&store)
96
        .filter_map(Export::into_func)
97
        .collect::<Vec<_>>();
98
    for func in funcs {
99
        let func_ty = func.ty(&store);
100
        fill_values(&mut params, func_ty.params(), &mut u);
101
        fill_values(&mut results, func_ty.results(), &mut u);
102
        _ = func.call(&mut store, &params, &mut results);
103
    }
104
});
105
106
/// Fill [`Val`]s of type `src` into `dst` using `u` for initialization.
107
///
108
/// Clears `dst` before the operation.
109
484k
fn fill_values(dst: &mut Vec<Val>, src: &[ValType], u: &mut Unstructured) {
110
484k
    dst.clear();
111
484k
    dst.extend(
112
484k
        src.iter()
113
484k
            .copied()
114
484k
            .map(FuzzValType::from)
115
1.99M
            .map(|ty| FuzzVal::with_type(ty, u))
116
484k
            .map(Val::from),
117
    );
118
484k
}