Coverage Report

Created: 2025-01-09 07:53

/src/wasm-tools/fuzz/src/validate.rs
Line
Count
Source (jump to first uncovered line)
1
use arbitrary::{Result, Unstructured};
2
use wasmparser::{Parser, Validator, WasmFeatures};
3
4
357
pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
5
357
    // Either use `wasm-smith` to generate a module with possibly invalid
6
357
    // functions or try validating raw bytes from the input itself.
7
357
    if u.arbitrary()? {
8
350
        validate_maybe_invalid_module(u)?;
9
    } else {
10
7
        validate_raw_bytes(u)?;
11
    }
12
357
    Ok(())
13
357
}
14
15
350
pub fn validate_maybe_invalid_module(u: &mut Unstructured<'_>) -> Result<()> {
16
    // Generate a "valid" module but specifically allow invalid functions which
17
    // means that some functions may be defined from the input bytes raw. This
18
    // means that most of the module is valid but only some functions may be
19
    // invalid which can help stress various bits and pieces of validation.
20
350
    let (wasm, config) = crate::generate_valid_module(u, |config, _| {
21
350
        config.allow_invalid_funcs = true;
22
350
        Ok(())
23
350
    })?;
24
350
    validate_all(u, crate::validator_for_config(&config), &wasm)
25
350
}
26
27
7
pub fn validate_raw_bytes(u: &mut Unstructured<'_>) -> Result<()> {
28
    // Enable arbitrary combinations of features to validate the input bytes.
29
7
    let validator = Validator::new_with_features(WasmFeatures::from_bits_truncate(u.arbitrary()?));
30
7
    let wasm = u.bytes(u.len())?;
31
7
    crate::log_wasm(wasm, "");
32
7
    validate_all(u, validator, wasm)
33
7
}
34
35
357
fn validate_all(u: &mut Unstructured<'_>, mut validator: Validator, wasm: &[u8]) -> Result<()> {
36
357
    // First try printing this module. Generate a random configuration for
37
357
    // printing and then see what happens. Mostly making sure nothing panics
38
357
    // here.
39
357
    let mut cfg = wasmprinter::Config::new();
40
357
    cfg.fold_instructions(u.arbitrary()?);
41
357
    cfg.print_skeleton(u.arbitrary()?);
42
357
    cfg.print_offsets(u.arbitrary()?);
43
357
    cfg.name_unnamed(u.arbitrary()?);
44
357
    log::debug!("print config {cfg:?}");
45
357
    let mut wat = String::new();
46
357
    let _ = cfg.print(wasm, &mut wasmprinter::PrintFmtWrite(&mut wat));
47
48
    // After printing then try to parse and validate the module. See how far we
49
    // get as invalid modules are explicitly allowed here. Generally looking for
50
    // panics and excessive resource usage here.
51
5.80k
    for payload in Parser::new(0).parse_all(wasm) {
52
5.80k
        let payload = match payload {
53
5.80k
            Ok(p) => p,
54
7
            Err(_) => return Ok(()),
55
        };
56
57
5.80k
        if validator.payload(&payload).is_err() {
58
0
            return Ok(());
59
5.80k
        }
60
61
        // Check that the payload's range is in bounds, since the payload is
62
        // supposedly valid.
63
        use wasmparser::Payload::*;
64
5.80k
        match payload {
65
350
            Version { range, .. } => assert!(wasm.get(range).is_some()),
66
313
            TypeSection(s) => assert!(wasm.get(s.range()).is_some()),
67
217
            ImportSection(s) => assert!(wasm.get(s.range()).is_some()),
68
215
            FunctionSection(s) => assert!(wasm.get(s.range()).is_some()),
69
155
            TableSection(s) => assert!(wasm.get(s.range()).is_some()),
70
125
            MemorySection(s) => assert!(wasm.get(s.range()).is_some()),
71
43
            TagSection(s) => assert!(wasm.get(s.range()).is_some()),
72
213
            GlobalSection(s) => assert!(wasm.get(s.range()).is_some()),
73
129
            ExportSection(s) => assert!(wasm.get(s.range()).is_some()),
74
26
            StartSection { range, .. } => assert!(wasm.get(range).is_some()),
75
81
            ElementSection(s) => assert!(wasm.get(s.range()).is_some()),
76
59
            DataCountSection { range, .. } => assert!(wasm.get(range).is_some()),
77
78
            DataSection(s) => assert!(wasm.get(s.range()).is_some()),
78
215
            CodeSectionStart { range, .. } => assert!(wasm.get(range).is_some()),
79
3.23k
            CodeSectionEntry(body) => assert!(wasm.get(body.range()).is_some()),
80
0
            InstanceSection(s) => assert!(wasm.get(s.range()).is_some()),
81
0
            CoreTypeSection(s) => assert!(wasm.get(s.range()).is_some()),
82
0
            ComponentInstanceSection(s) => assert!(wasm.get(s.range()).is_some()),
83
0
            ComponentAliasSection(s) => assert!(wasm.get(s.range()).is_some()),
84
0
            ComponentTypeSection(s) => assert!(wasm.get(s.range()).is_some()),
85
0
            ComponentCanonicalSection(s) => assert!(wasm.get(s.range()).is_some()),
86
0
            ComponentStartSection { range, .. } => assert!(wasm.get(range).is_some()),
87
0
            ComponentImportSection(s) => assert!(wasm.get(s.range()).is_some()),
88
0
            ComponentExportSection(s) => assert!(wasm.get(s.range()).is_some()),
89
0
            CustomSection(s) => assert!(wasm.get(s.range()).is_some()),
90
0
            UnknownSection { range, .. } => assert!(wasm.get(range).is_some()),
91
92
            // In order to support streaming parsing and validation, these
93
            // sections' ranges are not checked during validation, since they
94
            // contain nested sections and we don't want to require all nested
95
            // sections are present before we can parse/validate any of them.
96
            ComponentSection {
97
                unchecked_range: _, ..
98
            }
99
            | ModuleSection {
100
                unchecked_range: _, ..
101
0
            } => {}
102
103
            // No associated range.
104
350
            End(_) => {}
105
106
0
            _ => {}
107
        }
108
    }
109
110
350
    Ok(())
111
357
}