1 /*
2 * Copyright (c) 2019-2022 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 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8 #include <openssl/sha.h>
9
10 #include <err.h>
11 #include <fcntl.h>
12 #include <limits.h>
13 #include <stdbool.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #include "mutator_aux.h"
21
22 extern int fuzz_save_corpus;
23
24 static bool debug;
25 static unsigned int flags = MUTATE_ALL;
26 static unsigned long long test_fail;
27 static unsigned long long test_total;
28 static unsigned long long mutate_fail;
29 static unsigned long long mutate_total;
30
31 int LLVMFuzzerInitialize(int *, char ***);
32 int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
33 size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int);
34
35 static int
save_seed(const char * opt)36 save_seed(const char *opt)
37 {
38 const char *path;
39 int fd = -1, status = 1;
40 void *buf = NULL;
41 const size_t buflen = MAXCORPUS;
42 size_t n;
43 struct param *p = NULL;
44
45 if ((path = strchr(opt, '=')) == NULL || strlen(++path) == 0) {
46 warnx("usage: --fido-save-seed=<path>");
47 goto fail;
48 }
49
50 if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) {
51 warn("open %s", path);
52 goto fail;
53 }
54
55 if ((buf = malloc(buflen)) == NULL) {
56 warn("malloc");
57 goto fail;
58 }
59
60 n = pack_dummy(buf, buflen);
61
62 if ((p = unpack(buf, n)) == NULL) {
63 warnx("unpack");
64 goto fail;
65 }
66
67 if (write(fd, buf, n) != (ssize_t)n) {
68 warn("write %s", path);
69 goto fail;
70 }
71
72 status = 0;
73 fail:
74 if (fd != -1)
75 close(fd);
76 free(buf);
77 free(p);
78
79 return status;
80 }
81
82 static int
save_corpus(const struct param * p)83 save_corpus(const struct param *p)
84 {
85 uint8_t blob[MAXCORPUS], dgst[SHA256_DIGEST_LENGTH];
86 size_t blob_len;
87 char path[PATH_MAX];
88 int r, fd;
89
90 if ((blob_len = pack(blob, sizeof(blob), p)) == 0 ||
91 blob_len > sizeof(blob)) {
92 warnx("pack");
93 return -1;
94 }
95
96 if (SHA256(blob, blob_len, dgst) != dgst) {
97 warnx("sha256");
98 return -1;
99 }
100
101 if ((r = snprintf(path, sizeof(path), "saved_corpus_%02x%02x%02x%02x"
102 "%02x%02x%02x%02x", dgst[0], dgst[1], dgst[2], dgst[3], dgst[4],
103 dgst[5], dgst[6], dgst[7])) < 0 || (size_t)r >= sizeof(path)) {
104 warnx("snprintf");
105 return -1;
106 }
107
108 if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) {
109 warn("open %s", path);
110 return -1;
111 }
112
113 if (write(fd, blob, blob_len) != (ssize_t)blob_len) {
114 warn("write");
115 r = -1;
116 } else {
117 warnx("wrote %s", path);
118 r = 0;
119 }
120
121 close(fd);
122
123 return r;
124 }
125
126 static void
parse_mutate_flags(const char * opt,unsigned int * mutate_flags)127 parse_mutate_flags(const char *opt, unsigned int *mutate_flags)
128 {
129 const char *f;
130
131 if ((f = strchr(opt, '=')) == NULL || strlen(++f) == 0)
132 errx(1, "usage: --fido-mutate=<flag>");
133
134 if (strcmp(f, "seed") == 0)
135 *mutate_flags |= MUTATE_SEED;
136 else if (strcmp(f, "param") == 0)
137 *mutate_flags |= MUTATE_PARAM;
138 else if (strcmp(f, "wiredata") == 0)
139 *mutate_flags |= MUTATE_WIREDATA;
140 else
141 errx(1, "--fido-mutate: unknown flag '%s'", f);
142 }
143
144 int
LLVMFuzzerInitialize(int * argc,char *** argv)145 LLVMFuzzerInitialize(int *argc, char ***argv)
146 {
147 unsigned int mutate_flags = 0;
148
149 for (int i = 0; i < *argc; i++)
150 if (strcmp((*argv)[i], "--fido-debug") == 0) {
151 debug = 1;
152 } else if (strncmp((*argv)[i], "--fido-save-seed=", 17) == 0) {
153 exit(save_seed((*argv)[i]));
154 } else if (strncmp((*argv)[i], "--fido-mutate=", 14) == 0) {
155 parse_mutate_flags((*argv)[i], &mutate_flags);
156 }
157
158 if (mutate_flags)
159 flags = mutate_flags;
160
161 return 0;
162 }
163
164 int
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)165 LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
166 {
167 struct param *p;
168
169 if (size > MAXCORPUS)
170 return 0;
171
172 if (++test_total % 100000 == 0 && debug) {
173 double r = (double)test_fail/(double)test_total * 100.0;
174 fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__,
175 test_fail, test_total, r);
176 }
177
178 if ((p = unpack(data, size)) == NULL)
179 test_fail++;
180 else {
181 fuzz_save_corpus = 0;
182 test(p);
183 if (fuzz_save_corpus && save_corpus(p) < 0)
184 fprintf(stderr, "%s: failed to save corpus\n",
185 __func__);
186 free(p);
187 }
188
189 return 0;
190 }
191
192 size_t
LLVMFuzzerCustomMutator(uint8_t * data,size_t size,size_t maxsize,unsigned int seed)193 LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize,
194 unsigned int seed) NO_MSAN
195 {
196 struct param *p;
197 uint8_t blob[MAXCORPUS];
198 size_t blob_len;
199
200 memset(&p, 0, sizeof(p));
201
202 #ifdef WITH_MSAN
203 __msan_unpoison(data, maxsize);
204 #endif
205
206 if (++mutate_total % 100000 == 0 && debug) {
207 double r = (double)mutate_fail/(double)mutate_total * 100.0;
208 fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__,
209 mutate_fail, mutate_total, r);
210 }
211
212 if ((p = unpack(data, size)) == NULL) {
213 mutate_fail++;
214 return pack_dummy(data, maxsize);
215 }
216
217 mutate(p, seed, flags);
218
219 if ((blob_len = pack(blob, sizeof(blob), p)) == 0 ||
220 blob_len > sizeof(blob) || blob_len > maxsize) {
221 mutate_fail++;
222 free(p);
223 return 0;
224 }
225
226 free(p);
227
228 memcpy(data, blob, blob_len);
229
230 return blob_len;
231 }
232