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