/src/wasm-tools/fuzz/fuzz_targets/roundtrip.rs
Line | Count | Source (jump to first uncovered line) |
1 | | #![no_main] |
2 | | |
3 | | use libfuzzer_sys::*; |
4 | | use std::str; |
5 | | |
6 | | fuzz_target!(|data: &[u8]| { |
7 | | let string = match str::from_utf8(data) { |
8 | | Ok(s) => s, |
9 | | Err(_) => return, |
10 | | }; |
11 | | // Weed out `(module binary ...)` because when we print the bytes and |
12 | | // convert it back to binary it's not guaranteed to be exactly the same. |
13 | | // (think of something like an over-long LEB encoding) |
14 | | if string.contains("binary") { |
15 | | return; |
16 | | } |
17 | | |
18 | | // Also weed out `@custom` custom sections since we don't print those right |
19 | | // now. |
20 | | if string.contains("@custom") { |
21 | | return; |
22 | | } |
23 | | let wasm = match wat::parse_str(string) { |
24 | | Ok(bytes) => bytes, |
25 | | Err(_) => return, |
26 | | }; |
27 | | |
28 | | // Only roundtrip valid modules for now since invalid modules can often have |
29 | | // bizarre structures which aren't intended to print correctly or roundtrip |
30 | | // well. |
31 | | if wasmparser::validate(&wasm).is_err() { |
32 | | return; |
33 | | } |
34 | | |
35 | | // And finally validate that the name section, if present, is valid. This |
36 | | // can be invalid if names in the name section are too long (e.g. exceeding |
37 | | // the maximum length of a string). The printing process will skip invalid |
38 | | // name sections, so if it's invalid then our roundtrip'd bytes will |
39 | | // trivially not match, but not in an interesting way. |
40 | | if validate_name_section(&wasm).is_err() { |
41 | | return; |
42 | | } |
43 | | let string2 = match wasmprinter::print_bytes(&wasm) { |
44 | | Ok(s) => s, |
45 | | Err(_) => return, |
46 | | }; |
47 | | |
48 | | let wasm2 = wat::parse_str(&string2).unwrap(); |
49 | | if wasm == wasm2 { |
50 | | return; |
51 | | } |
52 | | |
53 | | std::fs::write("wasm1.wasm", &wasm).unwrap(); |
54 | | std::fs::write("wasm1.wat", &string).unwrap(); |
55 | | std::fs::write("wasm2.wasm", &wasm2).unwrap(); |
56 | | std::fs::write("wasm2.wat", &string2).unwrap(); |
57 | | panic!("wasm bytes differ on roundtrip"); |
58 | | }); |
59 | | |
60 | 271 | fn validate_name_section(wasm: &[u8]) -> wasmparser::Result<()> { |
61 | | use wasmparser::*; |
62 | 275k | for payload in Parser::new(0).parse_all(wasm) { |
63 | 275k | let reader = match payload? { |
64 | | Payload::CustomSection { |
65 | 209 | name: "name", |
66 | 209 | data_offset, |
67 | 209 | data, |
68 | | range: _, |
69 | 209 | } => NameSectionReader::new(data, data_offset)?, |
70 | | _ => continue, |
71 | | }; |
72 | 229 | for section in reader { |
73 | 229 | match section? { |
74 | 5 | Name::Module(n) => { |
75 | 5 | n.get_name()?; |
76 | | } |
77 | 124 | Name::Function(n) => { |
78 | 124 | let mut map = n.get_map()?; |
79 | 125k | for _ in 0..map.get_count() { |
80 | 125k | map.read()?; |
81 | | } |
82 | | } |
83 | 100 | Name::Local(n) => { |
84 | 100 | let mut reader = n.get_function_local_reader()?; |
85 | 90.3k | for _ in 0..reader.get_count() { |
86 | 90.3k | let local_name = reader.read()?; |
87 | 90.3k | let mut map = local_name.get_map()?; |
88 | 175k | for _ in 0..map.get_count() { |
89 | 175k | map.read()?; |
90 | | } |
91 | | } |
92 | | } |
93 | | } |
94 | | } |
95 | | } |
96 | 250 | Ok(()) |
97 | 271 | } |