changeset 1357:08f4fa4dc6a0 fuzz

closer to working
author Matt Johnston <matt@ucc.asn.au>
date Sat, 20 May 2017 13:23:16 +0800
parents 3677a510f545
children 6b89eb92f872
files common-kex.c common-session.c dbrandom.c fuzz-common.c fuzz-harness.c fuzz-wrapfd.c fuzz-wrapfd.h fuzz.h fuzzer-preauth.c includes.h netio.c packet.c
diffstat 12 files changed, 103 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/common-kex.c	Fri May 19 00:48:46 2017 +0800
+++ b/common-kex.c	Sat May 20 13:23:16 2017 +0800
@@ -944,14 +944,9 @@
 	}
 
 #ifdef DROPBEAR_FUZZ
-	ses.newkeys->recv.algo_crypt = &dropbear_nocipher;
-	ses.newkeys->trans.algo_crypt = &dropbear_nocipher;
-	ses.newkeys->recv.crypt_mode = &dropbear_mode_none;
-	ses.newkeys->trans.crypt_mode = &dropbear_mode_none;
-	ses.newkeys->recv.algo_mac = &dropbear_nohash;
-	ses.newkeys->trans.algo_mac = &dropbear_nohash;
-	ses.newkeys->recv.algo_comp = DROPBEAR_COMP_NONE;
-	ses.newkeys->trans.algo_comp = DROPBEAR_COMP_NONE;
+	if (fuzz.fuzzing) {
+		fuzz_kex_fakealgos();
+	}
 #endif
 
 	/* reserved for future extensions */
--- a/common-session.c	Fri May 19 00:48:46 2017 +0800
+++ b/common-session.c	Sat May 20 13:23:16 2017 +0800
@@ -161,7 +161,12 @@
 
 		/* We get woken up when signal handlers write to this pipe.
 		   SIGCHLD in svr-chansession is the only one currently. */
-		FD_SET(ses.signal_pipe[0], &readfd);
+#ifdef DROPBEAR_FUZZ
+		if (!fuzz.fuzzing) 
+#endif
+		{
+			FD_SET(ses.signal_pipe[0], &readfd);
+		}
 		ses.channel_signal_pending = 0;
 
 		/* set up for channels which can be read/written */
--- a/dbrandom.c	Fri May 19 00:48:46 2017 +0800
+++ b/dbrandom.c	Sat May 20 13:23:16 2017 +0800
@@ -28,8 +28,6 @@
 #include "bignum.h"
 #include "dbrandom.h"
 #include "runopts.h"
-#include "fuzz.h"
-
 
 /* this is used to generate unique output from the same hashpool */
 static uint32_t counter = 0;
--- a/fuzz-common.c	Fri May 19 00:48:46 2017 +0800
+++ b/fuzz-common.c	Sat May 20 13:23:16 2017 +0800
@@ -1,7 +1,5 @@
 #include "includes.h"
 
-#ifdef DROPBEAR_FUZZ
-
 #include "includes.h"
 #include "fuzz.h"
 #include "dbutil.h"
@@ -17,6 +15,7 @@
 
 static void common_setup_fuzzer(void) {
     fuzz.fuzzing = 1;
+    fuzz.wrapfds = 1;
     fuzz.input = m_malloc(sizeof(buffer));
     crypto_init();
 }
@@ -30,7 +29,7 @@
 
     // get prefix. input format is
     // string prefix
-    //     uint32_t seed
+    //     uint32 wrapfd seed
     //     ... to be extended later
     // [bytes] ssh input stream
 
@@ -114,4 +113,6 @@
     buf_free(b);
 }
 
-#endif /* DROPBEAR_FUZZ */
+void fuzz_kex_fakealgos(void) {
+    ses.newkeys->recv.crypt_mode = &dropbear_mode_none;
+}
--- a/fuzz-harness.c	Fri May 19 00:48:46 2017 +0800
+++ b/fuzz-harness.c	Sat May 20 13:23:16 2017 +0800
@@ -8,6 +8,10 @@
     int i;
     buffer *input = buf_new(100000);
 
+#if DROPBEAR_TRACE
+    debug_trace = 1;
+#endif
+
     for (i = 1; i < argc; i++) {
         char* fn = argv[i];
         buf_setlen(input, 0);
--- a/fuzz-wrapfd.c	Fri May 19 00:48:46 2017 +0800
+++ b/fuzz-wrapfd.c	Sat May 20 13:23:16 2017 +0800
@@ -1,6 +1,9 @@
+#define FUZZ_SKIP_WRAP 1
 #include "includes.h"
 #include "fuzz-wrapfd.h"
 
+#include "fuzz.h"
+
 static const int IOWRAP_MAXFD = FD_SETSIZE-1;
 static const int MAX_RANDOM_IN = 50000;
 static const double CHANCE_CLOSE = 1.0 / 300;
@@ -22,6 +25,7 @@
 static unsigned short rand_state[3];
 
 void wrapfd_setup(uint32_t seed) {
+	TRACE(("wrapfd_setup %x", seed))
 	nused = 0;
 	memset(wrap_fds, 0x0, sizeof(wrap_fds));
 
@@ -35,6 +39,8 @@
 	assert(wrap_fds[fd].mode == UNUSED);
 	assert(buf || mode == RANDOMIN);
 
+	TRACE(("wrapfd_add %d buf %p mode %d", fd, buf, mode))
+
 	wrap_fds[fd].mode = mode;
 	wrap_fds[fd].buf = buf;
 	wrap_used[nused] = fd;
@@ -49,6 +55,8 @@
 	assert(wrap_fds[fd].mode != UNUSED);
 	wrap_fds[fd].mode = UNUSED;
 
+	TRACE(("wrapfd_remove %d", fd))
+
 	// remove from used list
 	for (i = 0, j = 0; i < nused; i++) {
 		if (wrap_used[i] != fd) {
@@ -64,7 +72,12 @@
 	size_t maxread;
 	buffer *buf;
 
-	if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode != UNUSED) {
+	if (!fuzz.wrapfds) {
+		return read(fd, out, count);
+	}
+
+	if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode == UNUSED) {
+		// XXX - assertion failure?
 		TRACE(("Bad read descriptor %d\n", fd))
 		errno = EBADF;
 		return -1;
@@ -86,7 +99,9 @@
 	if (buf) {
 		maxread = MIN(buf->len - buf->pos, count);
 		// returns 0 if buf is EOF, as intended
-		maxread = nrand48(rand_state) % maxread + 1;
+		if (maxread > 0) {
+			maxread = nrand48(rand_state) % maxread + 1;
+		}
 		memcpy(out, buf_getptr(buf, maxread), maxread);
 		buf_incrpos(buf, maxread);
 		return maxread;
@@ -101,7 +116,13 @@
 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) {
+
+	if (!fuzz.wrapfds) {
+		return write(fd, in, count);
+	}
+
+	if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode == UNUSED) {
+		// XXX - assertion failure?
 		TRACE(("Bad read descriptor %d\n", fd))
 		errno = EBADF;
 		return -1;
@@ -128,11 +149,15 @@
 }
 
 int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds, 
-	fd_set *UNUSED(exceptfds), struct timeval *UNUSED(timeout)) {
-	int i, nset;
+	fd_set *exceptfds, struct timeval *timeout) {
+	int i, nset, sel;
 	int ret = 0;
 	int fdlist[IOWRAP_MAXFD+1] = {0};
 
+	if (!fuzz.wrapfds) {
+		return select(nfds, readfds, writefds, exceptfds, timeout);
+	}
+
 	assert(nfds <= IOWRAP_MAXFD+1);
 
 	if (erand48(rand_state) < CHANCE_INTR) {
@@ -141,24 +166,26 @@
 	}
 
 	// read
-	if (erand48(rand_state) < CHANCE_READ1) {
+	if (readfds != NULL && 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;
+				nset++;
 			}
 		}
 		FD_ZERO(readfds);
 
 		if (nset > 0) {
 			// set one
-			FD_SET(fdlist[random() % nset], readfds);
+			sel = fdlist[nrand48(rand_state) % nset];
+			FD_SET(sel, readfds);
 			ret++;
 
 			if (erand48(rand_state) < CHANCE_READ2) {
-				i = fdlist[random() % nset];
-				if (!FD_ISSET(i, readfds)) {
-					FD_SET(i, readfds);
+				sel = fdlist[nrand48(rand_state) % nset];
+				if (!FD_ISSET(sel, readfds)) {
+					FD_SET(sel, readfds);
 					ret++;
 				}
 			}
@@ -166,24 +193,26 @@
 	}
 
 	// write
-	if (erand48(rand_state) < CHANCE_WRITE1) {
+	if (writefds != NULL && 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;
+				nset++;
 			}
 		}
 		FD_ZERO(writefds);
 
 		// set one
 		if (nset > 0) {
-			FD_SET(fdlist[nrand48(rand_state) % nset], writefds);
+			sel = fdlist[nrand48(rand_state) % nset];
+			FD_SET(sel, writefds);
 			ret++;
 
 			if (erand48(rand_state) < CHANCE_WRITE2) {
-				i = fdlist[nrand48(rand_state) % nset];
-				if (!FD_ISSET(i, writefds)) {
-					FD_SET(i, writefds);
+				sel = fdlist[nrand48(rand_state) % nset];
+				if (!FD_ISSET(sel, writefds)) {
+					FD_SET(sel, writefds);
 					ret++;
 				}
 			}
@@ -191,3 +220,4 @@
 	}
 	return ret;
 }
+
--- a/fuzz-wrapfd.h	Fri May 19 00:48:46 2017 +0800
+++ b/fuzz-wrapfd.h	Sat May 20 13:23:16 2017 +0800
@@ -14,4 +14,10 @@
 // doesn't take ownership of buf. buf is optional.
 void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode);
 
+// called via #defines for read/write/select
+int wrapfd_read(int fd, void *out, size_t count);
+int wrapfd_write(int fd, const void* in, size_t count);
+int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds, 
+    fd_set *exceptfds, struct timeval *timeout);
+
 #endif // FUZZ_WRAPFD_H
--- a/fuzz.h	Fri May 19 00:48:46 2017 +0800
+++ b/fuzz.h	Sat May 20 13:23:16 2017 +0800
@@ -1,10 +1,13 @@
 #ifndef DROPBEAR_FUZZ_H
 #define DROPBEAR_FUZZ_H
 
+#include "config.h"
+#ifdef DROPBEAR_FUZZ
+
 #include "includes.h"
 #include "buffer.h"
-
-#ifdef DROPBEAR_FUZZ
+#include "algo.h"
+#include "fuzz-wrapfd.h"
 
 // once per process
 void svr_setup_fuzzer(void);
@@ -12,6 +15,16 @@
 // once per input. returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE
 int fuzzer_set_input(const uint8_t *Data, size_t Size);
 
+void fuzz_kex_fakealgos(void);
+
+// fake IO wrappers
+#ifndef FUZZ_SKIP_WRAP
+#define select(nfds, readfds, writefds, exceptfds, timeout) \
+        wrapfd_select(nfds, readfds, writefds, exceptfds, timeout)
+#define write(fd, buf, count) wrapfd_write(fd, buf, count)
+#define read(fd, buf, count) wrapfd_read(fd, buf, count)
+#endif // FUZZ_SKIP_WRAP
+
 struct dropbear_fuzz_options {
     int fuzzing;
 
@@ -20,6 +33,9 @@
 
     // fuzzing input
     buffer *input;
+    struct dropbear_cipher recv_cipher;
+    struct dropbear_hash recv_mac;
+    int wrapfds;
 
     // dropbear_exit() jumps back
     sigjmp_buf jmp;
@@ -34,6 +50,6 @@
 
 extern struct dropbear_fuzz_options fuzz;
 
-#endif
+#endif // DROPBEAR_FUZZ
 
 #endif /* DROPBEAR_FUZZ_H */
--- a/fuzzer-preauth.c	Fri May 19 00:48:46 2017 +0800
+++ b/fuzzer-preauth.c	Sat May 20 13:23:16 2017 +0800
@@ -24,6 +24,7 @@
 	if (setjmp(fuzz.jmp) == 0) {
 		svr_session(fakesock, fakesock);
 	} else {
+		TRACE(("dropbear_exit longjmped"))
 		// dropbear_exit jumped here
 	}
 
--- a/includes.h	Fri May 19 00:48:46 2017 +0800
+++ b/includes.h	Sat May 20 13:23:16 2017 +0800
@@ -133,7 +133,6 @@
 #include <tommath.h>
 #endif
 
-
 #include "compat.h"
 
 #ifndef HAVE_U_INT8_T
@@ -164,6 +163,8 @@
 
 #include "fake-rfc2553.h"
 
+#include "fuzz.h"
+
 #ifndef LOG_AUTHPRIV
 #define LOG_AUTHPRIV LOG_AUTH
 #endif
--- a/netio.c	Fri May 19 00:48:46 2017 +0800
+++ b/netio.c	Sat May 20 13:23:16 2017 +0800
@@ -195,6 +195,7 @@
 		}
 		iter = next_iter;
 	}
+	TRACE(("leave set_connect_fds"))
 }
 
 void handle_connect_fds(fd_set *writefd) {
--- a/packet.c	Fri May 19 00:48:46 2017 +0800
+++ b/packet.c	Sat May 20 13:23:16 2017 +0800
@@ -36,7 +36,6 @@
 #include "channel.h"
 #include "netio.h"
 #include "runopts.h"
-#include "fuzz.h"
 
 static int read_packet_init(void);
 static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
@@ -371,6 +370,17 @@
 	buf_setpos(ses.readbuf, 0);
 	make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes);
 
+#ifdef DROPBEAR_FUZZ
+	if (fuzz.fuzzing) {
+		// fail 1 in 1000 times to test error path
+		unsigned int value = *((unsigned int*)&mac_bytes);
+		if (value % 1000 == 0) {
+			return DROPBEAR_FAILURE;
+		}
+		return DROPBEAR_SUCCESS;
+	}
+#endif
+
 	/* compare the hash */
 	buf_setpos(ses.readbuf, contents_len);
 	if (constant_time_memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) {