summaryrefslogtreecommitdiffhomepage
path: root/fuzz
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2020-10-25 22:52:36 +0800
committerMatt Johnston <matt@ucc.asn.au>2020-10-25 22:52:36 +0800
commit4e8a1da551db44e5865f6a1cc28e24fc2c2bdd0f (patch)
tree409aebeb7bd0f03248dc0716d01a408cc09ed878 /fuzz
parent754dc50b933c76ca39660fe2d9e65e35f9aef428 (diff)
Add first try at fuzzing custom mutator
Diffstat (limited to 'fuzz')
-rw-r--r--fuzz/fuzz-common.c4
-rw-r--r--fuzz/fuzz-harness.c6
-rw-r--r--fuzz/fuzz-sshpacketmutator.c175
-rw-r--r--fuzz/fuzzer-client_mutator.c8
4 files changed, 193 insertions, 0 deletions
diff --git a/fuzz/fuzz-common.c b/fuzz/fuzz-common.c
index 065b7d9..8dddacb 100644
--- a/fuzz/fuzz-common.c
+++ b/fuzz/fuzz-common.c
@@ -256,10 +256,12 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
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 fuzz_run_client(const uint8_t *Data, size_t Size, int skip_kexmaths) {
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 */
diff --git a/fuzz/fuzz-harness.c b/fuzz/fuzz-harness.c
index ced707c..36905d6 100644
--- a/fuzz/fuzz-harness.c
+++ b/fuzz/fuzz-harness.c
@@ -46,3 +46,9 @@ int main(int argc, char ** argv) {
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;
+}
diff --git a/fuzz/fuzz-sshpacketmutator.c b/fuzz/fuzz-sshpacketmutator.c
new file mode 100644
index 0000000..fa0ed5f
--- /dev/null
+++ b/fuzz/fuzz-sshpacketmutator.c
@@ -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;
+}
+
diff --git a/fuzz/fuzzer-client_mutator.c b/fuzz/fuzzer-client_mutator.c
new file mode 100644
index 0000000..df57314
--- /dev/null
+++ b/fuzz/fuzzer-client_mutator.c
@@ -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);
+}
+