changeset 1356:3677a510f545 fuzz

add wrapfd. improve fuzzer in makefile
author Matt Johnston <matt@ucc.asn.au>
date Fri, 19 May 2017 00:48:46 +0800
parents 3fdd8c5a0195
children 08f4fa4dc6a0
files Makefile.in configure.ac fuzz-common.c fuzz-wrapfd.c fuzz-wrapfd.h fuzz.h fuzzer-preauth.c
diffstat 7 files changed, 268 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.in	Thu May 18 23:45:10 2017 +0800
+++ b/Makefile.in	Fri May 19 00:48:46 2017 +0800
@@ -34,7 +34,7 @@
 		queue.o \
 		atomicio.o compat.o fake-rfc2553.o \
 		ltc_prng.o ecc.o ecdsa.o crypto_desc.o \
-		gensignkey.o gendss.o genrsa.o fuzz-common.o 
+		gensignkey.o gendss.o genrsa.o 
 
 SVROBJS=svr-kex.o svr-auth.o sshpty.o \
 		svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \
@@ -57,6 +57,10 @@
 
 SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o
 
+ifeq (@DROPBEAR_FUZZ@, 1)
+	COMMONOBJS += fuzz-common.o  fuzz-wrapfd.o
+endif
+
 HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \
 		dss.h bignum.h signkey.h rsa.h dbrandom.h service.h auth.h \
 		debug.h channel.h chansession.h config.h queue.h sshpty.h \
@@ -270,3 +274,4 @@
 	/usr/bin/xxd -i -a keyr >> hostkeys.c
 	/usr/bin/xxd -i -a keye >> hostkeys.c
 	/usr/bin/xxd -i -a keyd >> hostkeys.c
+
--- a/configure.ac	Thu May 18 23:45:10 2017 +0800
+++ b/configure.ac	Fri May 19 00:48:46 2017 +0800
@@ -223,10 +223,14 @@
 	[
 		AC_DEFINE(DROPBEAR_FUZZ, 1, Fuzzing)
 		AC_MSG_NOTICE(Enabling fuzzing)
+		DROPBEAR_FUZZ=1
+	],
+	[
+		DROPBEAR_FUZZ=0
 	]
+
 )
-
-			
+AC_SUBST(DROPBEAR_FUZZ)
 
 # Checks for header files.
 AC_HEADER_STDC
--- a/fuzz-common.c	Thu May 18 23:45:10 2017 +0800
+++ b/fuzz-common.c	Fri May 19 00:48:46 2017 +0800
@@ -8,6 +8,8 @@
 #include "runopts.h"
 #include "crypto_desc.h"
 #include "session.h"
+#include "dbrandom.h"
+#include "fuzz-wrapfd.h"
 
 struct dropbear_fuzz_options fuzz;
 
@@ -15,9 +17,40 @@
 
 static void common_setup_fuzzer(void) {
     fuzz.fuzzing = 1;
+    fuzz.input = m_malloc(sizeof(buffer));
     crypto_init();
 }
 
+int fuzzer_set_input(const uint8_t *Data, size_t Size) {
+
+    fuzz.input->data = (unsigned char*)Data;
+    fuzz.input->size = Size;
+    fuzz.input->len = Size;
+    fuzz.input->pos = 0;
+
+    // get prefix. input format is
+    // string prefix
+    //     uint32_t seed
+    //     ... to be extended later
+    // [bytes] ssh input stream
+
+    // be careful to avoid triggering buffer.c assertions
+    if (fuzz.input->len < 8) {
+        return DROPBEAR_FAILURE;
+    }
+    size_t prefix_size = buf_getint(fuzz.input);
+    if (prefix_size != 4) {
+        return DROPBEAR_FAILURE;
+    }
+    uint32_t wrapseed = buf_getint(fuzz.input);
+    wrapfd_setup(wrapseed);
+
+    seedrandom();
+
+    return DROPBEAR_SUCCESS;
+}
+
+
 void svr_setup_fuzzer(void) {
     struct passwd *pw;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fuzz-wrapfd.c	Fri May 19 00:48:46 2017 +0800
@@ -0,0 +1,193 @@
+#include "includes.h"
+#include "fuzz-wrapfd.h"
+
+static const int IOWRAP_MAXFD = FD_SETSIZE-1;
+static const int MAX_RANDOM_IN = 50000;
+static const double CHANCE_CLOSE = 1.0 / 300;
+static const double CHANCE_INTR = 1.0 / 200;
+static const double CHANCE_READ1 = 0.6;
+static const double CHANCE_READ2 = 0.3;
+static const double CHANCE_WRITE1 = 0.8;
+static const double CHANCE_WRITE2 = 0.3;
+
+struct fdwrap {
+	enum wrapfd_mode mode;
+	buffer *buf;
+};
+
+static struct fdwrap wrap_fds[IOWRAP_MAXFD+1];
+// for quick selection of in-use descriptors
+static int wrap_used[IOWRAP_MAXFD+1];
+static unsigned int nused;
+static unsigned short rand_state[3];
+
+void wrapfd_setup(uint32_t seed) {
+	nused = 0;
+	memset(wrap_fds, 0x0, sizeof(wrap_fds));
+
+	*((uint32_t*)rand_state) = seed;
+	nrand48(rand_state);
+}
+
+void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode) {
+	assert(fd >= 0);
+	assert(fd <= IOWRAP_MAXFD);
+	assert(wrap_fds[fd].mode == UNUSED);
+	assert(buf || mode == RANDOMIN);
+
+	wrap_fds[fd].mode = mode;
+	wrap_fds[fd].buf = buf;
+	wrap_used[nused] = fd;
+
+	nused++;
+}
+
+void wrapfd_remove(int fd) {
+	unsigned int i, j;
+	assert(fd >= 0);
+	assert(fd <= IOWRAP_MAXFD);
+	assert(wrap_fds[fd].mode != UNUSED);
+	wrap_fds[fd].mode = UNUSED;
+
+	// remove from used list
+	for (i = 0, j = 0; i < nused; i++) {
+		if (wrap_used[i] != fd) {
+			wrap_used[j] = wrap_used[i];
+			j++;
+		}
+	}
+	nused--;
+}
+
+
+int wrapfd_read(int fd, void *out, size_t count) {
+	size_t maxread;
+	buffer *buf;
+
+	if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode != UNUSED) {
+		TRACE(("Bad read descriptor %d\n", fd))
+		errno = EBADF;
+		return -1;
+	}
+
+	assert(count != 0);
+
+	if (erand48(rand_state) < CHANCE_CLOSE) {
+		wrapfd_remove(fd);
+		return 0;
+	}
+
+	if (erand48(rand_state) < CHANCE_INTR) {
+		errno = EINTR;
+		return -1;
+	}
+
+	buf = wrap_fds[fd].buf;
+	if (buf) {
+		maxread = MIN(buf->len - buf->pos, count);
+		// returns 0 if buf is EOF, as intended
+		maxread = nrand48(rand_state) % maxread + 1;
+		memcpy(out, buf_getptr(buf, maxread), maxread);
+		buf_incrpos(buf, maxread);
+		return maxread;
+	}
+
+	maxread = MIN(MAX_RANDOM_IN, count);
+	maxread = nrand48(rand_state) % maxread + 1;
+	memset(out, 0xef, maxread);
+	return maxread;
+}
+
+int wrapfd_write(int fd, const void* in, size_t count) {
+	unsigned const volatile char* volin = in;
+	unsigned int i;
+	if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode != UNUSED) {
+		TRACE(("Bad read descriptor %d\n", fd))
+		errno = EBADF;
+		return -1;
+	}
+
+	assert(count != 0);
+
+	// force read to exercise sanitisers
+	for (i = 0; i < count; i++) {
+		(void)volin[i];
+	}
+
+	if (erand48(rand_state) < CHANCE_CLOSE) {
+		wrapfd_remove(fd);
+		return 0;
+	}
+
+	if (erand48(rand_state) < CHANCE_INTR) {
+		errno = EINTR;
+		return -1;
+	}
+
+	return nrand48(rand_state) % (count+1);
+}
+
+int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds, 
+	fd_set *UNUSED(exceptfds), struct timeval *UNUSED(timeout)) {
+	int i, nset;
+	int ret = 0;
+	int fdlist[IOWRAP_MAXFD+1] = {0};
+
+	assert(nfds <= IOWRAP_MAXFD+1);
+
+	if (erand48(rand_state) < CHANCE_INTR) {
+		errno = EINTR;
+		return -1;
+	}
+
+	// read
+	if (erand48(rand_state) < CHANCE_READ1) {
+		for (i = 0, nset = 0; i < nfds; i++) {
+			if (FD_ISSET(i, readfds)) {
+				assert(wrap_fds[i].mode != UNUSED);
+				fdlist[nset] = i;
+			}
+		}
+		FD_ZERO(readfds);
+
+		if (nset > 0) {
+			// set one
+			FD_SET(fdlist[random() % nset], readfds);
+			ret++;
+
+			if (erand48(rand_state) < CHANCE_READ2) {
+				i = fdlist[random() % nset];
+				if (!FD_ISSET(i, readfds)) {
+					FD_SET(i, readfds);
+					ret++;
+				}
+			}
+		}
+	}
+
+	// write
+	if (erand48(rand_state) < CHANCE_WRITE1) {
+		for (i = 0, nset = 0; i < nfds; i++) {
+			if (FD_ISSET(i, writefds)) {
+				assert(wrap_fds[i].mode != UNUSED);
+				fdlist[nset] = i;
+			}
+		}
+		FD_ZERO(writefds);
+
+		// set one
+		if (nset > 0) {
+			FD_SET(fdlist[nrand48(rand_state) % nset], writefds);
+			ret++;
+
+			if (erand48(rand_state) < CHANCE_WRITE2) {
+				i = fdlist[nrand48(rand_state) % nset];
+				if (!FD_ISSET(i, writefds)) {
+					FD_SET(i, writefds);
+					ret++;
+				}
+			}
+		}
+	}
+	return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fuzz-wrapfd.h	Fri May 19 00:48:46 2017 +0800
@@ -0,0 +1,17 @@
+#ifndef FUZZ_WRAPFD_H
+#define FUZZ_WRAPFD_H
+
+#include "buffer.h"
+
+enum wrapfd_mode {
+    UNUSED = 0,
+    PLAIN,
+    INPROGRESS,
+    RANDOMIN,
+};
+
+void wrapfd_setup(uint32_t wrapseed);
+// doesn't take ownership of buf. buf is optional.
+void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode);
+
+#endif // FUZZ_WRAPFD_H
--- a/fuzz.h	Thu May 18 23:45:10 2017 +0800
+++ b/fuzz.h	Fri May 19 00:48:46 2017 +0800
@@ -6,8 +6,12 @@
 
 #ifdef DROPBEAR_FUZZ
 
+// once per process
 void svr_setup_fuzzer(void);
 
+// once per input. returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE
+int fuzzer_set_input(const uint8_t *Data, size_t Size);
+
 struct dropbear_fuzz_options {
     int fuzzing;
 
@@ -15,7 +19,7 @@
     FILE* recordf;
 
     // fuzzing input
-    buffer input;
+    buffer *input;
 
     // dropbear_exit() jumps back
     sigjmp_buf jmp;
--- a/fuzzer-preauth.c	Thu May 18 23:45:10 2017 +0800
+++ b/fuzzer-preauth.c	Fri May 19 00:48:46 2017 +0800
@@ -1,10 +1,10 @@
 #include "fuzz.h"
 #include "dbrandom.h"
 #include "session.h"
+#include "fuzz-wrapfd.h"
 
-static int setup_fuzzer(void) {
+static void setup_fuzzer(void) {
 	svr_setup_fuzzer();
-	return 0;
 }
 
 int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
@@ -14,15 +14,15 @@
 		once = 1;
 	}
 
-	fuzz.input.data = (unsigned char*)Data;
-	fuzz.input.size = Size;
-	fuzz.input.len = Size;
-	fuzz.input.pos = 0;
+	if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) {
+		return 0;
+	}
 
-	seedrandom();
+	int fakesock = 1;
+	wrapfd_add(fakesock, fuzz.input, PLAIN);
 
 	if (setjmp(fuzz.jmp) == 0) {
-		svr_session(-1, -1);
+		svr_session(fakesock, fakesock);
 	} else {
 		// dropbear_exit jumped here
 	}