Coverage Report

Created: 2026-01-17 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nanopb/tests/build/fuzztest/fuzztest.c
Line
Count
Source
1
/* Fuzz testing for the nanopb core.
2
 * Attempts to verify all the properties defined in the security model document.
3
 *
4
 * This program can run in three configurations:
5
 * - Standalone fuzzer, generating its own inputs and testing against them.
6
 * - Fuzzing target, reading input on stdin.
7
 * - LLVM libFuzzer target, taking input as a function argument.
8
 */
9
10
#include <pb_decode.h>
11
#include <pb_encode.h>
12
#include <stdio.h>
13
#include <stdlib.h>
14
#include <string.h>
15
#include <assert.h>
16
#include <malloc_wrappers.h>
17
#include "random_data.h"
18
#include "validation.h"
19
#include "flakystream.h"
20
#include "test_helpers.h"
21
#include "alltypes_static.pb.h"
22
#include "alltypes_pointer.pb.h"
23
#include "alltypes_callback.pb.h"
24
#include "alltypes_proto3_static.pb.h"
25
#include "alltypes_proto3_pointer.pb.h"
26
27
/* Longer buffer size allows hitting more branches, but lowers performance. */
28
#ifndef FUZZTEST_BUFSIZE
29
#define FUZZTEST_BUFSIZE 256*1024
30
#endif
31
#ifndef FUZZTEST_MAX_STANDALONE_BUFSIZE
32
#define FUZZTEST_MAX_STANDALONE_BUFSIZE 16384
33
#endif
34
static size_t g_bufsize = FUZZTEST_BUFSIZE;
35
36
/* Focusing on a single test case at a time improves fuzzing performance.
37
 * If no test case is specified, enable all tests.
38
 */
39
#if !defined(FUZZTEST_PROTO2_STATIC) && \
40
    !defined(FUZZTEST_PROTO3_STATIC) && \
41
    !defined(FUZZTEST_PROTO2_POINTER) && \
42
    !defined(FUZZTEST_PROTO3_POINTER) && \
43
    !defined(FUZZTEST_IO_ERRORS)
44
#define FUZZTEST_PROTO2_STATIC
45
#define FUZZTEST_PROTO3_STATIC
46
#define FUZZTEST_PROTO2_POINTER
47
#define FUZZTEST_PROTO3_POINTER
48
#define FUZZTEST_IO_ERRORS
49
#endif
50
51
static uint32_t xor32_checksum(const void *data, size_t len)
52
12.1k
{
53
12.1k
    const uint8_t *buf = (const uint8_t*)data;
54
12.1k
    uint32_t checksum = 1234;
55
158M
    for (; len > 0; len--)
56
158M
    {
57
158M
        checksum ^= checksum << 13;
58
158M
        checksum ^= checksum >> 17;
59
158M
        checksum ^= checksum << 5;
60
158M
        checksum += *buf++;
61
158M
    }
62
12.1k
    return checksum;
63
12.1k
}
64
65
static bool do_decode(const uint8_t *buffer, size_t msglen, size_t structsize, const pb_msgdesc_t *msgtype, unsigned flags, bool assert_success)
66
20.5k
{
67
20.5k
    bool status;
68
20.5k
    pb_istream_t stream;
69
20.5k
    size_t initial_alloc_count = get_alloc_count();
70
20.5k
    uint8_t *buf2 = malloc_with_check(g_bufsize); /* This is just to match the amount of memory allocations in do_roundtrips(). */
71
20.5k
    void *msg = malloc_with_check(structsize);
72
20.5k
    alltypes_static_TestExtension extmsg = alltypes_static_TestExtension_init_zero;
73
20.5k
    pb_extension_t ext = pb_extension_init_zero;
74
20.5k
    assert(msg);
75
76
20.5k
    memset(msg, 0, structsize);
77
20.5k
    ext.type = &alltypes_static_TestExtension_testextension;
78
20.5k
    ext.dest = &extmsg;
79
20.5k
    ext.next = NULL;
80
81
20.5k
    if (msgtype == alltypes_static_AllTypes_fields)
82
11.6k
    {
83
11.6k
        ((alltypes_static_AllTypes*)msg)->extensions = &ext;
84
11.6k
    }
85
8.97k
    else if (msgtype == alltypes_pointer_AllTypes_fields)
86
3.56k
    {
87
3.56k
        ((alltypes_pointer_AllTypes*)msg)->extensions = &ext;
88
3.56k
    }
89
90
20.5k
    stream = pb_istream_from_buffer(buffer, msglen);
91
20.5k
    status = pb_decode_ex(&stream, msgtype, msg, flags);
92
93
20.5k
    if (status)
94
6.41k
    {
95
6.41k
        validate_message(msg, structsize, msgtype);
96
6.41k
    }
97
98
20.5k
    if (assert_success)
99
0
    {
100
0
        if (!status) fprintf(stderr, "pb_decode: %s\n", PB_GET_ERROR(&stream));
101
0
        assert(status);
102
0
    }
103
104
20.5k
    if (status)
105
6.41k
    {
106
        /* On error return, pb_release() should be called automatically. */
107
6.41k
        pb_release(msgtype, msg);
108
6.41k
    }
109
110
20.5k
    free_with_check(msg);
111
20.5k
    free_with_check(buf2);
112
20.5k
    assert(get_alloc_count() == initial_alloc_count);
113
    
114
20.5k
    return status;
115
20.5k
}
116
117
static bool do_stream_decode(const uint8_t *buffer, size_t msglen, size_t fail_after, size_t structsize, const pb_msgdesc_t *msgtype, unsigned flags, bool assert_success)
118
19.2k
{
119
19.2k
    bool status;
120
19.2k
    flakystream_t stream;
121
19.2k
    size_t initial_alloc_count = get_alloc_count();
122
19.2k
    void *msg = malloc_with_check(structsize);
123
19.2k
    assert(msg);
124
125
19.2k
    memset(msg, 0, structsize);
126
19.2k
    flakystream_init(&stream, buffer, msglen, fail_after);
127
19.2k
    status = pb_decode_ex(&stream.stream, msgtype, msg, flags);
128
129
19.2k
    if (status)
130
6.41k
    {
131
6.41k
        validate_message(msg, structsize, msgtype);
132
6.41k
    }
133
134
19.2k
    if (assert_success)
135
6.29k
    {
136
6.29k
        if (!status) fprintf(stderr, "pb_decode: %s\n", PB_GET_ERROR(&stream.stream));
137
6.29k
        assert(status);
138
6.29k
    }
139
140
19.2k
    if (status)
141
6.41k
    {
142
        /* On error return, pb_release() should be called automatically. */
143
6.41k
        pb_release(msgtype, msg);
144
6.41k
    }
145
146
19.2k
    free_with_check(msg);
147
19.2k
    assert(get_alloc_count() == initial_alloc_count);
148
149
19.2k
    return status;
150
19.2k
}
151
152
static int g_sentinel;
153
154
static bool field_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
155
8.60k
{
156
8.60k
    assert(stream);
157
8.60k
    assert(field);
158
8.60k
    assert(*arg == &g_sentinel);
159
8.60k
    return pb_read(stream, NULL, stream->bytes_left);
160
8.60k
}
161
162
static bool submsg_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
163
50.9k
{
164
50.9k
    assert(stream);
165
50.9k
    assert(field);
166
50.9k
    assert(*arg == &g_sentinel);
167
50.9k
    return true;
168
50.9k
}
169
170
bool do_callback_decode(const uint8_t *buffer, size_t msglen, bool assert_success)
171
5.63k
{
172
5.63k
    bool status;
173
5.63k
    pb_istream_t stream;
174
5.63k
    size_t initial_alloc_count = get_alloc_count();
175
5.63k
    alltypes_callback_AllTypes *msg = malloc_with_check(sizeof(alltypes_callback_AllTypes));
176
5.63k
    assert(msg);
177
178
5.63k
    memset(msg, 0, sizeof(alltypes_callback_AllTypes));
179
5.63k
    stream = pb_istream_from_buffer(buffer, msglen);
180
181
5.63k
    msg->rep_int32.funcs.decode = &field_callback;
182
5.63k
    msg->rep_int32.arg = &g_sentinel;
183
5.63k
    msg->rep_string.funcs.decode = &field_callback;
184
5.63k
    msg->rep_string.arg = &g_sentinel;
185
5.63k
    msg->rep_farray.funcs.decode = &field_callback;
186
5.63k
    msg->rep_farray.arg = &g_sentinel;
187
5.63k
    msg->req_limits.int64_min.funcs.decode = &field_callback;
188
5.63k
    msg->req_limits.int64_min.arg = &g_sentinel;
189
5.63k
    msg->cb_oneof.funcs.decode = &submsg_callback;
190
5.63k
    msg->cb_oneof.arg = &g_sentinel;
191
192
5.63k
    status = pb_decode(&stream, alltypes_callback_AllTypes_fields, msg);
193
194
5.63k
    if (assert_success)
195
1.30k
    {
196
1.30k
        if (!status) fprintf(stderr, "pb_decode: %s\n", PB_GET_ERROR(&stream));
197
1.30k
        assert(status);
198
1.30k
    }
199
200
5.63k
    pb_release(alltypes_callback_AllTypes_fields, msg);
201
5.63k
    free_with_check(msg);
202
5.63k
    assert(get_alloc_count() == initial_alloc_count);
203
204
5.63k
    return status;
205
5.63k
}
206
207
/* Do a decode -> encode -> decode -> encode roundtrip */
208
void do_roundtrip(const uint8_t *buffer, size_t msglen, size_t structsize, const pb_msgdesc_t *msgtype)
209
6.29k
{
210
6.29k
    bool status;
211
6.29k
    uint32_t checksum2, checksum3;
212
6.29k
    size_t msglen2, msglen3;
213
6.29k
    uint8_t *buf2 = malloc_with_check(g_bufsize);
214
6.29k
    void *msg = malloc_with_check(structsize);
215
216
    /* For proto2 types, we also test extension fields */
217
6.29k
    alltypes_static_TestExtension extmsg = alltypes_static_TestExtension_init_zero;
218
6.29k
    pb_extension_t ext = pb_extension_init_zero;
219
6.29k
    pb_extension_t **ext_field = NULL;
220
6.29k
    ext.type = &alltypes_static_TestExtension_testextension;
221
6.29k
    ext.dest = &extmsg;
222
6.29k
    ext.next = NULL;
223
224
6.29k
    assert(buf2 && msg);
225
226
6.29k
    if (msgtype == alltypes_static_AllTypes_fields)
227
1.30k
    {
228
1.30k
        ext_field = &((alltypes_static_AllTypes*)msg)->extensions;
229
1.30k
    }
230
4.98k
    else if (msgtype == alltypes_pointer_AllTypes_fields)
231
1.52k
    {
232
1.52k
        ext_field = &((alltypes_pointer_AllTypes*)msg)->extensions;
233
1.52k
    }
234
    
235
    /* Decode and encode the input data.
236
     * This will bring it into canonical format.
237
     */
238
6.29k
    {
239
6.29k
        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
240
6.29k
        memset(msg, 0, structsize);
241
6.29k
        if (ext_field) *ext_field = &ext;
242
6.29k
        status = pb_decode(&stream, msgtype, msg);
243
6.29k
        if (!status) fprintf(stderr, "pb_decode: %s\n", PB_GET_ERROR(&stream));
244
6.29k
        assert(status);
245
246
6.29k
        validate_message(msg, structsize, msgtype);
247
6.29k
    }
248
    
249
6.29k
    {
250
6.29k
        pb_ostream_t stream = pb_ostream_from_buffer(buf2, g_bufsize);
251
6.29k
        status = pb_encode(&stream, msgtype, msg);
252
253
        /* Some messages expand when re-encoding and might no longer fit
254
         * in the buffer. */
255
6.29k
        if (!status && strcmp(PB_GET_ERROR(&stream), "stream full") != 0)
256
0
        {
257
0
            fprintf(stderr, "pb_encode: %s\n", PB_GET_ERROR(&stream));
258
0
            assert(status);
259
0
        }
260
261
6.29k
        msglen2 = stream.bytes_written;
262
6.29k
        checksum2 = xor32_checksum(buf2, msglen2);
263
6.29k
    }
264
    
265
6.29k
    pb_release(msgtype, msg);
266
267
    /* Then decode from canonical format and re-encode. Result should remain the same. */
268
6.29k
    if (status)
269
5.90k
    {
270
5.90k
        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
271
5.90k
        memset(msg, 0, structsize);
272
5.90k
        if (ext_field) *ext_field = &ext;
273
5.90k
        status = pb_decode(&stream, msgtype, msg);
274
5.90k
        if (!status) fprintf(stderr, "pb_decode: %s\n", PB_GET_ERROR(&stream));
275
5.90k
        assert(status);
276
277
5.90k
        validate_message(msg, structsize, msgtype);
278
5.90k
    }
279
    
280
6.29k
    if (status)
281
5.90k
    {
282
5.90k
        pb_ostream_t stream = pb_ostream_from_buffer(buf2, g_bufsize);
283
5.90k
        status = pb_encode(&stream, msgtype, msg);
284
5.90k
        if (!status) fprintf(stderr, "pb_encode: %s\n", PB_GET_ERROR(&stream));
285
5.90k
        assert(status);
286
5.90k
        msglen3 = stream.bytes_written;
287
5.90k
        checksum3 = xor32_checksum(buf2, msglen3);
288
289
5.90k
        assert(msglen2 == msglen3);
290
5.90k
        assert(checksum2 == checksum3);
291
5.90k
    }
292
    
293
6.29k
    pb_release(msgtype, msg);
294
6.29k
    free_with_check(msg);
295
6.29k
    free_with_check(buf2);
296
6.29k
}
297
298
/* Run all enabled test cases for a given input */
299
void do_roundtrips(const uint8_t *data, size_t size, bool expect_valid)
300
52.0k
{
301
52.0k
    size_t initial_alloc_count = get_alloc_count();
302
52.0k
    PB_UNUSED(expect_valid); /* Potentially unused depending on configuration */
303
304
#ifdef FUZZTEST_PROTO2_STATIC
305
11.9k
    if (do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, expect_valid))
306
6.29k
    {
307
6.29k
        do_roundtrip(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields);
308
6.29k
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, true);
309
6.29k
        do_callback_decode(data, size, true);
310
6.29k
    }
311
#endif
312
313
#ifdef FUZZTEST_PROTO3_STATIC
314
11.9k
    if (do_decode(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, expect_valid))
315
6.29k
    {
316
6.29k
        do_roundtrip(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields);
317
6.29k
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, true);
318
6.29k
    }
319
#endif
320
321
#ifdef FUZZTEST_PROTO2_POINTER
322
11.9k
    if (do_decode(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, expect_valid))
323
6.29k
    {
324
6.29k
        do_roundtrip(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields);
325
6.29k
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, true);
326
6.29k
    }
327
#endif
328
329
#ifdef FUZZTEST_PROTO3_POINTER
330
11.9k
    if (do_decode(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, expect_valid))
331
6.29k
    {
332
6.29k
        do_roundtrip(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields);
333
6.29k
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, true);
334
6.29k
    }
335
#endif
336
337
#ifdef FUZZTEST_IO_ERRORS
338
    {
339
        size_t orig_max_alloc_bytes = get_max_alloc_bytes();
340
        /* Test decoding when error conditions occur.
341
         * The decoding will end either when running out of memory or when stream returns IO error.
342
         * Testing proto2 is enough for good coverage here, as it has a superset of the field types of proto3.
343
         */
344
        set_max_alloc_bytes(get_alloc_bytes() + 4096);
345
4.32k
        do_stream_decode(data, size, size - 16, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, false);
346
4.32k
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, false);
347
4.32k
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, PB_DECODE_DELIMITED, false);
348
        set_max_alloc_bytes(orig_max_alloc_bytes);
349
    }
350
351
    /* Test pb_decode_ex() modes */
352
4.32k
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NOINIT | PB_DECODE_DELIMITED, false);
353
4.32k
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NULLTERMINATED, false);
354
355
    /* Test callbacks also when message is not valid */
356
    do_callback_decode(data, size, false);
357
#endif
358
359
52.0k
    assert(get_alloc_count() == initial_alloc_count);
360
52.0k
}
do_roundtrips
Line
Count
Source
300
11.9k
{
301
11.9k
    size_t initial_alloc_count = get_alloc_count();
302
11.9k
    PB_UNUSED(expect_valid); /* Potentially unused depending on configuration */
303
304
11.9k
#ifdef FUZZTEST_PROTO2_STATIC
305
11.9k
    if (do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, expect_valid))
306
6.29k
    {
307
6.29k
        do_roundtrip(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields);
308
6.29k
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, true);
309
6.29k
        do_callback_decode(data, size, true);
310
6.29k
    }
311
11.9k
#endif
312
313
#ifdef FUZZTEST_PROTO3_STATIC
314
    if (do_decode(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, expect_valid))
315
    {
316
        do_roundtrip(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields);
317
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, true);
318
    }
319
#endif
320
321
#ifdef FUZZTEST_PROTO2_POINTER
322
    if (do_decode(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, expect_valid))
323
    {
324
        do_roundtrip(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields);
325
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, true);
326
    }
327
#endif
328
329
#ifdef FUZZTEST_PROTO3_POINTER
330
    if (do_decode(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, expect_valid))
331
    {
332
        do_roundtrip(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields);
333
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, true);
334
    }
335
#endif
336
337
#ifdef FUZZTEST_IO_ERRORS
338
    {
339
        size_t orig_max_alloc_bytes = get_max_alloc_bytes();
340
        /* Test decoding when error conditions occur.
341
         * The decoding will end either when running out of memory or when stream returns IO error.
342
         * Testing proto2 is enough for good coverage here, as it has a superset of the field types of proto3.
343
         */
344
        set_max_alloc_bytes(get_alloc_bytes() + 4096);
345
        do_stream_decode(data, size, size - 16, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, false);
346
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, false);
347
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, PB_DECODE_DELIMITED, false);
348
        set_max_alloc_bytes(orig_max_alloc_bytes);
349
    }
350
351
    /* Test pb_decode_ex() modes */
352
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NOINIT | PB_DECODE_DELIMITED, false);
353
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NULLTERMINATED, false);
354
355
    /* Test callbacks also when message is not valid */
356
    do_callback_decode(data, size, false);
357
#endif
358
359
    assert(get_alloc_count() == initial_alloc_count);
360
11.9k
}
do_roundtrips
Line
Count
Source
300
11.9k
{
301
11.9k
    size_t initial_alloc_count = get_alloc_count();
302
11.9k
    PB_UNUSED(expect_valid); /* Potentially unused depending on configuration */
303
304
#ifdef FUZZTEST_PROTO2_STATIC
305
    if (do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, expect_valid))
306
    {
307
        do_roundtrip(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields);
308
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, true);
309
        do_callback_decode(data, size, true);
310
    }
311
#endif
312
313
#ifdef FUZZTEST_PROTO3_STATIC
314
    if (do_decode(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, expect_valid))
315
    {
316
        do_roundtrip(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields);
317
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, true);
318
    }
319
#endif
320
321
11.9k
#ifdef FUZZTEST_PROTO2_POINTER
322
11.9k
    if (do_decode(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, expect_valid))
323
6.29k
    {
324
6.29k
        do_roundtrip(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields);
325
6.29k
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, true);
326
6.29k
    }
327
11.9k
#endif
328
329
#ifdef FUZZTEST_PROTO3_POINTER
330
    if (do_decode(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, expect_valid))
331
    {
332
        do_roundtrip(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields);
333
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, true);
334
    }
335
#endif
336
337
#ifdef FUZZTEST_IO_ERRORS
338
    {
339
        size_t orig_max_alloc_bytes = get_max_alloc_bytes();
340
        /* Test decoding when error conditions occur.
341
         * The decoding will end either when running out of memory or when stream returns IO error.
342
         * Testing proto2 is enough for good coverage here, as it has a superset of the field types of proto3.
343
         */
344
        set_max_alloc_bytes(get_alloc_bytes() + 4096);
345
        do_stream_decode(data, size, size - 16, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, false);
346
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, false);
347
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, PB_DECODE_DELIMITED, false);
348
        set_max_alloc_bytes(orig_max_alloc_bytes);
349
    }
350
351
    /* Test pb_decode_ex() modes */
352
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NOINIT | PB_DECODE_DELIMITED, false);
353
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NULLTERMINATED, false);
354
355
    /* Test callbacks also when message is not valid */
356
    do_callback_decode(data, size, false);
357
#endif
358
359
    assert(get_alloc_count() == initial_alloc_count);
360
11.9k
}
do_roundtrips
Line
Count
Source
300
11.9k
{
301
11.9k
    size_t initial_alloc_count = get_alloc_count();
302
11.9k
    PB_UNUSED(expect_valid); /* Potentially unused depending on configuration */
303
304
#ifdef FUZZTEST_PROTO2_STATIC
305
    if (do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, expect_valid))
306
    {
307
        do_roundtrip(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields);
308
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, true);
309
        do_callback_decode(data, size, true);
310
    }
311
#endif
312
313
#ifdef FUZZTEST_PROTO3_STATIC
314
    if (do_decode(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, expect_valid))
315
    {
316
        do_roundtrip(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields);
317
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, true);
318
    }
319
#endif
320
321
#ifdef FUZZTEST_PROTO2_POINTER
322
    if (do_decode(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, expect_valid))
323
    {
324
        do_roundtrip(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields);
325
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, true);
326
    }
327
#endif
328
329
11.9k
#ifdef FUZZTEST_PROTO3_POINTER
330
11.9k
    if (do_decode(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, expect_valid))
331
6.29k
    {
332
6.29k
        do_roundtrip(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields);
333
6.29k
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, true);
334
6.29k
    }
335
11.9k
#endif
336
337
#ifdef FUZZTEST_IO_ERRORS
338
    {
339
        size_t orig_max_alloc_bytes = get_max_alloc_bytes();
340
        /* Test decoding when error conditions occur.
341
         * The decoding will end either when running out of memory or when stream returns IO error.
342
         * Testing proto2 is enough for good coverage here, as it has a superset of the field types of proto3.
343
         */
344
        set_max_alloc_bytes(get_alloc_bytes() + 4096);
345
        do_stream_decode(data, size, size - 16, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, false);
346
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, false);
347
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, PB_DECODE_DELIMITED, false);
348
        set_max_alloc_bytes(orig_max_alloc_bytes);
349
    }
350
351
    /* Test pb_decode_ex() modes */
352
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NOINIT | PB_DECODE_DELIMITED, false);
353
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NULLTERMINATED, false);
354
355
    /* Test callbacks also when message is not valid */
356
    do_callback_decode(data, size, false);
357
#endif
358
359
    assert(get_alloc_count() == initial_alloc_count);
360
11.9k
}
do_roundtrips
Line
Count
Source
300
11.9k
{
301
11.9k
    size_t initial_alloc_count = get_alloc_count();
302
11.9k
    PB_UNUSED(expect_valid); /* Potentially unused depending on configuration */
303
304
#ifdef FUZZTEST_PROTO2_STATIC
305
    if (do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, expect_valid))
306
    {
307
        do_roundtrip(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields);
308
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, true);
309
        do_callback_decode(data, size, true);
310
    }
311
#endif
312
313
11.9k
#ifdef FUZZTEST_PROTO3_STATIC
314
11.9k
    if (do_decode(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, expect_valid))
315
6.29k
    {
316
6.29k
        do_roundtrip(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields);
317
6.29k
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, true);
318
6.29k
    }
319
11.9k
#endif
320
321
#ifdef FUZZTEST_PROTO2_POINTER
322
    if (do_decode(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, expect_valid))
323
    {
324
        do_roundtrip(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields);
325
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, true);
326
    }
327
#endif
328
329
#ifdef FUZZTEST_PROTO3_POINTER
330
    if (do_decode(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, expect_valid))
331
    {
332
        do_roundtrip(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields);
333
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, true);
334
    }
335
#endif
336
337
#ifdef FUZZTEST_IO_ERRORS
338
    {
339
        size_t orig_max_alloc_bytes = get_max_alloc_bytes();
340
        /* Test decoding when error conditions occur.
341
         * The decoding will end either when running out of memory or when stream returns IO error.
342
         * Testing proto2 is enough for good coverage here, as it has a superset of the field types of proto3.
343
         */
344
        set_max_alloc_bytes(get_alloc_bytes() + 4096);
345
        do_stream_decode(data, size, size - 16, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, false);
346
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, false);
347
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, PB_DECODE_DELIMITED, false);
348
        set_max_alloc_bytes(orig_max_alloc_bytes);
349
    }
350
351
    /* Test pb_decode_ex() modes */
352
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NOINIT | PB_DECODE_DELIMITED, false);
353
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NULLTERMINATED, false);
354
355
    /* Test callbacks also when message is not valid */
356
    do_callback_decode(data, size, false);
357
#endif
358
359
    assert(get_alloc_count() == initial_alloc_count);
360
11.9k
}
do_roundtrips
Line
Count
Source
300
4.32k
{
301
4.32k
    size_t initial_alloc_count = get_alloc_count();
302
4.32k
    PB_UNUSED(expect_valid); /* Potentially unused depending on configuration */
303
304
#ifdef FUZZTEST_PROTO2_STATIC
305
    if (do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, expect_valid))
306
    {
307
        do_roundtrip(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields);
308
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, true);
309
        do_callback_decode(data, size, true);
310
    }
311
#endif
312
313
#ifdef FUZZTEST_PROTO3_STATIC
314
    if (do_decode(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, expect_valid))
315
    {
316
        do_roundtrip(data, size, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields);
317
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_static_AllTypes), alltypes_proto3_static_AllTypes_fields, 0, true);
318
    }
319
#endif
320
321
#ifdef FUZZTEST_PROTO2_POINTER
322
    if (do_decode(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, expect_valid))
323
    {
324
        do_roundtrip(data, size, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields);
325
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, true);
326
    }
327
#endif
328
329
#ifdef FUZZTEST_PROTO3_POINTER
330
    if (do_decode(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, expect_valid))
331
    {
332
        do_roundtrip(data, size, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields);
333
        do_stream_decode(data, size, SIZE_MAX, sizeof(alltypes_proto3_pointer_AllTypes), alltypes_proto3_pointer_AllTypes_fields, 0, true);
334
    }
335
#endif
336
337
4.32k
#ifdef FUZZTEST_IO_ERRORS
338
4.32k
    {
339
4.32k
        size_t orig_max_alloc_bytes = get_max_alloc_bytes();
340
        /* Test decoding when error conditions occur.
341
         * The decoding will end either when running out of memory or when stream returns IO error.
342
         * Testing proto2 is enough for good coverage here, as it has a superset of the field types of proto3.
343
         */
344
4.32k
        set_max_alloc_bytes(get_alloc_bytes() + 4096);
345
4.32k
        do_stream_decode(data, size, size - 16, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, 0, false);
346
4.32k
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, 0, false);
347
4.32k
        do_stream_decode(data, size, size - 16, sizeof(alltypes_pointer_AllTypes), alltypes_pointer_AllTypes_fields, PB_DECODE_DELIMITED, false);
348
4.32k
        set_max_alloc_bytes(orig_max_alloc_bytes);
349
4.32k
    }
350
351
    /* Test pb_decode_ex() modes */
352
4.32k
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NOINIT | PB_DECODE_DELIMITED, false);
353
4.32k
    do_decode(data, size, sizeof(alltypes_static_AllTypes), alltypes_static_AllTypes_fields, PB_DECODE_NULLTERMINATED, false);
354
355
    /* Test callbacks also when message is not valid */
356
4.32k
    do_callback_decode(data, size, false);
357
4.32k
#endif
358
359
    assert(get_alloc_count() == initial_alloc_count);
360
4.32k
}
361
362
/* Fuzzer stub for Google OSSFuzz integration */
363
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
364
16.2k
{
365
16.2k
    if (size > g_bufsize)
366
20
        return 0;
367
368
16.2k
    do_roundtrips(data, size, false);
369
370
16.2k
    return 0;
371
16.2k
}
372
373
#ifndef LLVMFUZZER
374
375
static bool generate_base_message(uint8_t *buffer, size_t *msglen)
376
{
377
    pb_ostream_t stream;
378
    bool status;
379
    static const alltypes_static_AllTypes initval = alltypes_static_AllTypes_init_default;
380
381
    /* Allocate a message and fill it with defaults */
382
    alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes));
383
    memcpy(msg, &initval, sizeof(initval));
384
385
    /* Apply randomness to the data before encoding */
386
    while (rand_int(0, 7))
387
        rand_mess((uint8_t*)msg, sizeof(alltypes_static_AllTypes));
388
389
    msg->extensions = NULL;
390
391
    stream = pb_ostream_from_buffer(buffer, g_bufsize);
392
    status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg);
393
    assert(stream.bytes_written <= g_bufsize);
394
    assert(stream.bytes_written <= alltypes_static_AllTypes_size);
395
    
396
    *msglen = stream.bytes_written;
397
    pb_release(alltypes_static_AllTypes_fields, msg);
398
    free_with_check(msg);
399
    
400
    return status;
401
}
402
403
/* Stand-alone fuzzer iteration, generates random data itself */
404
static void run_iteration()
405
{
406
    uint8_t *buffer = malloc_with_check(g_bufsize);
407
    size_t msglen;
408
    
409
    /* Fill the whole buffer with noise, to prepare for length modifications */
410
    rand_fill(buffer, g_bufsize);
411
412
    if (generate_base_message(buffer, &msglen))
413
    {
414
        rand_protobuf_noise(buffer, g_bufsize, &msglen);
415
    
416
        /* At this point the message should always be valid */
417
        do_roundtrips(buffer, msglen, true);
418
        
419
        /* Apply randomness to the encoded data */
420
        while (rand_bool())
421
            rand_mess(buffer, g_bufsize);
422
        
423
        /* Apply randomness to encoded data length */
424
        if (rand_bool())
425
            msglen = rand_int(0, g_bufsize);
426
        
427
        /* In this step the message may be valid or invalid */
428
        do_roundtrips(buffer, msglen, false);
429
    }
430
    
431
    free_with_check(buffer);
432
    assert(get_alloc_count() == 0);
433
}
434
435
int main(int argc, char **argv)
436
{
437
    int i;
438
    int iterations;
439
440
    if (argc >= 2)
441
    {
442
        /* Run in stand-alone mode */
443
        if (g_bufsize > FUZZTEST_MAX_STANDALONE_BUFSIZE)
444
            g_bufsize = FUZZTEST_MAX_STANDALONE_BUFSIZE;
445
446
        random_set_seed(strtoul(argv[1], NULL, 0));
447
        iterations = (argc >= 3) ? atol(argv[2]) : 10000;
448
449
        for (i = 0; i < iterations; i++)
450
        {
451
            printf("Iteration %d/%d, seed %lu\n", i, iterations, (unsigned long)random_get_seed());
452
            run_iteration();
453
        }
454
    }
455
    else
456
    {
457
        /* Run as a stub for afl-fuzz and similar */
458
        uint8_t *buffer;
459
        size_t msglen;
460
461
        buffer = malloc_with_check(g_bufsize);
462
463
        SET_BINARY_MODE(stdin);
464
        msglen = fread(buffer, 1, g_bufsize, stdin);
465
        LLVMFuzzerTestOneInput(buffer, msglen);
466
467
        if (!feof(stdin))
468
        {
469
            /* Read any leftover input data if our buffer is smaller than
470
             * message size. */
471
            fprintf(stderr, "Warning: input message too long\n");
472
            while (fread(buffer, 1, g_bufsize, stdin) == g_bufsize);
473
        }
474
475
        free_with_check(buffer);
476
    }
477
    
478
    return 0;
479
}
480
#endif