Coverage Report

Created: 2023-04-25 07:07

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