/src/spice-usbredir/fuzzing/usbredirparserfuzz.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* usbredirparserfuzz.cc -- fuzzing for usbredirparser |
2 | | |
3 | | Copyright 2021 Michael Hanselmann |
4 | | |
5 | | This program is free software; you can redistribute it and/or |
6 | | modify it under the terms of the GNU General Public |
7 | | License as published by the Free Software Foundation; either |
8 | | version 2 of the License, or (at your option) any later version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include <array> |
20 | | #include <algorithm> |
21 | | #include <memory> |
22 | | #include <type_traits> |
23 | | |
24 | | #include <cassert> |
25 | | #include <cinttypes> |
26 | | #include <cstring> |
27 | | #include <limits> |
28 | | |
29 | | #include <sys/types.h> |
30 | | #include <unistd.h> |
31 | | |
32 | | #include <fuzzer/FuzzedDataProvider.h> |
33 | | |
34 | | #include "usbredirfilter.h" |
35 | | #include "usbredirparser.h" |
36 | | |
37 | | namespace { |
38 | | struct ParserDeleter { |
39 | 3.20k | void operator()(struct usbredirparser *p) { |
40 | 3.20k | usbredirparser_destroy(p); |
41 | 3.20k | } |
42 | | }; |
43 | | |
44 | | std::unique_ptr<struct usbredirparser, ParserDeleter> parser; |
45 | | std::unique_ptr<FuzzedDataProvider> fdp; |
46 | | |
47 | | void parser_log(void *priv, int level, const char *msg) |
48 | 866k | { |
49 | 866k | } |
50 | | |
51 | | int wobbly_read_write_count(int count) |
52 | 5.77M | { |
53 | 5.77M | if (count > (1024 * 1024)) { |
54 | 170 | return count; |
55 | 170 | } |
56 | | |
57 | 5.77M | return std::min(count, fdp->ConsumeIntegralInRange(1, 4 * count)); |
58 | 5.77M | } |
59 | | |
60 | | int parser_read(void *priv, uint8_t *data, int count) |
61 | 3.58M | { |
62 | | // Simulate short reads |
63 | 3.58M | return fdp->ConsumeData(data, wobbly_read_write_count(count)); |
64 | 3.58M | } |
65 | | |
66 | | // Read over complete input buffer to detect buffer overflows |
67 | | void read_all(const void *data, size_t count) |
68 | 2.19M | { |
69 | | #ifdef __cpp_lib_smart_ptr_for_overwrite |
70 | | const auto buf = std::make_unique_for_overwrite<uint8_t[]>(count); |
71 | | #else |
72 | 2.19M | const auto buf = std::make_unique<uint8_t[]>(count); |
73 | 2.19M | #endif |
74 | | |
75 | 2.19M | memcpy(buf.get(), data, count); |
76 | 2.19M | } |
77 | | |
78 | | template <typename T, typename = std::enable_if_t<std::is_class<T>::value>> |
79 | | void read_all(const T *ptr) |
80 | 3.13k | { |
81 | 3.13k | read_all(ptr, sizeof(T)); |
82 | 3.13k | } usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_control_packet_header, void>(usb_redir_control_packet_header const*) Line | Count | Source | 80 | 659 | { | 81 | 659 | read_all(ptr, sizeof(T)); | 82 | 659 | } |
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_bulk_packet_header, void>(usb_redir_bulk_packet_header const*) Line | Count | Source | 80 | 538 | { | 81 | 538 | read_all(ptr, sizeof(T)); | 82 | 538 | } |
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_iso_packet_header, void>(usb_redir_iso_packet_header const*) Line | Count | Source | 80 | 549 | { | 81 | 549 | read_all(ptr, sizeof(T)); | 82 | 549 | } |
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_interrupt_packet_header, void>(usb_redir_interrupt_packet_header const*) Line | Count | Source | 80 | 486 | { | 81 | 486 | read_all(ptr, sizeof(T)); | 82 | 486 | } |
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_start_bulk_receiving_header, void>(usb_redir_start_bulk_receiving_header const*) Line | Count | Source | 80 | 216 | { | 81 | 216 | read_all(ptr, sizeof(T)); | 82 | 216 | } |
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_stop_bulk_receiving_header, void>(usb_redir_stop_bulk_receiving_header const*) Line | Count | Source | 80 | 202 | { | 81 | 202 | read_all(ptr, sizeof(T)); | 82 | 202 | } |
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_bulk_receiving_status_header, void>(usb_redir_bulk_receiving_status_header const*) Line | Count | Source | 80 | 230 | { | 81 | 230 | read_all(ptr, sizeof(T)); | 82 | 230 | } |
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_buffered_bulk_packet_header, void>(usb_redir_buffered_bulk_packet_header const*) Line | Count | Source | 80 | 253 | { | 81 | 253 | read_all(ptr, sizeof(T)); | 82 | 253 | } |
|
83 | | |
84 | | int parser_write(void *priv, uint8_t *data, int count) |
85 | 2.19M | { |
86 | | // Simulate short writes |
87 | 2.19M | count = wobbly_read_write_count(count); |
88 | | |
89 | 2.19M | read_all(data, count); |
90 | | |
91 | 2.19M | return count; |
92 | 2.19M | } |
93 | | |
94 | | void parser_device_connect(void *priv, |
95 | | struct usb_redir_device_connect_header *device_connect) |
96 | 240 | { |
97 | 240 | } |
98 | | |
99 | | void parser_device_disconnect(void *priv) |
100 | 118k | { |
101 | 118k | } |
102 | | |
103 | | void parser_reset(void *priv) |
104 | 252 | { |
105 | 252 | } |
106 | | |
107 | | void parser_interface_info(void *priv, |
108 | | struct usb_redir_interface_info_header *info) |
109 | 199 | { |
110 | 199 | } |
111 | | |
112 | | void parser_ep_info(void *priv, |
113 | | struct usb_redir_ep_info_header *ep_info) |
114 | 220 | { |
115 | 220 | } |
116 | | |
117 | | void parser_set_configuration(void *priv, uint64_t id, |
118 | | struct usb_redir_set_configuration_header *set_configuration) |
119 | 322 | { |
120 | 322 | } |
121 | | |
122 | | void parser_get_configuration(void *priv, uint64_t id) |
123 | 197 | { |
124 | 197 | } |
125 | | |
126 | | void parser_configuration_status(void *priv, uint64_t id, |
127 | | struct usb_redir_configuration_status_header *config_status) |
128 | 220 | { |
129 | 220 | } |
130 | | |
131 | | void parser_set_alt_setting(void *priv, uint64_t id, |
132 | | struct usb_redir_set_alt_setting_header *set_alt_setting) |
133 | 194 | { |
134 | 194 | } |
135 | | |
136 | | void parser_get_alt_setting(void *priv, uint64_t id, |
137 | | struct usb_redir_get_alt_setting_header *get_alt_setting) |
138 | 196 | { |
139 | 196 | } |
140 | | |
141 | | void parser_alt_setting_status(void *priv, uint64_t id, |
142 | | struct usb_redir_alt_setting_status_header *alt_setting_status) |
143 | 195 | { |
144 | 195 | } |
145 | | |
146 | | void parser_start_iso_stream(void *priv, uint64_t id, |
147 | | struct usb_redir_start_iso_stream_header *start_iso_stream) |
148 | 194 | { |
149 | 194 | } |
150 | | |
151 | | void parser_stop_iso_stream(void *priv, uint64_t id, |
152 | | struct usb_redir_stop_iso_stream_header *stop_iso_stream) |
153 | 197 | { |
154 | 197 | } |
155 | | |
156 | | void parser_iso_stream_status(void *priv, uint64_t id, |
157 | | struct usb_redir_iso_stream_status_header *iso_stream_status) |
158 | 318 | { |
159 | 318 | } |
160 | | |
161 | | void parser_start_interrupt_receiving(void *priv, uint64_t id, |
162 | | struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving) |
163 | 354 | { |
164 | 354 | } |
165 | | |
166 | | void parser_stop_interrupt_receiving(void *priv, uint64_t id, |
167 | | struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving) |
168 | 322 | { |
169 | 322 | } |
170 | | |
171 | | void parser_interrupt_receiving_status(void *priv, uint64_t id, |
172 | | struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status) |
173 | 334 | { |
174 | 334 | } |
175 | | |
176 | | void parser_alloc_bulk_streams(void *priv, uint64_t id, |
177 | | struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams) |
178 | 209 | { |
179 | 209 | } |
180 | | |
181 | | void parser_free_bulk_streams(void *priv, uint64_t id, |
182 | | struct usb_redir_free_bulk_streams_header *free_bulk_streams) |
183 | 194 | { |
184 | 194 | } |
185 | | |
186 | | void parser_bulk_streams_status(void *priv, uint64_t id, |
187 | | struct usb_redir_bulk_streams_status_header *bulk_streams_status) |
188 | 194 | { |
189 | 194 | } |
190 | | |
191 | | void parser_cancel_data_packet(void *priv, uint64_t id) |
192 | 194 | { |
193 | 194 | } |
194 | | |
195 | | void parser_filter_reject(void *priv) |
196 | 196 | { |
197 | 196 | } |
198 | | |
199 | | void parser_filter_filter(void *priv, |
200 | | struct usbredirfilter_rule *rules, int rules_count) |
201 | 474 | { |
202 | 474 | usbredirfilter_free(rules); |
203 | 474 | } |
204 | | |
205 | | void parser_control_packet(void *priv, uint64_t id, |
206 | | struct usb_redir_control_packet_header *control_packet, |
207 | | uint8_t *data, int data_len) |
208 | 659 | { |
209 | 659 | read_all(control_packet); |
210 | 659 | read_all(data, data_len); |
211 | 659 | usbredirparser_free_packet_data(parser.get(), data); |
212 | 659 | } |
213 | | |
214 | | void parser_bulk_packet(void *priv, uint64_t id, |
215 | | struct usb_redir_bulk_packet_header *bulk_packet, |
216 | | uint8_t *data, int data_len) |
217 | 538 | { |
218 | 538 | read_all(bulk_packet); |
219 | 538 | read_all(data, data_len); |
220 | 538 | usbredirparser_free_packet_data(parser.get(), data); |
221 | 538 | } |
222 | | |
223 | | void parser_iso_packet(void *priv, uint64_t id, |
224 | | struct usb_redir_iso_packet_header *iso_packet, |
225 | | uint8_t *data, int data_len) |
226 | 549 | { |
227 | 549 | read_all(iso_packet); |
228 | 549 | read_all(data, data_len); |
229 | 549 | usbredirparser_free_packet_data(parser.get(), data); |
230 | 549 | } |
231 | | |
232 | | void parser_interrupt_packet(void *priv, uint64_t id, |
233 | | struct usb_redir_interrupt_packet_header *interrupt_packet, |
234 | | uint8_t *data, int data_len) |
235 | 486 | { |
236 | 486 | read_all(interrupt_packet); |
237 | 486 | read_all(data, data_len); |
238 | 486 | usbredirparser_free_packet_data(parser.get(), data); |
239 | 486 | } |
240 | | |
241 | | void parser_buffered_bulk_packet(void *priv, uint64_t id, |
242 | | struct usb_redir_buffered_bulk_packet_header *buffered_bulk_header, |
243 | | uint8_t *data, int data_len) |
244 | 253 | { |
245 | 253 | read_all(buffered_bulk_header); |
246 | 253 | read_all(data, data_len); |
247 | 253 | usbredirparser_free_packet_data(parser.get(), data); |
248 | 253 | } |
249 | | |
250 | | void *parser_alloc_lock() |
251 | 3.20k | { |
252 | 3.20k | return nullptr; |
253 | 3.20k | } |
254 | | |
255 | | void parser_lock(void *lock) |
256 | 0 | { |
257 | 0 | } |
258 | | |
259 | | void parser_unlock(void *lock) |
260 | 0 | { |
261 | 0 | } |
262 | | |
263 | | void parser_free_lock(void *lock) |
264 | 0 | { |
265 | 0 | } |
266 | | |
267 | | void parser_hello(void *priv, struct usb_redir_hello_header *h) |
268 | 76 | { |
269 | 76 | } |
270 | | |
271 | | void parser_device_disconnect_ack(void *priv) |
272 | 205 | { |
273 | 205 | } |
274 | | |
275 | | void parser_start_bulk_receiving(void *priv, uint64_t id, |
276 | | struct usb_redir_start_bulk_receiving_header *start_bulk_receiving) |
277 | 216 | { |
278 | 216 | read_all(start_bulk_receiving); |
279 | 216 | } |
280 | | |
281 | | void parser_stop_bulk_receiving(void *priv, uint64_t id, |
282 | | struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving) |
283 | 202 | { |
284 | 202 | read_all(stop_bulk_receiving); |
285 | 202 | } |
286 | | |
287 | | void parser_bulk_receiving_status(void *priv, uint64_t id, |
288 | | struct usb_redir_bulk_receiving_status_header *bulk_receiving_status) |
289 | 230 | { |
290 | 230 | read_all(bulk_receiving_status); |
291 | 230 | } |
292 | | |
293 | | int try_unserialize(struct usbredirparser *parser, FuzzedDataProvider *fdp) |
294 | 1.02k | { |
295 | 1.02k | std::vector<uint8_t> state; |
296 | 1.02k | size_t len = fdp->ConsumeIntegralInRange<size_t>(1, 64 * 1024); |
297 | | |
298 | 1.02k | state.reserve(len); |
299 | | |
300 | 1.02k | if (len >= 4) { |
301 | 991 | const uint32_t magic = USBREDIRPARSER_SERIALIZE_MAGIC; |
302 | 991 | assert(state.empty()); |
303 | 991 | state.resize(sizeof(magic)); |
304 | 991 | memcpy(state.data(), &magic, sizeof(magic)); |
305 | | |
306 | 991 | len -= 4; |
307 | 991 | } |
308 | | |
309 | 1.02k | if (len > 0) { |
310 | 1.02k | const std::vector<uint8_t> payload{fdp->ConsumeBytes<uint8_t>(len)}; |
311 | | |
312 | 1.02k | state.insert(state.end(), payload.cbegin(), payload.cend()); |
313 | 1.02k | } |
314 | | |
315 | 1.02k | if (state.empty()) { |
316 | 3 | return 0; |
317 | 3 | } |
318 | | |
319 | 1.02k | state.shrink_to_fit(); |
320 | | |
321 | 1.02k | return usbredirparser_unserialize(parser, &state[0], state.size()); |
322 | 1.02k | } |
323 | | |
324 | | int try_serialize(struct usbredirparser *parser) |
325 | 50.5k | { |
326 | 50.5k | uint8_t *state = nullptr; |
327 | 50.5k | int len = 0; |
328 | 50.5k | int ret; |
329 | | |
330 | 50.5k | ret = usbredirparser_serialize(parser, &state, &len); |
331 | | |
332 | 50.5k | if (ret == 0) { |
333 | 50.5k | free(state); |
334 | 50.5k | } |
335 | | |
336 | 50.5k | return ret; |
337 | 50.5k | } |
338 | | } // namespace |
339 | | |
340 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
341 | 3.20k | { |
342 | 3.20k | std::array<uint32_t, USB_REDIR_CAPS_SIZE> caps = {0}; |
343 | 3.20k | int ret; |
344 | | |
345 | 3.20k | fdp = std::make_unique<FuzzedDataProvider>(data, size); |
346 | | |
347 | 3.20k | parser.reset(usbredirparser_create()); |
348 | 3.20k | if (parser == nullptr) { |
349 | 0 | return 1; |
350 | 0 | } |
351 | | |
352 | 3.20k | parser->log_func = parser_log; |
353 | 3.20k | parser->read_func = parser_read; |
354 | 3.20k | parser->write_func = parser_write; |
355 | 3.20k | parser->device_connect_func = parser_device_connect; |
356 | 3.20k | parser->device_disconnect_func = parser_device_disconnect; |
357 | 3.20k | parser->reset_func = parser_reset; |
358 | 3.20k | parser->interface_info_func = parser_interface_info; |
359 | 3.20k | parser->ep_info_func = parser_ep_info; |
360 | 3.20k | parser->set_configuration_func = parser_set_configuration; |
361 | 3.20k | parser->get_configuration_func = parser_get_configuration; |
362 | 3.20k | parser->configuration_status_func = parser_configuration_status; |
363 | 3.20k | parser->set_alt_setting_func = parser_set_alt_setting; |
364 | 3.20k | parser->get_alt_setting_func = parser_get_alt_setting; |
365 | 3.20k | parser->alt_setting_status_func = parser_alt_setting_status; |
366 | 3.20k | parser->start_iso_stream_func = parser_start_iso_stream; |
367 | 3.20k | parser->stop_iso_stream_func = parser_stop_iso_stream; |
368 | 3.20k | parser->iso_stream_status_func = parser_iso_stream_status; |
369 | 3.20k | parser->start_interrupt_receiving_func = parser_start_interrupt_receiving; |
370 | 3.20k | parser->stop_interrupt_receiving_func = parser_stop_interrupt_receiving; |
371 | 3.20k | parser->interrupt_receiving_status_func = parser_interrupt_receiving_status; |
372 | 3.20k | parser->alloc_bulk_streams_func = parser_alloc_bulk_streams; |
373 | 3.20k | parser->free_bulk_streams_func = parser_free_bulk_streams; |
374 | 3.20k | parser->bulk_streams_status_func = parser_bulk_streams_status; |
375 | 3.20k | parser->cancel_data_packet_func = parser_cancel_data_packet; |
376 | 3.20k | parser->control_packet_func = parser_control_packet; |
377 | 3.20k | parser->bulk_packet_func = parser_bulk_packet; |
378 | 3.20k | parser->iso_packet_func = parser_iso_packet; |
379 | 3.20k | parser->interrupt_packet_func = parser_interrupt_packet; |
380 | 3.20k | parser->alloc_lock_func = parser_alloc_lock; |
381 | 3.20k | parser->lock_func = parser_lock; |
382 | 3.20k | parser->unlock_func = parser_unlock; |
383 | 3.20k | parser->free_lock_func = parser_free_lock; |
384 | 3.20k | parser->hello_func = parser_hello; |
385 | 3.20k | parser->filter_reject_func = parser_filter_reject; |
386 | 3.20k | parser->filter_filter_func = parser_filter_filter; |
387 | 3.20k | parser->device_disconnect_ack_func = parser_device_disconnect_ack; |
388 | 3.20k | parser->start_bulk_receiving_func = parser_start_bulk_receiving; |
389 | 3.20k | parser->stop_bulk_receiving_func = parser_stop_bulk_receiving; |
390 | 3.20k | parser->bulk_receiving_status_func = parser_bulk_receiving_status; |
391 | 3.20k | parser->buffered_bulk_packet_func = parser_buffered_bulk_packet; |
392 | | |
393 | 3.20k | for (uint32_t &cap : caps) { |
394 | 3.20k | cap = fdp->ConsumeIntegral<decltype(caps)::value_type>(); |
395 | 3.20k | } |
396 | | |
397 | 3.20k | const int init_flags = fdp->ConsumeIntegral<uint8_t>() & |
398 | 3.20k | (usbredirparser_fl_usb_host | usbredirparser_fl_no_hello); |
399 | | |
400 | 3.20k | usbredirparser_init(parser.get(), "fuzzer", caps.data(), caps.size(), |
401 | 3.20k | init_flags); |
402 | | |
403 | 3.20k | if (fdp->ConsumeBool() && try_unserialize(parser.get(), fdp.get()) != 0) { |
404 | 730 | goto out; |
405 | 730 | } |
406 | | |
407 | 235k | while (fdp->remaining_bytes() > 0 || |
408 | 235k | usbredirparser_has_data_to_write(parser.get())) { |
409 | 234k | if (fdp->ConsumeBool() && try_serialize(parser.get()) != 0) { |
410 | 0 | goto out; |
411 | 0 | } |
412 | | |
413 | 234k | if (fdp->remaining_bytes() > 0) { |
414 | 234k | ret = usbredirparser_do_read(parser.get()); |
415 | | |
416 | 234k | switch (ret) { |
417 | 232k | case usbredirparser_read_parse_error: |
418 | | // Keep reading |
419 | 232k | break; |
420 | 1.32k | default: |
421 | 1.32k | goto out; |
422 | 234k | } |
423 | | |
424 | 232k | if (fdp->ConsumeBool() && try_serialize(parser.get()) != 0) { |
425 | 0 | goto out; |
426 | 0 | } |
427 | 232k | } |
428 | | |
429 | 234k | while (usbredirparser_has_data_to_write(parser.get())) { |
430 | 1.41k | ret = usbredirparser_do_write(parser.get()); |
431 | 1.41k | if (ret < 0) { |
432 | 0 | goto out; |
433 | 0 | } |
434 | 1.41k | } |
435 | 232k | } |
436 | | |
437 | 3.20k | out: |
438 | 3.20k | parser.reset(); |
439 | | |
440 | 3.20k | return 0; |
441 | 2.47k | } |
442 | | |
443 | | /* vim: set sw=4 sts=4 et : */ |