/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 | } |