/src/wasm-tools/fuzz/fuzz_targets/incremental-parse.rs
Line | Count | Source |
1 | | #![no_main] |
2 | | |
3 | | use libfuzzer_sys::*; |
4 | | use wasmparser::*; |
5 | | use Payload::*; |
6 | | |
7 | | // Simulate receiving chunks of data by fuzzing over a `Vec<Vec<u8>>` where each |
8 | | // element of the outer vec is a chunk of data we received. |
9 | | // |
10 | | // The assertion here is that parsing everything in one go should always produce |
11 | | // the exact same results as an incremental parse. |
12 | | fuzz_target!(|data: Vec<Vec<u8>>| { |
13 | | drop(env_logger::try_init()); |
14 | | |
15 | | // Concatenate everything together, create our expected iterator of |
16 | | // payloads, and then write out `input.wasm` if debugging is enabled. |
17 | 41.1M | let everything = data.iter().flat_map(|a| a).copied().collect::<Vec<_>>(); |
18 | | let mut expected = Parser::new(0).parse_all(&everything); |
19 | | if log::log_enabled!(log::Level::Debug) { |
20 | | std::fs::write("input.wasm", &everything).unwrap(); |
21 | | } |
22 | | |
23 | | // Create our parser as well as a stack of nested parsers for parsing nested |
24 | | // modules. |
25 | | let mut stack = Vec::new(); |
26 | | let mut parser = Parser::new(0); |
27 | | |
28 | | // We'll be parsing data from `buf` starting at `pos`, and we translate |
29 | | // `data` into an iterator of chunks so when requested we'll take another |
30 | | // chunk of data and feed it in. |
31 | | let mut pos = 0; |
32 | | let mut buf = Vec::new(); |
33 | | let mut data = data.into_iter().peekable(); |
34 | | loop { |
35 | | log::debug!("parsing {}..{}", pos, buf.len()); |
36 | | let payload = match parser.parse(&buf[pos..], data.peek().is_none()) { |
37 | | // If more data is requested then we're guaranteed that `data` |
38 | | // should have another element in its iterato, so pull that off and |
39 | | // add it to the end of the `buf`. |
40 | | Ok(Chunk::NeedMoreData(_n)) => { |
41 | | buf.extend(data.next().unwrap()); |
42 | | continue; |
43 | | } |
44 | | Ok(Chunk::Parsed { consumed, payload }) => { |
45 | | log::debug!("parsed {} bytes", consumed); |
46 | | pos += consumed; |
47 | | payload |
48 | | } |
49 | | |
50 | | // On failure we should receive the same failure as if we did a full |
51 | | // parse. |
52 | | Err(actual) => { |
53 | | let expected = expected |
54 | | .next() |
55 | | .expect("full parse stopped early") |
56 | | .err() |
57 | | .expect("full parse didn't return an error"); |
58 | | assert_eq!(expected.offset(), actual.offset()); |
59 | | assert_eq!(expected.message(), actual.message()); |
60 | | break; |
61 | | } |
62 | | }; |
63 | | log::debug!("parsed payload {:?}", payload); |
64 | | let expected_payload = expected |
65 | | .next() |
66 | | .expect("full parse stopped early") |
67 | | .expect("full parse failed but incremental succeeded"); |
68 | | match (payload, expected_payload) { |
69 | | (End(_), End(_)) => match stack.pop() { |
70 | | Some(p) => parser = p, |
71 | | None => { |
72 | | log::debug!("no more parsers"); |
73 | | assert!(expected.next().is_none()); |
74 | | break; |
75 | | } |
76 | | }, |
77 | | ( |
78 | | Version { |
79 | | num: a, |
80 | | encoding: ae, |
81 | | range: ar, |
82 | | }, |
83 | | Version { |
84 | | num: b, |
85 | | encoding: be, |
86 | | range: br, |
87 | | }, |
88 | | ) => { |
89 | | assert_eq!(a, b); |
90 | | assert_eq!(ae, be); |
91 | | assert_eq!(ar, br); |
92 | | } |
93 | | |
94 | | (TypeSection(a), TypeSection(b)) => assert_eq!(a.range(), b.range()), |
95 | | (ImportSection(a), ImportSection(b)) => assert_eq!(a.range(), b.range()), |
96 | | (FunctionSection(a), FunctionSection(b)) => assert_eq!(a.range(), b.range()), |
97 | | (TableSection(a), TableSection(b)) => assert_eq!(a.range(), b.range()), |
98 | | (MemorySection(a), MemorySection(b)) => assert_eq!(a.range(), b.range()), |
99 | | (GlobalSection(a), GlobalSection(b)) => assert_eq!(a.range(), b.range()), |
100 | | (ExportSection(a), ExportSection(b)) => assert_eq!(a.range(), b.range()), |
101 | | (TagSection(a), TagSection(b)) => assert_eq!(a.range(), b.range()), |
102 | | (StartSection { func: a, range: ar }, StartSection { func: b, range: br }) => { |
103 | | assert_eq!(a, b); |
104 | | assert_eq!(ar, br); |
105 | | } |
106 | | (ElementSection(a), ElementSection(b)) => assert_eq!(a.range(), b.range()), |
107 | | ( |
108 | | DataCountSection { |
109 | | count: a, |
110 | | range: ar, |
111 | | }, |
112 | | DataCountSection { |
113 | | count: b, |
114 | | range: br, |
115 | | }, |
116 | | ) => { |
117 | | assert_eq!(a, b); |
118 | | assert_eq!(ar, br); |
119 | | } |
120 | | (DataSection(a), DataSection(b)) => assert_eq!(a.range(), b.range()), |
121 | | (CustomSection(ca), CustomSection(cb)) => { |
122 | | assert_eq!(ca.name(), cb.name()); |
123 | | assert_eq!(ca.data_offset(), cb.data_offset()); |
124 | | assert_eq!(ca.data(), cb.data()); |
125 | | assert_eq!(ca.range(), cb.range()); |
126 | | } |
127 | | ( |
128 | | CodeSectionStart { |
129 | | count: a, |
130 | | range: ar, |
131 | | size: asz, |
132 | | }, |
133 | | CodeSectionStart { |
134 | | count: b, |
135 | | range: br, |
136 | | size: bsz, |
137 | | }, |
138 | | ) => { |
139 | | assert_eq!(a, b); |
140 | | assert_eq!(ar, br); |
141 | | assert_eq!(asz, bsz); |
142 | | } |
143 | | |
144 | | (CodeSectionEntry(a), CodeSectionEntry(b)) => { |
145 | | assert_eq!(a.get_binary_reader().range(), b.get_binary_reader().range()); |
146 | | } |
147 | | |
148 | | ( |
149 | | ModuleSection { |
150 | | parser: p, |
151 | | range: a, |
152 | | }, |
153 | | ModuleSection { range: b, .. }, |
154 | | ) => { |
155 | | assert_eq!(a, b); |
156 | | stack.push(parser); |
157 | | parser = p; |
158 | | } |
159 | | (InstanceSection(a), InstanceSection(b)) => assert_eq!(a.range(), b.range()), |
160 | | ( |
161 | | ComponentSection { |
162 | | parser: p, |
163 | | range: a, |
164 | | }, |
165 | | ComponentSection { range: b, .. }, |
166 | | ) => { |
167 | | assert_eq!(a, b); |
168 | | stack.push(parser); |
169 | | parser = p; |
170 | | } |
171 | | (ComponentInstanceSection(a), ComponentInstanceSection(b)) => { |
172 | | assert_eq!(a.range(), b.range()) |
173 | | } |
174 | | (ComponentAliasSection(a), ComponentAliasSection(b)) => { |
175 | | assert_eq!(a.range(), b.range()) |
176 | | } |
177 | | (ComponentTypeSection(a), ComponentTypeSection(b)) => assert_eq!(a.range(), b.range()), |
178 | | (ComponentCanonicalSection(a), ComponentCanonicalSection(b)) => { |
179 | | assert_eq!(a.range(), b.range()) |
180 | | } |
181 | | (ComponentStartSection { range: a, .. }, ComponentStartSection { range: b, .. }) => { |
182 | | assert_eq!(a, b) |
183 | | } |
184 | | (ComponentImportSection(a), ComponentImportSection(b)) => { |
185 | | assert_eq!(a.range(), b.range()) |
186 | | } |
187 | | (ComponentExportSection(a), ComponentExportSection(b)) => { |
188 | | assert_eq!(a.range(), b.range()) |
189 | | } |
190 | | (CoreTypeSection(a), CoreTypeSection(b)) => { |
191 | | assert_eq!(a.range(), b.range()) |
192 | | } |
193 | | |
194 | | ( |
195 | | UnknownSection { |
196 | | id: a, |
197 | | contents: ac, |
198 | | range: ar, |
199 | | }, |
200 | | UnknownSection { |
201 | | id: b, |
202 | | contents: bc, |
203 | | range: br, |
204 | | }, |
205 | | ) => { |
206 | | assert_eq!(a, b); |
207 | | assert_eq!(ar, br); |
208 | | assert_eq!(ac, bc); |
209 | | } |
210 | | |
211 | | (a, b) => { |
212 | | panic!("expected {:?}\ngot {:?}", b, a); |
213 | | } |
214 | | } |
215 | | } |
216 | | }); |