/* * Copyright (c) 2019-2022 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #include <openssl/sha.h> #include <err.h> #include <fcntl.h> #include <limits.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "mutator_aux.h" extern int fuzz_save_corpus; static bool debug; static unsigned int flags = MUTATE_ALL; static unsigned long long test_fail; static unsigned long long test_total; static unsigned long long mutate_fail; static unsigned long long mutate_total; int LLVMFuzzerInitialize(int *, char ***); int LLVMFuzzerTestOneInput(const uint8_t *, size_t); size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); static int save_seed(const char *opt) { const char *path; int fd = -1, status = 1; void *buf = NULL; const size_t buflen = MAXCORPUS; size_t n; struct param *p = NULL; if ((path = strchr(opt, '=')) == NULL || strlen(++path) == 0) { warnx("usage: --fido-save-seed=<path>"); goto fail; } if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) { warn("open %s", path); goto fail; } if ((buf = malloc(buflen)) == NULL) { warn("malloc"); goto fail; } n = pack_dummy(buf, buflen); if ((p = unpack(buf, n)) == NULL) { warnx("unpack"); goto fail; } if (write(fd, buf, n) != (ssize_t)n) { warn("write %s", path); goto fail; } status = 0; fail: if (fd != -1) close(fd); free(buf); free(p); return status; } static int save_corpus(const struct param *p) { uint8_t blob[MAXCORPUS], dgst[SHA256_DIGEST_LENGTH]; size_t blob_len; char path[PATH_MAX]; int r, fd; if ((blob_len = pack(blob, sizeof(blob), p)) == 0 || blob_len > sizeof(blob)) { warnx("pack"); return -1; } if (SHA256(blob, blob_len, dgst) != dgst) { warnx("sha256"); return -1; } if ((r = snprintf(path, sizeof(path), "saved_corpus_%02x%02x%02x%02x" "%02x%02x%02x%02x", dgst[0], dgst[1], dgst[2], dgst[3], dgst[4], dgst[5], dgst[6], dgst[7])) < 0 || (size_t)r >= sizeof(path)) { warnx("snprintf"); return -1; } if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) { warn("open %s", path); return -1; } if (write(fd, blob, blob_len) != (ssize_t)blob_len) { warn("write"); r = -1; } else { warnx("wrote %s", path); r = 0; } close(fd); return r; } static void parse_mutate_flags(const char *opt, unsigned int *mutate_flags) { const char *f; if ((f = strchr(opt, '=')) == NULL || strlen(++f) == 0) errx(1, "usage: --fido-mutate=<flag>"); if (strcmp(f, "seed") == 0) *mutate_flags |= MUTATE_SEED; else if (strcmp(f, "param") == 0) *mutate_flags |= MUTATE_PARAM; else if (strcmp(f, "wiredata") == 0) *mutate_flags |= MUTATE_WIREDATA; else errx(1, "--fido-mutate: unknown flag '%s'", f); } int LLVMFuzzerInitialize(int *argc, char ***argv) { unsigned int mutate_flags = 0; for (int i = 0; i < *argc; i++) if (strcmp((*argv)[i], "--fido-debug") == 0) { debug = 1; } else if (strncmp((*argv)[i], "--fido-save-seed=", 17) == 0) { exit(save_seed((*argv)[i])); } else if (strncmp((*argv)[i], "--fido-mutate=", 14) == 0) { parse_mutate_flags((*argv)[i], &mutate_flags); } if (mutate_flags) flags = mutate_flags; return 0; } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { struct param *p; if (size > MAXCORPUS) return 0; if (++test_total % 100000 == 0 && debug) { double r = (double)test_fail/(double)test_total * 100.0; fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__, test_fail, test_total, r); } if ((p = unpack(data, size)) == NULL) test_fail++; else { fuzz_save_corpus = 0; test(p); if (fuzz_save_corpus && save_corpus(p) < 0) fprintf(stderr, "%s: failed to save corpus\n", __func__); free(p); } return 0; } size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, unsigned int seed) NO_MSAN { struct param *p; uint8_t blob[MAXCORPUS]; size_t blob_len; memset(&p, 0, sizeof(p)); #ifdef WITH_MSAN __msan_unpoison(data, maxsize); #endif if (++mutate_total % 100000 == 0 && debug) { double r = (double)mutate_fail/(double)mutate_total * 100.0; fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__, mutate_fail, mutate_total, r); } if ((p = unpack(data, size)) == NULL) { mutate_fail++; return pack_dummy(data, maxsize); } mutate(p, seed, flags); if ((blob_len = pack(blob, sizeof(blob), p)) == 0 || blob_len > sizeof(blob) || blob_len > maxsize) { mutate_fail++; free(p); return 0; } free(p); memcpy(data, blob, blob_len); return blob_len; }