summaryrefslogtreecommitdiffstats
path: root/fuzz/libfuzzer.c
diff options
context:
space:
mode:
Diffstat (limited to 'fuzz/libfuzzer.c')
-rw-r--r--fuzz/libfuzzer.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/fuzz/libfuzzer.c b/fuzz/libfuzzer.c
new file mode 100644
index 0000000..073ebe6
--- /dev/null
+++ b/fuzz/libfuzzer.c
@@ -0,0 +1,230 @@
+/*
+ * 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 <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;
+}