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