Coverage Report

Created: 2025-01-06 07:43

/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
361
pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
5
361
    // Either use `wasm-smith` to generate a module with possibly invalid
6
361
    // functions or try validating raw bytes from the input itself.
7
361
    if u.arbitrary()? {
8
351
        validate_maybe_invalid_module(u)?;
9
    } else {
10
10
        validate_raw_bytes(u)?;
11
    }
12
361
    Ok(())
13
361
}
14
15
351
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
351
    let (wasm, config) = crate::generate_valid_module(u, |config, _| {
21
351
        config.allow_invalid_funcs = true;
22
351
        Ok(())
23
351
    })?;
24
351
    validate_all(u, crate::validator_for_config(&config), &wasm)
25
351
}
26
27
10
pub fn validate_raw_bytes(u: &mut Unstructured<'_>) -> Result<()> {
28
    // Enable arbitrary combinations of features to validate the input bytes.
29
10
    let validator = Validator::new_with_features(WasmFeatures::from_bits_truncate(u.arbitrary()?));
30
10
    let wasm = u.bytes(u.len())?;
31
10
    crate::log_wasm(wasm, "");
32
10
    validate_all(u, validator, wasm)
33
10
}
34
35
361
fn validate_all(u: &mut Unstructured<'_>, mut validator: Validator, wasm: &[u8]) -> Result<()> {
36
361
    // First try printing this module. Generate a random configuration for
37
361
    // printing and then see what happens. Mostly making sure nothing panics
38
361
    // here.
39
361
    let mut cfg = wasmprinter::Config::new();
40
361
    cfg.fold_instructions(u.arbitrary()?);
41
361
    cfg.print_skeleton(u.arbitrary()?);
42
361
    cfg.print_offsets(u.arbitrary()?);
43
361
    cfg.name_unnamed(u.arbitrary()?);
44
361
    log::debug!("print config {cfg:?}");
45
361
    let mut wat = String::new();
46
361
    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.82k
    for payload in Parser::new(0).parse_all(wasm) {
52
5.82k
        let payload = match payload {
53
5.81k
            Ok(p) => p,
54
10
            Err(_) => return Ok(()),
55
        };
56
57
5.81k
        if validator.payload(&payload).is_err() {
58
0
            return Ok(());
59
5.81k
        }
60
61
        // Check that the payload's range is in bounds, since the payload is
62
        // supposedly valid.
63
        use wasmparser::Payload::*;
64
5.81k
        match payload {
65
351
            Version { range, .. } => assert!(wasm.get(range).is_some()),
66
314
            TypeSection(s) => assert!(wasm.get(s.range()).is_some()),
67
218
            ImportSection(s) => assert!(wasm.get(s.range()).is_some()),
68
216
            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
214
            GlobalSection(s) => assert!(wasm.get(s.range()).is_some()),
73
130
            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
216
            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
351
            End(_) => {}
105
106
0
            _ => {}
107
        }
108
    }
109
110
351
    Ok(())
111
361
}