From cc1b07dcf170890c34c43f247334dbc78d6f8647 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Thu, 15 Oct 2020 22:46:24 +0800
Subject: [PATCH] Make wrapfd share a common buffer for all FDs

--HG--
branch : fuzz
---
 dbutil.c       |  7 +++++-
 fuzz-common.c  | 39 +++++++++++++++++++++++++++++---
 fuzz-harness.c |  1 -
 fuzz-wrapfd.c  | 61 ++++++++++++++++++++------------------------------
 fuzz-wrapfd.h  | 10 ++++-----
 fuzz.h         |  1 +
 6 files changed, 71 insertions(+), 48 deletions(-)

diff --git a/dbutil.c b/dbutil.c
index 32920f7..5af6330 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -121,7 +121,6 @@ static void generic_dropbear_exit(int exitcode, const char* format,
 	_dropbear_log(LOG_INFO, fmtbuf, param);
 
 #if DROPBEAR_FUZZ
-	/* longjmp before cleaning up svr_opts */
     if (fuzz.do_jmp) {
         longjmp(fuzz.jmp, 1);
     }
@@ -258,6 +257,12 @@ int spawn_command(void(*exec_fn)(const void *user_data), const void *exec_data,
 	const int FDIN = 0;
 	const int FDOUT = 1;
 
+#if DROPBEAR_FUZZ
+	if (fuzz.fuzzing) {
+		return fuzz_spawn_command(ret_writefd, ret_readfd, ret_errfd, ret_pid);
+	}
+#endif
+
 	/* redirect stdin/stdout/stderr */
 	if (pipe(infds) != 0) {
 		return DROPBEAR_FAILURE;
diff --git a/fuzz-common.c b/fuzz-common.c
index c14fa66..d099bff 100644
--- a/fuzz-common.c
+++ b/fuzz-common.c
@@ -36,7 +36,7 @@ int fuzz_set_input(const uint8_t *Data, size_t Size) {
 
     memset(&ses, 0x0, sizeof(ses));
     memset(&svr_ses, 0x0, sizeof(svr_ses));
-    wrapfd_setup();
+    wrapfd_setup(fuzz.input);
 
     fuzz_seed();
 
@@ -78,6 +78,29 @@ void fuzz_svr_setup(void) {
     load_fixed_hostkeys();
 }
 
+#if 0
+void fuzz_cli_setup(void) {
+    fuzz_common_setup();
+    
+    _dropbear_exit = cli_dropbear_exit;
+
+    char *argv[] = { 
+        "-E", 
+    };
+
+    int argc = sizeof(argv) / sizeof(*argv);
+    cli_getopts(argc, argv);
+
+    /* user lookups might be slow, cache it */
+    fuzz.pw_name = m_strdup("person");
+    fuzz.pw_dir = m_strdup("/tmp");
+    fuzz.pw_shell = m_strdup("/bin/zsh");
+    fuzz.pw_passwd = m_strdup("!!zzznope");
+
+    load_fixed_hostkeys();
+}
+#endif
+
 static void load_fixed_hostkeys(void) {
 #include "fuzz-hostkeys.c"   
 
@@ -151,6 +174,17 @@ void fuzz_fake_send_kexdh_reply(void) {
     finish_kexhashbuf();
 }
 
+/* fake version of spawn_command() */
+int fuzz_spawn_command(int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
+    *ret_writefd = wrapfd_new();
+    *ret_readfd = wrapfd_new();
+    if (ret_errfd) {
+        *ret_errfd = wrapfd_new();
+    }
+    ret_pid = 999;
+    return DROPBEAR_SUCCESS;
+}
+
 int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
     static int once = 0;
     if (!once) {
@@ -182,8 +216,7 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
     uint32_t wrapseed = buf_getint(fuzz.input);
     wrapfd_setseed(wrapseed);
 
-    int fakesock = 20;
-    wrapfd_add(fakesock, fuzz.input, PLAIN);
+    int fakesock = wrapfd_new();
 
     m_malloc_set_epoch(1);
     if (setjmp(fuzz.jmp) == 0) {
diff --git a/fuzz-harness.c b/fuzz-harness.c
index be23d4e..eabc58a 100644
--- a/fuzz-harness.c
+++ b/fuzz-harness.c
@@ -9,7 +9,6 @@ int main(int argc, char ** argv) {
     buffer *input = buf_new(100000);
 
     for (i = 1; i < argc; i++) {
-        printf("arg %s\n", argv[i]);
 #if DEBUG_TRACE
         if (strcmp(argv[i], "-v") == 0) {
             debug_trace = 1;
diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c
index ed8968a..431c524 100644
--- a/fuzz-wrapfd.c
+++ b/fuzz-wrapfd.c
@@ -7,6 +7,8 @@
 #include "fuzz.h"
 
 #define IOWRAP_MAXFD (FD_SETSIZE-1)
+// hopefully above any real fd...
+static const int WRAPFD_STARTFD = 400;
 static const int MAX_RANDOM_IN = 50000;
 static const double CHANCE_CLOSE = 1.0 / 600;
 static const double CHANCE_INTR = 1.0 / 900;
@@ -17,25 +19,21 @@ static const double CHANCE_WRITE2 = 0.5;
 
 struct fdwrap {
 	enum wrapfd_mode mode;
-	buffer *buf;
 	int closein;
 	int closeout;
 };
 
 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];
+static buffer *input_buf;
 
-void wrapfd_setup(void) {
+void wrapfd_setup(buffer *buf) {
 	TRACE(("wrapfd_setup"))
-	nused = 0;
 	memset(wrap_fds, 0x0, sizeof(wrap_fds));
-	memset(wrap_used, 0x0, sizeof(wrap_used));
 
 	memset(rand_state, 0x0, sizeof(rand_state));
 	wrapfd_setseed(50);
+	input_buf = buf;
 }
 
 void wrapfd_setseed(uint32_t seed) {
@@ -43,39 +41,29 @@ void wrapfd_setseed(uint32_t seed) {
 	nrand48(rand_state);
 }
 
-void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode) {
-	TRACE(("wrapfd_add %d buf %p mode %d", fd, buf, 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_fds[fd].closein = 0;
-	wrap_fds[fd].closeout = 0;
-	wrap_used[nused] = fd;
-
-	nused++;
+int wrapfd_new() {
+	int fd;
+	// Find a spare file descriptor to use
+	for (fd = WRAPFD_STARTFD; fd < IOWRAP_MAXFD; fd++) {
+		if (wrap_fds[fd].mode == UNUSED) {
+			// check real file descriptors haven't got as far as WRAPFD_STARTFD
+			assert(close(fd) == -1 && errno == EBADF);
+			wrap_fds[fd].mode = COMMONBUF;
+			wrap_fds[fd].closein = 0;
+			wrap_fds[fd].closeout = 0;
+			return fd;
+		}
+	}
+	errno = EMFILE;
+	return -1;
 }
 
 void wrapfd_remove(int fd) {
-	unsigned int i, j;
 	TRACE(("wrapfd_remove %d", fd))
 	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_close(int fd) {
@@ -115,15 +103,14 @@ int wrapfd_read(int fd, void *out, size_t count) {
 		return -1;
 	}
 
-	buf = wrap_fds[fd].buf;
-	if (buf) {
-		maxread = MIN(buf->len - buf->pos, count);
+	if (input_buf) {
+		maxread = MIN(input_buf->len - input_buf->pos, count);
 		/* returns 0 if buf is EOF, as intended */
 		if (maxread > 0) {
 			maxread = nrand48(rand_state) % maxread + 1;
 		}
-		memcpy(out, buf_getptr(buf, maxread), maxread);
-		buf_incrpos(buf, maxread);
+		memcpy(out, buf_getptr(input_buf, maxread), maxread);
+		buf_incrpos(input_buf, maxread);
 		return maxread;
 	}
 
diff --git a/fuzz-wrapfd.h b/fuzz-wrapfd.h
index 7aed43a..60c66a7 100644
--- a/fuzz-wrapfd.h
+++ b/fuzz-wrapfd.h
@@ -5,15 +5,13 @@
 
 enum wrapfd_mode {
     UNUSED = 0,
-    PLAIN,
-    INPROGRESS,
-    RANDOMIN
+    COMMONBUF, // using the common buffer
 };
 
-void wrapfd_setup(void);
+// buf is a common buffer read by all wrapped FDs. doesn't take ownership of buf
+void wrapfd_setup(buffer *buf);
 void wrapfd_setseed(uint32_t seed);
-// doesn't take ownership of buf. buf is optional.
-void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode);
+int wrapfd_new();
 
 // called via #defines for read/write/select
 int wrapfd_read(int fd, void *out, size_t count);
diff --git a/fuzz.h b/fuzz.h
index dab6c37..cc98ed2 100644
--- a/fuzz.h
+++ b/fuzz.h
@@ -31,6 +31,7 @@ void fuzz_seed(void);
 void fuzz_get_socket_address(int fd, char **local_host, char **local_port,
                         char **remote_host, char **remote_port, int host_lookup);
 void fuzz_fake_send_kexdh_reply(void);
+int fuzz_spawn_command(int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid);
 
 // fake IO wrappers
 #ifndef FUZZ_SKIP_WRAP