xref: /freebsd/contrib/libfido2/fuzz/libfuzzer.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*
2  * Copyright (c) 2019 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  */
6 
7 #include <err.h>
8 #include <fcntl.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 #include "mutator_aux.h"
17 
18 static bool debug;
19 static unsigned int flags = MUTATE_ALL;
20 static unsigned long long test_fail;
21 static unsigned long long test_total;
22 static unsigned long long mutate_fail;
23 static unsigned long long mutate_total;
24 
25 int LLVMFuzzerInitialize(int *, char ***);
26 int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
27 size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int);
28 
29 static int
30 save_seed(const char *opt)
31 {
32 	const char *path;
33 	int fd = -1, status = 1;
34 	void *buf = NULL;
35 	const size_t buflen = 4096;
36 	size_t n;
37 	struct param *p = NULL;
38 
39 	if ((path = strchr(opt, '=')) == NULL || strlen(++path) == 0) {
40 		warnx("usage: --fido-save-seed=<path>");
41 		goto fail;
42 	}
43 
44 	if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) {
45 		warn("open %s", path);
46 		goto fail;
47 	}
48 
49 	if ((buf = malloc(buflen)) == NULL) {
50 		warn("malloc");
51 		goto fail;
52 	}
53 
54 	n = pack_dummy(buf, buflen);
55 
56 	if ((p = unpack(buf, n)) == NULL) {
57 		warnx("unpack");
58 		goto fail;
59 	}
60 
61 	if (write(fd, buf, n) != (ssize_t)n) {
62 		warn("write %s", path);
63 		goto fail;
64 	}
65 
66 	status = 0;
67 fail:
68 	if (fd != -1)
69 		close(fd);
70 	free(buf);
71 	free(p);
72 
73 	return status;
74 }
75 
76 static void
77 parse_mutate_flags(const char *opt, unsigned int *mutate_flags)
78 {
79 	const char *f;
80 
81 	if ((f = strchr(opt, '=')) == NULL || strlen(++f) == 0)
82 		errx(1, "usage: --fido-mutate=<flag>");
83 
84 	if (strcmp(f, "seed") == 0)
85 		*mutate_flags |= MUTATE_SEED;
86 	else if (strcmp(f, "param") == 0)
87 		*mutate_flags |= MUTATE_PARAM;
88 	else if (strcmp(f, "wiredata") == 0)
89 		*mutate_flags |= MUTATE_WIREDATA;
90 	else
91 		errx(1, "--fido-mutate: unknown flag '%s'", f);
92 }
93 
94 int
95 LLVMFuzzerInitialize(int *argc, char ***argv)
96 {
97 	unsigned int mutate_flags = 0;
98 
99 	for (int i = 0; i < *argc; i++)
100 		if (strcmp((*argv)[i], "--fido-debug") == 0) {
101 			debug = 1;
102 		} else if (strncmp((*argv)[i], "--fido-save-seed=", 17) == 0) {
103 			exit(save_seed((*argv)[i]));
104 		} else if (strncmp((*argv)[i], "--fido-mutate=", 14) == 0) {
105 			parse_mutate_flags((*argv)[i], &mutate_flags);
106 		}
107 
108 	if (mutate_flags)
109 		flags = mutate_flags;
110 
111 	return 0;
112 }
113 
114 int
115 LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
116 {
117 	struct param *p;
118 
119 	if (size > 4096)
120 		return 0;
121 
122 	if (++test_total % 100000 == 0 && debug) {
123 		double r = (double)test_fail/(double)test_total * 100.0;
124 		fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__,
125 		    test_fail, test_total, r);
126 	}
127 
128 	if ((p = unpack(data, size)) == NULL)
129 		test_fail++;
130 	else {
131 		test(p);
132 		free(p);
133 	}
134 
135 	return 0;
136 }
137 
138 size_t
139 LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize,
140     unsigned int seed) NO_MSAN
141 {
142 	struct param *p;
143 	uint8_t blob[4096];
144 	size_t blob_len;
145 
146 	memset(&p, 0, sizeof(p));
147 
148 #ifdef WITH_MSAN
149 	__msan_unpoison(data, maxsize);
150 #endif
151 
152 	if (++mutate_total % 100000 == 0 && debug) {
153 		double r = (double)mutate_fail/(double)mutate_total * 100.0;
154 		fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__,
155 		    mutate_fail, mutate_total, r);
156 	}
157 
158 	if ((p = unpack(data, size)) == NULL) {
159 		mutate_fail++;
160 		return pack_dummy(data, maxsize);
161 	}
162 
163 	mutate(p, seed, flags);
164 
165 	if ((blob_len = pack(blob, sizeof(blob), p)) == 0 ||
166 	    blob_len > sizeof(blob) || blob_len > maxsize) {
167 		mutate_fail++;
168 		free(p);
169 		return 0;
170 	}
171 
172 	free(p);
173 
174 	memcpy(data, blob, blob_len);
175 
176 	return blob_len;
177 }
178