Coverage Report

Created: 2026-02-26 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spice-usbredir/fuzzing/usbredirparserfuzz.cc
Line
Count
Source
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.16k
    void operator()(struct usbredirparser *p) {
40
3.16k
        usbredirparser_destroy(p);
41
3.16k
    }
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
752k
{
49
752k
}
50
51
int wobbly_read_write_count(int count)
52
5.31M
{
53
5.31M
    if (count > (1024 * 1024)) {
54
165
        return count;
55
165
    }
56
57
5.31M
    return std::min(count, fdp->ConsumeIntegralInRange(1, 4 * count));
58
5.31M
}
59
60
int parser_read(void *priv, uint8_t *data, int count)
61
2.52M
{
62
    // Simulate short reads
63
2.52M
    return fdp->ConsumeData(data, wobbly_read_write_count(count));
64
2.52M
}
65
66
// Read over complete input buffer to detect buffer overflows
67
void read_all(const void *data, size_t count)
68
2.79M
{
69
#ifdef __cpp_lib_smart_ptr_for_overwrite
70
    const auto buf = std::make_unique_for_overwrite<uint8_t[]>(count);
71
#else
72
2.79M
    const auto buf = std::make_unique<uint8_t[]>(count);
73
2.79M
#endif
74
75
2.79M
    memcpy(buf.get(), data, count);
76
2.79M
}
77
78
template <typename T, typename = std::enable_if_t<std::is_class<T>::value>>
79
void read_all(const T *ptr)
80
3.22k
{
81
3.22k
    read_all(ptr, sizeof(T));
82
3.22k
}
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_control_packet_header, void>(usb_redir_control_packet_header const*)
Line
Count
Source
80
569
{
81
569
    read_all(ptr, sizeof(T));
82
569
}
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
615
{
81
615
    read_all(ptr, sizeof(T));
82
615
}
usbredirparserfuzz.cc:void (anonymous namespace)::read_all<usb_redir_interrupt_packet_header, void>(usb_redir_interrupt_packet_header const*)
Line
Count
Source
80
455
{
81
455
    read_all(ptr, sizeof(T));
82
455
}
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
212
{
81
212
    read_all(ptr, sizeof(T));
82
212
}
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
218
{
81
218
    read_all(ptr, sizeof(T));
82
218
}
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
417
{
81
417
    read_all(ptr, sizeof(T));
82
417
}
83
84
int parser_write(void *priv, uint8_t *data, int count)
85
2.79M
{
86
    // Simulate short writes
87
2.79M
    count = wobbly_read_write_count(count);
88
89
2.79M
    read_all(data, count);
90
91
2.79M
    return count;
92
2.79M
}
93
94
void parser_device_connect(void *priv,
95
    struct usb_redir_device_connect_header *device_connect)
96
194
{
97
194
}
98
99
void parser_device_disconnect(void *priv)
100
155k
{
101
155k
}
102
103
void parser_reset(void *priv)
104
240
{
105
240
}
106
107
void parser_interface_info(void *priv,
108
    struct usb_redir_interface_info_header *info)
109
201
{
110
201
}
111
112
void parser_ep_info(void *priv,
113
    struct usb_redir_ep_info_header *ep_info)
114
219
{
115
219
}
116
117
void parser_set_configuration(void *priv, uint64_t id,
118
    struct usb_redir_set_configuration_header *set_configuration)
119
381
{
120
381
}
121
122
void parser_get_configuration(void *priv, uint64_t id)
123
535
{
124
535
}
125
126
void parser_configuration_status(void *priv, uint64_t id,
127
    struct usb_redir_configuration_status_header *config_status)
128
244
{
129
244
}
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
407
{
139
407
}
140
141
void parser_alt_setting_status(void *priv, uint64_t id,
142
    struct usb_redir_alt_setting_status_header *alt_setting_status)
143
196
{
144
196
}
145
146
void parser_start_iso_stream(void *priv, uint64_t id,
147
    struct usb_redir_start_iso_stream_header *start_iso_stream)
148
207
{
149
207
}
150
151
void parser_stop_iso_stream(void *priv, uint64_t id,
152
    struct usb_redir_stop_iso_stream_header *stop_iso_stream)
153
355
{
154
355
}
155
156
void parser_iso_stream_status(void *priv, uint64_t id,
157
    struct usb_redir_iso_stream_status_header *iso_stream_status)
158
276
{
159
276
}
160
161
void parser_start_interrupt_receiving(void *priv, uint64_t id,
162
    struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving)
163
504
{
164
504
}
165
166
void parser_stop_interrupt_receiving(void *priv, uint64_t id,
167
    struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving)
168
471
{
169
471
}
170
171
void parser_interrupt_receiving_status(void *priv, uint64_t id,
172
    struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status)
173
322
{
174
322
}
175
176
void parser_alloc_bulk_streams(void *priv, uint64_t id,
177
    struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams)
178
228
{
179
228
}
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
195
{
189
195
}
190
191
void parser_cancel_data_packet(void *priv, uint64_t id)
192
241
{
193
241
}
194
195
void parser_filter_reject(void *priv)
196
199
{
197
199
}
198
199
void parser_filter_filter(void *priv,
200
    struct usbredirfilter_rule *rules, int rules_count)
201
796
{
202
796
    usbredirfilter_free(rules);
203
796
}
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
569
{
209
569
    read_all(control_packet);
210
569
    read_all(data, data_len);
211
569
    usbredirparser_free_packet_data(parser.get(), data);
212
569
}
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
615
{
227
615
    read_all(iso_packet);
228
615
    read_all(data, data_len);
229
615
    usbredirparser_free_packet_data(parser.get(), data);
230
615
}
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
455
{
236
455
    read_all(interrupt_packet);
237
455
    read_all(data, data_len);
238
455
    usbredirparser_free_packet_data(parser.get(), data);
239
455
}
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
417
{
245
417
    read_all(buffered_bulk_header);
246
417
    read_all(data, data_len);
247
417
    usbredirparser_free_packet_data(parser.get(), data);
248
417
}
249
250
void *parser_alloc_lock()
251
3.16k
{
252
3.16k
    return nullptr;
253
3.16k
}
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
79
{
269
79
}
270
271
void parser_device_disconnect_ack(void *priv)
272
294
{
273
294
}
274
275
void parser_start_bulk_receiving(void *priv, uint64_t id,
276
    struct usb_redir_start_bulk_receiving_header *start_bulk_receiving)
277
212
{
278
212
    read_all(start_bulk_receiving);
279
212
}
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
218
{
290
218
    read_all(bulk_receiving_status);
291
218
}
292
293
int try_unserialize(struct usbredirparser *parser, FuzzedDataProvider *fdp)
294
969
{
295
969
    std::vector<uint8_t> state;
296
969
    size_t len = fdp->ConsumeIntegralInRange<size_t>(1, 64 * 1024);
297
298
969
    state.reserve(len);
299
300
969
    if (len >= 4) {
301
938
        const uint32_t magic = USBREDIRPARSER_SERIALIZE_MAGIC;
302
938
        assert(state.empty());
303
938
        state.resize(sizeof(magic));
304
938
        memcpy(state.data(), &magic, sizeof(magic));
305
306
938
        len -= 4;
307
938
    }
308
309
969
    if (len > 0) {
310
968
        const std::vector<uint8_t> payload{fdp->ConsumeBytes<uint8_t>(len)};
311
312
968
        state.insert(state.end(), payload.cbegin(), payload.cend());
313
968
    }
314
315
969
    if (state.empty()) {
316
3
        return 0;
317
3
    }
318
319
966
    state.shrink_to_fit();
320
321
966
    return usbredirparser_unserialize(parser, &state[0], state.size());
322
969
}
323
324
int try_serialize(struct usbredirparser *parser)
325
37.1k
{
326
37.1k
    uint8_t *state = nullptr;
327
37.1k
    int len = 0;
328
37.1k
    int ret;
329
330
37.1k
    ret = usbredirparser_serialize(parser, &state, &len);
331
332
37.1k
    if (ret == 0) {
333
37.1k
        free(state);
334
37.1k
    }
335
336
37.1k
    return ret;
337
37.1k
}
338
}  // namespace
339
340
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
341
3.16k
{
342
3.16k
    std::array<uint32_t, USB_REDIR_CAPS_SIZE> caps = {0};
343
3.16k
    int ret;
344
345
3.16k
    fdp = std::make_unique<FuzzedDataProvider>(data, size);
346
347
3.16k
    parser.reset(usbredirparser_create());
348
3.16k
    if (parser == nullptr) {
349
0
        return 1;
350
0
    }
351
352
3.16k
    parser->log_func = parser_log;
353
3.16k
    parser->read_func = parser_read;
354
3.16k
    parser->write_func = parser_write;
355
3.16k
    parser->device_connect_func = parser_device_connect;
356
3.16k
    parser->device_disconnect_func = parser_device_disconnect;
357
3.16k
    parser->reset_func = parser_reset;
358
3.16k
    parser->interface_info_func = parser_interface_info;
359
3.16k
    parser->ep_info_func = parser_ep_info;
360
3.16k
    parser->set_configuration_func = parser_set_configuration;
361
3.16k
    parser->get_configuration_func = parser_get_configuration;
362
3.16k
    parser->configuration_status_func = parser_configuration_status;
363
3.16k
    parser->set_alt_setting_func = parser_set_alt_setting;
364
3.16k
    parser->get_alt_setting_func = parser_get_alt_setting;
365
3.16k
    parser->alt_setting_status_func = parser_alt_setting_status;
366
3.16k
    parser->start_iso_stream_func = parser_start_iso_stream;
367
3.16k
    parser->stop_iso_stream_func = parser_stop_iso_stream;
368
3.16k
    parser->iso_stream_status_func = parser_iso_stream_status;
369
3.16k
    parser->start_interrupt_receiving_func = parser_start_interrupt_receiving;
370
3.16k
    parser->stop_interrupt_receiving_func = parser_stop_interrupt_receiving;
371
3.16k
    parser->interrupt_receiving_status_func = parser_interrupt_receiving_status;
372
3.16k
    parser->alloc_bulk_streams_func = parser_alloc_bulk_streams;
373
3.16k
    parser->free_bulk_streams_func = parser_free_bulk_streams;
374
3.16k
    parser->bulk_streams_status_func = parser_bulk_streams_status;
375
3.16k
    parser->cancel_data_packet_func = parser_cancel_data_packet;
376
3.16k
    parser->control_packet_func = parser_control_packet;
377
3.16k
    parser->bulk_packet_func = parser_bulk_packet;
378
3.16k
    parser->iso_packet_func = parser_iso_packet;
379
3.16k
    parser->interrupt_packet_func = parser_interrupt_packet;
380
3.16k
    parser->alloc_lock_func = parser_alloc_lock;
381
3.16k
    parser->lock_func = parser_lock;
382
3.16k
    parser->unlock_func = parser_unlock;
383
3.16k
    parser->free_lock_func = parser_free_lock;
384
3.16k
    parser->hello_func = parser_hello;
385
3.16k
    parser->filter_reject_func = parser_filter_reject;
386
3.16k
    parser->filter_filter_func = parser_filter_filter;
387
3.16k
    parser->device_disconnect_ack_func = parser_device_disconnect_ack;
388
3.16k
    parser->start_bulk_receiving_func = parser_start_bulk_receiving;
389
3.16k
    parser->stop_bulk_receiving_func = parser_stop_bulk_receiving;
390
3.16k
    parser->bulk_receiving_status_func = parser_bulk_receiving_status;
391
3.16k
    parser->buffered_bulk_packet_func = parser_buffered_bulk_packet;
392
393
3.16k
    for (uint32_t &cap : caps) {
394
3.16k
        cap = fdp->ConsumeIntegral<decltype(caps)::value_type>();
395
3.16k
    }
396
397
3.16k
    const int init_flags = fdp->ConsumeIntegral<uint8_t>() &
398
3.16k
        (usbredirparser_fl_usb_host | usbredirparser_fl_no_hello);
399
400
3.16k
    usbredirparser_init(parser.get(), "fuzzer", caps.data(), caps.size(),
401
3.16k
                        init_flags);
402
403
3.16k
    if (fdp->ConsumeBool() && try_unserialize(parser.get(), fdp.get()) != 0) {
404
687
        goto out;
405
687
    }
406
407
163k
    while (fdp->remaining_bytes() > 0 ||
408
162k
           usbredirparser_has_data_to_write(parser.get())) {
409
162k
        if (fdp->ConsumeBool() && try_serialize(parser.get()) != 0) {
410
0
            goto out;
411
0
        }
412
413
162k
        if (fdp->remaining_bytes() > 0) {
414
162k
            ret = usbredirparser_do_read(parser.get());
415
416
162k
            switch (ret) {
417
161k
            case usbredirparser_read_parse_error:
418
                // Keep reading
419
161k
                break;
420
1.37k
            default:
421
1.37k
                goto out;
422
162k
            }
423
424
161k
            if (fdp->ConsumeBool() && try_serialize(parser.get()) != 0) {
425
0
                goto out;
426
0
            }
427
161k
        }
428
429
162k
        while (usbredirparser_has_data_to_write(parser.get())) {
430
1.30k
            ret = usbredirparser_do_write(parser.get());
431
1.30k
            if (ret < 0) {
432
0
                goto out;
433
0
            }
434
1.30k
        }
435
161k
    }
436
437
3.16k
out:
438
3.16k
    parser.reset();
439
440
3.16k
    return 0;
441
2.48k
}
442
443
/* vim: set sw=4 sts=4 et : */