/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 | | drop(env_logger::try_init()); |
12 | | write_file("wasm1.wat", &string); |
13 | | // Weed out `(module binary ...)` because when we print the bytes and |
14 | | // convert it back to binary it's not guaranteed to be exactly the same. |
15 | | // (think of something like an over-long LEB encoding) |
16 | | if string.contains("binary") { |
17 | | return; |
18 | | } |
19 | | |
20 | | // Also weed out `@custom` custom sections since we don't print those right |
21 | | // now. |
22 | | if string.contains("@custom") { |
23 | | return; |
24 | | } |
25 | | let wasm = match wat::parse_str(string) { |
26 | | Ok(bytes) => bytes, |
27 | | Err(_) => return, |
28 | | }; |
29 | | write_file("wasm1.wasm", &wasm); |
30 | | |
31 | | // Only roundtrip valid modules for now since invalid modules can often have |
32 | | // bizarre structures which aren't intended to print correctly or roundtrip |
33 | | // well. |
34 | | if wasmparser::validate(&wasm).is_err() { |
35 | | return; |
36 | | } |
37 | | |
38 | | // And finally validate that the name section, if present, is valid. This |
39 | | // can be invalid if names in the name section are too long (e.g. exceeding |
40 | | // the maximum length of a string). The printing process will skip invalid |
41 | | // name sections, so if it's invalid then our roundtrip'd bytes will |
42 | | // trivially not match, but not in an interesting way. |
43 | | if validate_name_section(&wasm).is_err() { |
44 | | return; |
45 | | } |
46 | | let string2 = match wasmprinter::print_bytes(&wasm) { |
47 | | Ok(s) => s, |
48 | | Err(_) => return, |
49 | | }; |
50 | | write_file("wasm2.wat", &string2); |
51 | | |
52 | | let wasm2 = wat::parse_str(&string2).unwrap(); |
53 | | write_file("wasm2.wasm", &wasm2); |
54 | | if wasm == wasm2 { |
55 | | return; |
56 | | } |
57 | | |
58 | | panic!("wasm bytes differ on roundtrip"); |
59 | | }); |
60 | | |
61 | 23.8k | fn write_file(path: &str, contents: impl AsRef<[u8]>) { |
62 | 23.8k | if !log::log_enabled!(log::Level::Debug) { |
63 | 23.8k | return; |
64 | 0 | } |
65 | 0 | log::debug!("writing file {path}"); |
66 | 0 | std::fs::write(path, contents.as_ref()).unwrap(); |
67 | 23.8k | } roundtrip::write_file::<&alloc::vec::Vec<u8>> Line | Count | Source | 61 | 10.6k | fn write_file(path: &str, contents: impl AsRef<[u8]>) { | 62 | 10.6k | if !log::log_enabled!(log::Level::Debug) { | 63 | 10.6k | return; | 64 | 0 | } | 65 | 0 | log::debug!("writing file {path}"); | 66 | 0 | std::fs::write(path, contents.as_ref()).unwrap(); | 67 | 10.6k | } |
roundtrip::write_file::<&alloc::string::String> Line | Count | Source | 61 | 2.99k | fn write_file(path: &str, contents: impl AsRef<[u8]>) { | 62 | 2.99k | if !log::log_enabled!(log::Level::Debug) { | 63 | 2.99k | return; | 64 | 0 | } | 65 | 0 | log::debug!("writing file {path}"); | 66 | 0 | std::fs::write(path, contents.as_ref()).unwrap(); | 67 | 2.99k | } |
roundtrip::write_file::<&&str> Line | Count | Source | 61 | 10.1k | fn write_file(path: &str, contents: impl AsRef<[u8]>) { | 62 | 10.1k | if !log::log_enabled!(log::Level::Debug) { | 63 | 10.1k | return; | 64 | 0 | } | 65 | 0 | log::debug!("writing file {path}"); | 66 | 0 | std::fs::write(path, contents.as_ref()).unwrap(); | 67 | 10.1k | } |
|
68 | | |
69 | 2.99k | fn validate_name_section(wasm: &[u8]) -> wasmparser::Result<()> { |
70 | | use wasmparser::*; |
71 | 102k | for payload in Parser::new(0).parse_all(wasm) { |
72 | 102k | let reader = match payload? { |
73 | 1.00k | Payload::CustomSection(c) if c.name() == "name" => { |
74 | 1.00k | NameSectionReader::new(c.data(), c.data_offset()) |
75 | | } |
76 | 101k | _ => continue, |
77 | | }; |
78 | 2.33k | for section in reader { |
79 | 1.33k | match section? { |
80 | 4 | Name::Module { .. } => {} |
81 | 591 | Name::Function(n) |
82 | 0 | | Name::Type(n) |
83 | 6 | | Name::Table(n) |
84 | 2 | | Name::Memory(n) |
85 | 3 | | Name::Global(n) |
86 | 547 | | Name::Element(n) |
87 | 3 | | Name::Data(n) => { |
88 | 106k | for name in n { |
89 | 104k | name?; |
90 | | } |
91 | | } |
92 | 91 | Name::Local(n) | Name::Label(n) => { |
93 | 21.7k | for name in n { |
94 | 81.8k | for name in name?.names { |
95 | 81.8k | name?; |
96 | | } |
97 | | } |
98 | | } |
99 | 0 | Name::Unknown { .. } => {} |
100 | | } |
101 | | } |
102 | | } |
103 | 2.99k | Ok(()) |
104 | 2.99k | } |