diff options
Diffstat (limited to 'fuzz/fuzz_bio.c')
-rw-r--r-- | fuzz/fuzz_bio.c | 335 |
1 files changed, 175 insertions, 160 deletions
diff --git a/fuzz/fuzz_bio.c b/fuzz/fuzz_bio.c index 05f6ce3..5051a34 100644 --- a/fuzz/fuzz_bio.c +++ b/fuzz/fuzz_bio.c | |||
@@ -19,27 +19,17 @@ | |||
19 | 19 | ||
20 | #include "../openbsd-compat/openbsd-compat.h" | 20 | #include "../openbsd-compat/openbsd-compat.h" |
21 | 21 | ||
22 | #define TAG_PIN 0x01 | ||
23 | #define TAG_NAME 0x02 | ||
24 | #define TAG_SEED 0x03 | ||
25 | #define TAG_ID 0x04 | ||
26 | #define TAG_INFO_WIRE_DATA 0x05 | ||
27 | #define TAG_ENROLL_WIRE_DATA 0x06 | ||
28 | #define TAG_LIST_WIRE_DATA 0x07 | ||
29 | #define TAG_SET_NAME_WIRE_DATA 0x08 | ||
30 | #define TAG_REMOVE_WIRE_DATA 0x09 | ||
31 | |||
32 | /* Parameter set defining a FIDO2 credential management operation. */ | 22 | /* Parameter set defining a FIDO2 credential management operation. */ |
33 | struct param { | 23 | struct param { |
34 | char pin[MAXSTR]; | 24 | char pin[MAXSTR]; |
35 | char name[MAXSTR]; | 25 | char name[MAXSTR]; |
36 | int seed; | 26 | int seed; |
37 | struct blob id; | 27 | struct blob id; |
38 | struct blob info_wire_data; | 28 | struct blob info_wire_data; |
39 | struct blob enroll_wire_data; | 29 | struct blob enroll_wire_data; |
40 | struct blob list_wire_data; | 30 | struct blob list_wire_data; |
41 | struct blob set_name_wire_data; | 31 | struct blob set_name_wire_data; |
42 | struct blob remove_wire_data; | 32 | struct blob remove_wire_data; |
43 | }; | 33 | }; |
44 | 34 | ||
45 | /* | 35 | /* |
@@ -100,58 +90,141 @@ static const uint8_t dummy_remove_wire_data[] = { | |||
100 | WIREDATA_CTAP_CBOR_STATUS, | 90 | WIREDATA_CTAP_CBOR_STATUS, |
101 | }; | 91 | }; |
102 | 92 | ||
103 | int LLVMFuzzerTestOneInput(const uint8_t *, size_t); | 93 | struct param * |
104 | size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); | 94 | unpack(const uint8_t *ptr, size_t len) |
105 | |||
106 | static int | ||
107 | unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN | ||
108 | { | 95 | { |
109 | uint8_t **pp = (void *)&ptr; | 96 | cbor_item_t *item = NULL, **v; |
110 | 97 | struct cbor_load_result cbor; | |
111 | if (unpack_string(TAG_PIN, pp, &len, p->pin) < 0 || | 98 | struct param *p; |
112 | unpack_string(TAG_NAME, pp, &len, p->name) < 0 || | 99 | int ok = -1; |
113 | unpack_int(TAG_SEED, pp, &len, &p->seed) < 0 || | 100 | |
114 | unpack_blob(TAG_ID, pp, &len, &p->id) < 0 || | 101 | if ((p = calloc(1, sizeof(*p))) == NULL || |
115 | unpack_blob(TAG_INFO_WIRE_DATA, pp, &len, &p->info_wire_data) < 0 || | 102 | (item = cbor_load(ptr, len, &cbor)) == NULL || |
116 | unpack_blob(TAG_ENROLL_WIRE_DATA, pp, &len, &p->enroll_wire_data) < 0 || | 103 | cbor.read != len || |
117 | unpack_blob(TAG_LIST_WIRE_DATA, pp, &len, &p->list_wire_data) < 0 || | 104 | cbor_isa_array(item) == false || |
118 | unpack_blob(TAG_SET_NAME_WIRE_DATA, pp, &len, &p->set_name_wire_data) < 0 || | 105 | cbor_array_is_definite(item) == false || |
119 | unpack_blob(TAG_REMOVE_WIRE_DATA, pp, &len, &p->remove_wire_data) < 0) | 106 | cbor_array_size(item) != 9 || |
120 | return (-1); | 107 | (v = cbor_array_handle(item)) == NULL) |
121 | 108 | goto fail; | |
122 | return (0); | 109 | |
110 | if (unpack_int(v[0], &p->seed) < 0 || | ||
111 | unpack_string(v[1], p->pin) < 0 || | ||
112 | unpack_string(v[2], p->name) < 0 || | ||
113 | unpack_blob(v[3], &p->id) < 0 || | ||
114 | unpack_blob(v[4], &p->info_wire_data) < 0 || | ||
115 | unpack_blob(v[5], &p->enroll_wire_data) < 0 || | ||
116 | unpack_blob(v[6], &p->list_wire_data) < 0 || | ||
117 | unpack_blob(v[7], &p->set_name_wire_data) < 0 || | ||
118 | unpack_blob(v[8], &p->remove_wire_data) < 0) | ||
119 | goto fail; | ||
120 | |||
121 | ok = 0; | ||
122 | fail: | ||
123 | if (ok < 0) { | ||
124 | free(p); | ||
125 | p = NULL; | ||
126 | } | ||
127 | |||
128 | if (item) | ||
129 | cbor_decref(&item); | ||
130 | |||
131 | return p; | ||
123 | } | 132 | } |
124 | 133 | ||
125 | static size_t | 134 | size_t |
126 | pack(uint8_t *ptr, size_t len, const struct param *p) | 135 | pack(uint8_t *ptr, size_t len, const struct param *p) |
127 | { | 136 | { |
128 | const size_t max = len; | 137 | cbor_item_t *argv[9], *array = NULL; |
129 | 138 | size_t cbor_alloc_len, cbor_len = 0; | |
130 | if (pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 || | 139 | unsigned char *cbor = NULL; |
131 | pack_string(TAG_NAME, &ptr, &len, p->name) < 0 || | 140 | |
132 | pack_int(TAG_SEED, &ptr, &len, p->seed) < 0 || | 141 | memset(argv, 0, sizeof(argv)); |
133 | pack_blob(TAG_ID, &ptr, &len, &p->id) < 0 || | 142 | |
134 | pack_blob(TAG_INFO_WIRE_DATA, &ptr, &len, &p->info_wire_data) < 0 || | 143 | if ((array = cbor_new_definite_array(9)) == NULL || |
135 | pack_blob(TAG_ENROLL_WIRE_DATA, &ptr, &len, &p->enroll_wire_data) < 0 || | 144 | (argv[0] = pack_int(p->seed)) == NULL || |
136 | pack_blob(TAG_LIST_WIRE_DATA, &ptr, &len, &p->list_wire_data) < 0 || | 145 | (argv[1] = pack_string(p->pin)) == NULL || |
137 | pack_blob(TAG_SET_NAME_WIRE_DATA, &ptr, &len, &p->set_name_wire_data) < 0 || | 146 | (argv[2] = pack_string(p->name)) == NULL || |
138 | pack_blob(TAG_REMOVE_WIRE_DATA, &ptr, &len, &p->remove_wire_data) < 0) | 147 | (argv[3] = pack_blob(&p->id)) == NULL || |
139 | return (0); | 148 | (argv[4] = pack_blob(&p->info_wire_data)) == NULL || |
140 | 149 | (argv[5] = pack_blob(&p->enroll_wire_data)) == NULL || | |
141 | return (max - len); | 150 | (argv[6] = pack_blob(&p->list_wire_data)) == NULL || |
151 | (argv[7] = pack_blob(&p->set_name_wire_data)) == NULL || | ||
152 | (argv[8] = pack_blob(&p->remove_wire_data)) == NULL) | ||
153 | goto fail; | ||
154 | |||
155 | for (size_t i = 0; i < 9; i++) | ||
156 | if (cbor_array_push(array, argv[i]) == false) | ||
157 | goto fail; | ||
158 | |||
159 | if ((cbor_len = cbor_serialize_alloc(array, &cbor, | ||
160 | &cbor_alloc_len)) > len) { | ||
161 | cbor_len = 0; | ||
162 | goto fail; | ||
163 | } | ||
164 | |||
165 | memcpy(ptr, cbor, cbor_len); | ||
166 | fail: | ||
167 | for (size_t i = 0; i < 9; i++) | ||
168 | if (argv[i]) | ||
169 | cbor_decref(&argv[i]); | ||
170 | |||
171 | if (array) | ||
172 | cbor_decref(&array); | ||
173 | |||
174 | free(cbor); | ||
175 | |||
176 | return cbor_len; | ||
142 | } | 177 | } |
143 | 178 | ||
144 | static size_t | 179 | size_t |
145 | input_len(int max) | 180 | pack_dummy(uint8_t *ptr, size_t len) |
146 | { | 181 | { |
147 | return (2 * len_string(max) + len_int() + 6 * len_blob(max)); | 182 | struct param dummy; |
183 | uint8_t blob[4096]; | ||
184 | size_t blob_len; | ||
185 | |||
186 | memset(&dummy, 0, sizeof(dummy)); | ||
187 | |||
188 | strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin)); | ||
189 | strlcpy(dummy.name, dummy_name, sizeof(dummy.name)); | ||
190 | |||
191 | dummy.info_wire_data.len = sizeof(dummy_info_wire_data); | ||
192 | dummy.enroll_wire_data.len = sizeof(dummy_enroll_wire_data); | ||
193 | dummy.list_wire_data.len = sizeof(dummy_list_wire_data); | ||
194 | dummy.set_name_wire_data.len = sizeof(dummy_set_name_wire_data); | ||
195 | dummy.remove_wire_data.len = sizeof(dummy_remove_wire_data); | ||
196 | dummy.id.len = sizeof(dummy_id); | ||
197 | |||
198 | memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data, | ||
199 | dummy.info_wire_data.len); | ||
200 | memcpy(&dummy.enroll_wire_data.body, &dummy_enroll_wire_data, | ||
201 | dummy.enroll_wire_data.len); | ||
202 | memcpy(&dummy.list_wire_data.body, &dummy_list_wire_data, | ||
203 | dummy.list_wire_data.len); | ||
204 | memcpy(&dummy.set_name_wire_data.body, &dummy_set_name_wire_data, | ||
205 | dummy.set_name_wire_data.len); | ||
206 | memcpy(&dummy.remove_wire_data.body, &dummy_remove_wire_data, | ||
207 | dummy.remove_wire_data.len); | ||
208 | memcpy(&dummy.id.body, &dummy_id, dummy.id.len); | ||
209 | |||
210 | assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); | ||
211 | |||
212 | if (blob_len > len) { | ||
213 | memcpy(ptr, blob, len); | ||
214 | return len; | ||
215 | } | ||
216 | |||
217 | memcpy(ptr, blob, blob_len); | ||
218 | |||
219 | return blob_len; | ||
148 | } | 220 | } |
149 | 221 | ||
150 | static fido_dev_t * | 222 | static fido_dev_t * |
151 | prepare_dev() | 223 | prepare_dev(void) |
152 | { | 224 | { |
153 | fido_dev_t *dev; | 225 | fido_dev_t *dev; |
154 | fido_dev_io_t io; | 226 | fido_dev_io_t io; |
227 | bool x; | ||
155 | 228 | ||
156 | memset(&io, 0, sizeof(io)); | 229 | memset(&io, 0, sizeof(io)); |
157 | 230 | ||
@@ -163,26 +236,35 @@ prepare_dev() | |||
163 | if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev, | 236 | if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev, |
164 | &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) { | 237 | &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) { |
165 | fido_dev_free(&dev); | 238 | fido_dev_free(&dev); |
166 | return (NULL); | 239 | return NULL; |
167 | } | 240 | } |
168 | 241 | ||
169 | return (dev); | 242 | x = fido_dev_is_fido2(dev); |
243 | consume(&x, sizeof(x)); | ||
244 | x = fido_dev_supports_pin(dev); | ||
245 | consume(&x, sizeof(x)); | ||
246 | x = fido_dev_has_pin(dev); | ||
247 | consume(&x, sizeof(x)); | ||
248 | |||
249 | return dev; | ||
170 | } | 250 | } |
171 | 251 | ||
172 | static void | 252 | static void |
173 | get_info(struct param *p) | 253 | get_info(const struct param *p) |
174 | { | 254 | { |
175 | fido_dev_t *dev = NULL; | 255 | fido_dev_t *dev = NULL; |
176 | fido_bio_info_t *i = NULL; | 256 | fido_bio_info_t *i = NULL; |
177 | uint8_t type; | 257 | uint8_t type; |
178 | uint8_t max_samples; | 258 | uint8_t max_samples; |
259 | int r; | ||
179 | 260 | ||
180 | set_wire_data(p->info_wire_data.body, p->info_wire_data.len); | 261 | set_wire_data(p->info_wire_data.body, p->info_wire_data.len); |
181 | 262 | ||
182 | if ((dev = prepare_dev()) == NULL || (i = fido_bio_info_new()) == NULL) | 263 | if ((dev = prepare_dev()) == NULL || (i = fido_bio_info_new()) == NULL) |
183 | goto done; | 264 | goto done; |
184 | 265 | ||
185 | fido_bio_dev_get_info(dev, i); | 266 | r = fido_bio_dev_get_info(dev, i); |
267 | consume_str(fido_strerr(r)); | ||
186 | 268 | ||
187 | type = fido_bio_info_type(i); | 269 | type = fido_bio_info_type(i); |
188 | max_samples = fido_bio_info_max_samples(i); | 270 | max_samples = fido_bio_info_max_samples(i); |
@@ -217,7 +299,7 @@ consume_enroll(fido_bio_enroll_t *e) | |||
217 | } | 299 | } |
218 | 300 | ||
219 | static void | 301 | static void |
220 | enroll(struct param *p) | 302 | enroll(const struct param *p) |
221 | { | 303 | { |
222 | fido_dev_t *dev = NULL; | 304 | fido_dev_t *dev = NULL; |
223 | fido_bio_template_t *t = NULL; | 305 | fido_bio_template_t *t = NULL; |
@@ -252,7 +334,7 @@ done: | |||
252 | } | 334 | } |
253 | 335 | ||
254 | static void | 336 | static void |
255 | list(struct param *p) | 337 | list(const struct param *p) |
256 | { | 338 | { |
257 | fido_dev_t *dev = NULL; | 339 | fido_dev_t *dev = NULL; |
258 | fido_bio_template_array_t *ta = NULL; | 340 | fido_bio_template_array_t *ta = NULL; |
@@ -280,7 +362,7 @@ done: | |||
280 | } | 362 | } |
281 | 363 | ||
282 | static void | 364 | static void |
283 | set_name(struct param *p) | 365 | set_name(const struct param *p) |
284 | { | 366 | { |
285 | fido_dev_t *dev = NULL; | 367 | fido_dev_t *dev = NULL; |
286 | fido_bio_template_t *t = NULL; | 368 | fido_bio_template_t *t = NULL; |
@@ -306,10 +388,11 @@ done: | |||
306 | } | 388 | } |
307 | 389 | ||
308 | static void | 390 | static void |
309 | del(struct param *p) | 391 | del(const struct param *p) |
310 | { | 392 | { |
311 | fido_dev_t *dev = NULL; | 393 | fido_dev_t *dev = NULL; |
312 | fido_bio_template_t *t = NULL; | 394 | fido_bio_template_t *t = NULL; |
395 | int r; | ||
313 | 396 | ||
314 | set_wire_data(p->remove_wire_data.body, p->remove_wire_data.len); | 397 | set_wire_data(p->remove_wire_data.body, p->remove_wire_data.len); |
315 | 398 | ||
@@ -317,8 +400,9 @@ del(struct param *p) | |||
317 | (t = fido_bio_template_new()) == NULL) | 400 | (t = fido_bio_template_new()) == NULL) |
318 | goto done; | 401 | goto done; |
319 | 402 | ||
320 | fido_bio_template_set_id(t, p->id.body, p->id.len); | 403 | r = fido_bio_template_set_id(t, p->id.body, p->id.len); |
321 | consume_template(t); | 404 | consume_template(t); |
405 | consume_str(fido_strerr(r)); | ||
322 | 406 | ||
323 | fido_bio_dev_enroll_remove(dev, t, p->pin); | 407 | fido_bio_dev_enroll_remove(dev, t, p->pin); |
324 | 408 | ||
@@ -330,106 +414,37 @@ done: | |||
330 | fido_bio_template_free(&t); | 414 | fido_bio_template_free(&t); |
331 | } | 415 | } |
332 | 416 | ||
333 | int | 417 | void |
334 | LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) | 418 | test(const struct param *p) |
335 | { | 419 | { |
336 | struct param p; | 420 | prng_init((unsigned int)p->seed); |
337 | |||
338 | memset(&p, 0, sizeof(p)); | ||
339 | |||
340 | if (size < input_len(GETLEN_MIN) || size > input_len(GETLEN_MAX) || | ||
341 | unpack(data, size, &p) < 0) | ||
342 | return (0); | ||
343 | |||
344 | prng_init((unsigned int)p.seed); | ||
345 | |||
346 | fido_init(FIDO_DEBUG); | 421 | fido_init(FIDO_DEBUG); |
347 | fido_set_log_handler(consume_str); | 422 | fido_set_log_handler(consume_str); |
348 | 423 | ||
349 | get_info(&p); | 424 | get_info(p); |
350 | enroll(&p); | 425 | enroll(p); |
351 | list(&p); | 426 | list(p); |
352 | set_name(&p); | 427 | set_name(p); |
353 | del(&p); | 428 | del(p); |
354 | |||
355 | return (0); | ||
356 | } | 429 | } |
357 | 430 | ||
358 | static size_t | 431 | void |
359 | pack_dummy(uint8_t *ptr, size_t len) | 432 | mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN |
360 | { | 433 | { |
361 | struct param dummy; | 434 | if (flags & MUTATE_SEED) |
362 | uint8_t blob[32768]; | 435 | p->seed = (int)seed; |
363 | size_t blob_len; | ||
364 | |||
365 | memset(&dummy, 0, sizeof(dummy)); | ||
366 | |||
367 | strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin)); | ||
368 | strlcpy(dummy.name, dummy_name, sizeof(dummy.name)); | ||
369 | |||
370 | dummy.info_wire_data.len = sizeof(dummy_info_wire_data); | ||
371 | dummy.enroll_wire_data.len = sizeof(dummy_enroll_wire_data); | ||
372 | dummy.list_wire_data.len = sizeof(dummy_list_wire_data); | ||
373 | dummy.set_name_wire_data.len = sizeof(dummy_set_name_wire_data); | ||
374 | dummy.remove_wire_data.len = sizeof(dummy_remove_wire_data); | ||
375 | dummy.id.len = sizeof(dummy_id); | ||
376 | |||
377 | memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data, | ||
378 | dummy.info_wire_data.len); | ||
379 | memcpy(&dummy.enroll_wire_data.body, &dummy_enroll_wire_data, | ||
380 | dummy.enroll_wire_data.len); | ||
381 | memcpy(&dummy.list_wire_data.body, &dummy_list_wire_data, | ||
382 | dummy.list_wire_data.len); | ||
383 | memcpy(&dummy.set_name_wire_data.body, &dummy_set_name_wire_data, | ||
384 | dummy.set_name_wire_data.len); | ||
385 | memcpy(&dummy.remove_wire_data.body, &dummy_remove_wire_data, | ||
386 | dummy.remove_wire_data.len); | ||
387 | memcpy(&dummy.id.body, &dummy_id, dummy.id.len); | ||
388 | |||
389 | blob_len = pack(blob, sizeof(blob), &dummy); | ||
390 | assert(blob_len != 0); | ||
391 | 436 | ||
392 | if (blob_len > len) { | 437 | if (flags & MUTATE_PARAM) { |
393 | memcpy(ptr, blob, len); | 438 | mutate_blob(&p->id); |
394 | return (len); | 439 | mutate_string(p->pin); |
440 | mutate_string(p->name); | ||
395 | } | 441 | } |
396 | 442 | ||
397 | memcpy(ptr, blob, blob_len); | 443 | if (flags & MUTATE_WIREDATA) { |
398 | 444 | mutate_blob(&p->info_wire_data); | |
399 | return (blob_len); | 445 | mutate_blob(&p->enroll_wire_data); |
400 | } | 446 | mutate_blob(&p->list_wire_data); |
401 | 447 | mutate_blob(&p->set_name_wire_data); | |
402 | size_t | 448 | mutate_blob(&p->remove_wire_data); |
403 | LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, | 449 | } |
404 | unsigned int seed) NO_MSAN | ||
405 | { | ||
406 | struct param p; | ||
407 | uint8_t blob[16384]; | ||
408 | size_t blob_len; | ||
409 | |||
410 | memset(&p, 0, sizeof(p)); | ||
411 | |||
412 | if (unpack(data, size, &p) < 0) | ||
413 | return (pack_dummy(data, maxsize)); | ||
414 | |||
415 | p.seed = (int)seed; | ||
416 | |||
417 | mutate_blob(&p.id); | ||
418 | mutate_blob(&p.info_wire_data); | ||
419 | mutate_blob(&p.enroll_wire_data); | ||
420 | mutate_blob(&p.list_wire_data); | ||
421 | mutate_blob(&p.set_name_wire_data); | ||
422 | mutate_blob(&p.remove_wire_data); | ||
423 | |||
424 | mutate_string(p.pin); | ||
425 | mutate_string(p.name); | ||
426 | |||
427 | blob_len = pack(blob, sizeof(blob), &p); | ||
428 | |||
429 | if (blob_len == 0 || blob_len > maxsize) | ||
430 | return (0); | ||
431 | |||
432 | memcpy(data, blob, blob_len); | ||
433 | |||
434 | return (blob_len); | ||
435 | } | 450 | } |