/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 | } 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 | } |
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 | } |
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 | } |
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 | } |
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 |