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
save_seed(const char * opt)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
parse_mutate_flags(const char * opt,unsigned int * mutate_flags)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
LLVMFuzzerInitialize(int * argc,char *** argv)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
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)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
LLVMFuzzerCustomMutator(uint8_t * data,size_t size,size_t maxsize,unsigned int seed)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