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