changeset 1760:2406a9987810

Add first try at fuzzing custom mutator
author Matt Johnston <matt@ucc.asn.au>
date Sun, 25 Oct 2020 22:52:36 +0800
parents 4c5599435084
children a339b1c4b9f2
files Makefile.in fuzz/fuzz-common.c fuzz/fuzz-harness.c fuzz/fuzz-sshpacketmutator.c fuzz/fuzzer-client_mutator.c
diffstat 5 files changed, 195 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.in	Sun Oct 25 21:47:42 2020 +0800
+++ b/Makefile.in	Sun Oct 25 22:52:36 2020 +0800
@@ -269,7 +269,8 @@
 
 # list of fuzz targets
 FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths \
-	fuzzer-kexdh fuzzer-kexecdh fuzzer-kexcurve25519 fuzzer-client fuzzer-client_nomaths
+	fuzzer-kexdh fuzzer-kexecdh fuzzer-kexcurve25519 fuzzer-client fuzzer-client_nomaths \
+	fuzzer-client_mutator
 
 FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS))
 FUZZ_OBJS = $(addprefix fuzz/,$(addsuffix .o,$(FUZZ_TARGETS)))
--- a/fuzz/fuzz-common.c	Sun Oct 25 21:47:42 2020 +0800
+++ b/fuzz/fuzz-common.c	Sun Oct 25 22:52:36 2020 +0800
@@ -256,10 +256,12 @@
     int fakesock = wrapfd_new();
 
     m_malloc_set_epoch(1);
+    fuzz.do_jmp = 1;
     if (setjmp(fuzz.jmp) == 0) {
         svr_session(fakesock, fakesock);
         m_malloc_free_epoch(1, 0);
     } else {
+        fuzz.do_jmp = 0;
         m_malloc_free_epoch(1, 1);
         TRACE(("dropbear_exit longjmped"))
         /* dropbear_exit jumped here */
@@ -302,10 +304,12 @@
     int fakesock = wrapfd_new();
 
     m_malloc_set_epoch(1);
+    fuzz.do_jmp = 1;
     if (setjmp(fuzz.jmp) == 0) {
         cli_session(fakesock, fakesock, NULL, 0);
         m_malloc_free_epoch(1, 0);
     } else {
+        fuzz.do_jmp = 0;
         m_malloc_free_epoch(1, 1);
         TRACE(("dropbear_exit longjmped"))
         /* dropbear_exit jumped here */
--- a/fuzz/fuzz-harness.c	Sun Oct 25 21:47:42 2020 +0800
+++ b/fuzz/fuzz-harness.c	Sun Oct 25 22:52:36 2020 +0800
@@ -46,3 +46,9 @@
 
     return 0;
 }
+
+size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+    printf("standalone fuzzer harness shouldn't call LLVMFuzzerMutate");
+    abort();
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fuzz/fuzz-sshpacketmutator.c	Sun Oct 25 22:52:36 2020 +0800
@@ -0,0 +1,175 @@
+#include "fuzz.h"
+
+size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+
+static void fuzz_get_packets(buffer *inp, buffer **out_packets, unsigned int *num_out_packets) {
+    /* Skip any existing banner. Format is
+          SSH-protoversion-softwareversion SP comments CR LF
+    so we look for SSH-2. then a subsequent LF */
+    unsigned char* version = memmem(inp->data, inp->len, "SSH-2.", strlen("SSH-2."));
+    if (version) {
+        buf_incrpos(inp, version - inp->data);
+        unsigned char* newline = memchr(&inp->data[inp->pos], '\n', inp->len - inp->pos);
+        if (newline) {
+            buf_incrpos(inp, newline - &inp->data[inp->pos]);
+        } else {
+            /* Give up on any version string */
+            buf_setpos(inp, 0);
+        }
+    }
+
+    const unsigned int max_out_packets = *num_out_packets;
+    *num_out_packets = 0;
+    while (1) {
+        if (inp->pos + 4 > inp->len) {
+            /* End of input */
+            break;
+        }
+
+        if (*num_out_packets >= max_out_packets) {
+            /* End of output */
+            break;
+        }
+
+        /* Read packet */
+        unsigned int packet_len = buf_getint(inp);
+        if (packet_len <= RECV_MAX_PACKET_LEN) {
+            /* Bad length, try skipping a single byte */
+            buf_decrpos(inp, 3);
+            continue;
+        }
+        packet_len = MIN(packet_len, inp->len - inp->pos);
+
+        /* Copy to output buffer */
+        buffer* new_packet = buf_new(RECV_MAX_PACKET_LEN);
+        buf_putint(new_packet, packet_len);
+        buf_putbytes(new_packet, buf_getptr(inp, packet_len), packet_len);
+        buf_incrpos(inp, packet_len);
+
+        out_packets[*num_out_packets] = new_packet;
+        (*num_out_packets)++;
+    }
+
+}
+
+/* Mutate in-place */
+void buf_llvm_mutate(buffer *buf) {
+    /* Position it after packet_length and padding_length */
+    const unsigned int offset = 5;
+    if (buf->len < offset) {
+        return;
+    }
+    buf_setpos(buf, offset);
+    size_t max_size = buf->size - buf->pos;
+    size_t new_size = LLVMFuzzerMutate(buf_getwriteptr(buf, max_size),
+        buf->len - buf->pos, max_size);
+    buf_setpos(buf, 0);
+    buf_putint(buf, new_size);
+    buf_setlen(buf, offset + new_size);
+}
+
+
+static const char* FIXED_VERSION = "SSH-2.0-dbfuzz\r\n";
+static const size_t MAX_FUZZ_PACKETS = 500;
+/* XXX This might need tuning */
+static const size_t MAX_OUT_SIZE = 50000;
+
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+              size_t MaxSize, unsigned int Seed) {
+    int i;
+    unsigned short randstate[3] = {0,0,0};
+    memcpy(randstate, &Seed, sizeof(Seed));
+
+    /* 1% chance straight llvm mutate */
+    if (nrand48(randstate) % 100 == 0) {
+        return LLVMFuzzerMutate(Data, Size, MaxSize);
+    }
+
+    buffer inp_buf = {.data = Data, .size = Size, .len = Size, .pos = 0};
+    buffer *inp = &inp_buf;
+
+    /* Parse packets */
+    buffer* packets[MAX_FUZZ_PACKETS] = {0};
+    unsigned int num_packets = MAX_FUZZ_PACKETS;
+    fuzz_get_packets(inp, packets, &num_packets);
+
+    if (num_packets == 0) {
+        // gotta do something
+        memcpy(Data, FIXED_VERSION, MIN(strlen(FIXED_VERSION), MaxSize));
+        return LLVMFuzzerMutate(Data, Size, MaxSize);
+    }
+
+    /* Start output */
+    buffer *oup = buf_new(MAX_OUT_SIZE);
+    /* Put a new banner to output */
+    buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION));
+
+    /* Iterate output */
+    for (i = 0; i < num_packets+1; i++) {
+        // These are pointers to output
+        buffer *out_packetA = NULL, *out_packetB = NULL;
+        // These need to be freed
+        buffer *alloc_packetA = NULL, *alloc_packetB = NULL;
+
+        /* 5% chance each */
+        const int optA = nrand48(randstate) % 20;
+        const int other = nrand48(randstate) % num_packets;
+        if (optA == 0) {
+            /* Copy another */
+            out_packetA = packets[nrand48(randstate) % num_packets];
+        }
+        if (optA == 1) {
+            /* Mutate another */
+            alloc_packetA = buf_new(RECV_MAX_PACKET_LEN);
+            buffer *from = packets[nrand48(randstate) % num_packets];
+            buf_putbytes(alloc_packetA, from->data, from->len);
+            out_packetA = alloc_packetA;
+            buf_llvm_mutate(out_packetA);
+        }
+
+        /* 10% chance each of mutate or drop */
+        if (i < num_packets) {
+            int optB = nrand48(randstate) % 10;
+            if (optB == 0) {
+                /* Copy as-is */
+                out_packetB = packets[i];
+            } 
+            if (optB == 1) {
+                /* Drop it */
+            } 
+            if (optB == 2) {
+                /* Mutate it */
+                alloc_packetB = buf_new(RECV_MAX_PACKET_LEN);
+                buffer *from = packets[nrand48(randstate) % num_packets];
+                buf_putbytes(alloc_packetB, from->data, from->len);
+                out_packetB = alloc_packetB;
+                buf_llvm_mutate(out_packetB);
+            } 
+        }
+
+        if (out_packetA && oup->len + out_packetA->len <= oup->size) {
+            buf_putbytes(oup, out_packetA->data, out_packetA->len);
+        }
+        if (out_packetB && oup->len + out_packetB->len <= oup->size) {
+            buf_putbytes(oup, out_packetB->data, out_packetB->len);
+        }
+        if (alloc_packetA) {
+            buf_free(alloc_packetA);
+            alloc_packetA = NULL;
+        }
+        if (alloc_packetB) {
+            buf_free(alloc_packetB);
+            alloc_packetB = NULL;
+        }
+    }
+
+    for (i = 0; i < num_packets; i++) {
+        buf_free(packets[i]);
+    }
+
+    size_t ret_len = MIN(MaxSize, oup->len);
+    memcpy(Data, oup->data, ret_len);
+    buf_free(oup);
+    return ret_len;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fuzz/fuzzer-client_mutator.c	Sun Oct 25 22:52:36 2020 +0800
@@ -0,0 +1,8 @@
+#include "fuzz.h"
+
+#include "fuzz-sshpacketmutator.c"
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+	return fuzz_run_client(Data, Size, 0);
+}
+