diff --git a/common-kex.c b/common-kex.c
index ac28fa38d8994ca6f504db4591d1be35d58fbd50..5259fceaa238c1f0ea75437dc89ee7a1aeab62ea 100644
--- a/common-kex.c
+++ b/common-kex.c
@@ -944,14 +944,9 @@ static void read_kex_algos() {
 	}
 
 #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 */
diff --git a/common-session.c b/common-session.c
index 99a54702c637c8ef7d7d490818897923dd19d857..aa97b6516579c49e78192ca36fb4b3459ef6bb6b 100644
--- a/common-session.c
+++ b/common-session.c
@@ -161,7 +161,12 @@ void session_loop(void(*loophandler)()) {
 
 		/* 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 */
diff --git a/dbrandom.c b/dbrandom.c
index f9da8bb0b33ad87d73994b0ad4a4bd334053b8d1..b4b63ccaa6f1bb7f943ac15205290fada19181f0 100644
--- a/dbrandom.c
+++ b/dbrandom.c
@@ -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;
diff --git a/fuzz-common.c b/fuzz-common.c
index bfd2634607cb2f7ab9e3a0cae0b8d3fb3ae65940..cc3d4d64b13b0629f8faae4fc6ffa814f2107a2d 100644
--- a/fuzz-common.c
+++ b/fuzz-common.c
@@ -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 load_fixed_hostkeys(void);
 
 static void common_setup_fuzzer(void) {
     fuzz.fuzzing = 1;
+    fuzz.wrapfds = 1;
     fuzz.input = m_malloc(sizeof(buffer));
     crypto_init();
 }
@@ -30,7 +29,7 @@ int fuzzer_set_input(const uint8_t *Data, size_t Size) {
 
     // 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 @@ static void load_fixed_hostkeys(void) {
     buf_free(b);
 }
 
-#endif /* DROPBEAR_FUZZ */
+void fuzz_kex_fakealgos(void) {
+    ses.newkeys->recv.crypt_mode = &dropbear_mode_none;
+}
diff --git a/fuzz-harness.c b/fuzz-harness.c
index 822d4ac80f9cafc21377a202b2346a6679383a79..e3f661794b0df9a5eb4a70b5b0eaacb1fde7785d 100644
--- a/fuzz-harness.c
+++ b/fuzz-harness.c
@@ -8,6 +8,10 @@ int main(int argc, char ** argv) {
     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);
diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c
index 7509afee46bf9c6897640f62be6eb360503b13c8..62f1c914edccba26809a6ec631545ba59a5a5388 100644
--- a/fuzz-wrapfd.c
+++ b/fuzz-wrapfd.c
@@ -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 int nused;
 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 @@ void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode) {
 	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 @@ void wrapfd_remove(int fd) {
 	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 @@ 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) {
+	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 @@ int wrapfd_read(int fd, void *out, size_t count) {
 	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_read(int fd, void *out, size_t count) {
 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_write(int fd, const void* in, size_t count) {
 }
 
 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 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds,
 	}
 
 	// 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 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds,
 	}
 
 	// 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 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds,
 	}
 	return ret;
 }
+
diff --git a/fuzz-wrapfd.h b/fuzz-wrapfd.h
index d4578b7429a6a9d4a9dff431cf394f250964e643..a73a7fe24bb546f2670e50656e139fe2f8e585c5 100644
--- a/fuzz-wrapfd.h
+++ b/fuzz-wrapfd.h
@@ -14,4 +14,10 @@ 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);
 
+// 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
diff --git a/fuzz.h b/fuzz.h
index c46ded9b234b4da13d12dda2677338934206df89..5a1ab3e7bd8a5a25eb24c4316efa98366cca9678 100644
--- a/fuzz.h
+++ b/fuzz.h
@@ -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 @@ 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);
 
+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 @@ struct dropbear_fuzz_options {
 
     // 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 @@ struct dropbear_fuzz_options {
 
 extern struct dropbear_fuzz_options fuzz;
 
-#endif
+#endif // DROPBEAR_FUZZ
 
 #endif /* DROPBEAR_FUZZ_H */
diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c
index 7564973c800ca5334fbc564c85ded78972e8d549..7a56e6a85e5fe82e37c2b1e246efa64676e578fe 100644
--- a/fuzzer-preauth.c
+++ b/fuzzer-preauth.c
@@ -24,6 +24,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
 	if (setjmp(fuzz.jmp) == 0) {
 		svr_session(fakesock, fakesock);
 	} else {
+		TRACE(("dropbear_exit longjmped"))
 		// dropbear_exit jumped here
 	}
 
diff --git a/includes.h b/includes.h
index e9fe1fdef4d766468f214f19bb746edc71e01d96..b3e13579239dc29183d918a1f71e96c97703538b 100644
--- a/includes.h
+++ b/includes.h
@@ -133,7 +133,6 @@
 #include <tommath.h>
 #endif
 
-
 #include "compat.h"
 
 #ifndef HAVE_U_INT8_T
@@ -164,6 +163,8 @@ typedef u_int32_t uint32_t;
 
 #include "fake-rfc2553.h"
 
+#include "fuzz.h"
+
 #ifndef LOG_AUTHPRIV
 #define LOG_AUTHPRIV LOG_AUTH
 #endif
diff --git a/netio.c b/netio.c
index 17693ecf2b12fc74c584f2b26a2cc8851e4049a1..b482431fb07c1d591608b0f1fafc9b225388d81c 100644
--- a/netio.c
+++ b/netio.c
@@ -195,6 +195,7 @@ void set_connect_fds(fd_set *writefd) {
 		}
 		iter = next_iter;
 	}
+	TRACE(("leave set_connect_fds"))
 }
 
 void handle_connect_fds(fd_set *writefd) {
diff --git a/packet.c b/packet.c
index 235069b0333f64bc57bd8f92487045cedbd7c0ee..a02cb1b00076abc4774761f2e4bd3a4c74a3c127 100644
--- a/packet.c
+++ b/packet.c
@@ -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 @@ static int checkmac() {
 	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) {