Merge fuzz branch

This commit is contained in:
Matt Johnston 2020-10-18 22:53:44 +08:00
commit 6ca24af24a
14 changed files with 244 additions and 130 deletions

View File

@ -268,7 +268,8 @@ lint:
## Fuzzing targets ## Fuzzing targets
# list of fuzz targets # list of fuzz targets
FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths fuzzer-kexdh fuzzer-kexecdh fuzzer-kexcurve25519 FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths \
fuzzer-kexdh fuzzer-kexecdh fuzzer-kexcurve25519 fuzzer-client fuzzer-client_nomaths
FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS)) FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS))
@ -279,10 +280,7 @@ list-fuzz-targets:
fuzzstandalone: FUZZLIB=fuzz-harness.o fuzzstandalone: FUZZLIB=fuzz-harness.o
fuzzstandalone: fuzz-harness.o fuzz-targets fuzzstandalone: fuzz-harness.o fuzz-targets
# exclude svr-main.o to avoid duplicate main fuzz-harness.o: $(HEADERS) $(LIBTOM_DEPS) Makefile $(allobjs) fuzz-common.o
svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs))
fuzz-harness.o: $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) fuzz-common.o
# build all the fuzzers. This will require fail to link unless built with # build all the fuzzers. This will require fail to link unless built with
# make fuzz-targets FUZZLIB=-lFuzzer.a # make fuzz-targets FUZZLIB=-lFuzzer.a
@ -290,25 +288,31 @@ fuzz-harness.o: $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) fuzz-common.o
fuzz-targets: $(FUZZ_TARGETS) $(FUZZER_OPTIONS) fuzz-targets: $(FUZZ_TARGETS) $(FUZZER_OPTIONS)
fuzzer-preauth: fuzzer-preauth.o fuzz-harness.o fuzzer-preauth: fuzzer-preauth.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-preauth_nomaths: fuzzer-preauth_nomaths.o fuzz-harness.o fuzzer-preauth_nomaths: fuzzer-preauth_nomaths.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-pubkey: fuzzer-pubkey.o fuzz-harness.o fuzzer-pubkey: fuzzer-pubkey.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-verify: fuzzer-verify.o fuzz-harness.o fuzzer-verify: fuzzer-verify.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-kexdh: fuzzer-kexdh.o fuzz-harness.o fuzzer-kexdh: fuzzer-kexdh.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-kexecdh: fuzzer-kexecdh.o fuzz-harness.o fuzzer-kexecdh: fuzzer-kexecdh.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-kexcurve25519: fuzzer-kexcurve25519.o fuzz-harness.o fuzzer-kexcurve25519: fuzzer-kexcurve25519.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-client: fuzzer-client.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-client_nomaths: fuzzer-client_nomaths.o fuzz-harness.o
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
fuzzer-%.options: Makefile fuzzer-%.options: Makefile
echo "[libfuzzer]" > $@ echo "[libfuzzer]" > $@

View File

@ -46,6 +46,13 @@ void send_msg_kexdh_init() {
TRACE(("send_msg_kexdh_init()")) TRACE(("send_msg_kexdh_init()"))
CHECKCLEARTOWRITE(); CHECKCLEARTOWRITE();
#if DROPBEAR_FUZZ
if (fuzz.fuzzing && fuzz.skip_kexmaths) {
return;
}
#endif
buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT); buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT);
switch (ses.newkeys->algo_kex->mode) { switch (ses.newkeys->algo_kex->mode) {
#if DROPBEAR_NORMAL_DH #if DROPBEAR_NORMAL_DH
@ -99,6 +106,12 @@ void recv_msg_kexdh_reply() {
TRACE(("enter recv_msg_kexdh_reply")) TRACE(("enter recv_msg_kexdh_reply"))
#if DROPBEAR_FUZZ
if (fuzz.fuzzing && fuzz.skip_kexmaths) {
return;
}
#endif
if (cli_ses.kex_state != KEXDH_INIT_SENT) { if (cli_ses.kex_state != KEXDH_INIT_SENT) {
dropbear_exit("Received out-of-order kexdhreply"); dropbear_exit("Received out-of-order kexdhreply");
} }

View File

@ -31,9 +31,7 @@
#include "dbrandom.h" #include "dbrandom.h"
#include "crypto_desc.h" #include "crypto_desc.h"
#include "netio.h" #include "netio.h"
#include "fuzz.h"
static void cli_dropbear_exit(int exitcode, const char* format, va_list param) ATTRIB_NORETURN;
static void cli_dropbear_log(int priority, const char* format, va_list param);
#if DROPBEAR_CLI_PROXYCMD #if DROPBEAR_CLI_PROXYCMD
static void cli_proxy_cmd(int *sock_in, int *sock_out, pid_t *pid_out); static void cli_proxy_cmd(int *sock_in, int *sock_out, pid_t *pid_out);
@ -98,58 +96,6 @@ int main(int argc, char ** argv) {
} }
#endif /* DBMULTI stuff */ #endif /* DBMULTI stuff */
static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
char exitmsg[150];
char fullmsg[300];
/* Note that exit message must be rendered before session cleanup */
/* Render the formatted exit message */
vsnprintf(exitmsg, sizeof(exitmsg), format, param);
TRACE(("Exited, cleaning up: %s", exitmsg))
/* Add the prefix depending on session/auth state */
if (!ses.init_done) {
snprintf(fullmsg, sizeof(fullmsg), "Exited: %s", exitmsg);
} else {
snprintf(fullmsg, sizeof(fullmsg),
"Connection to %s@%s:%s exited: %s",
cli_opts.username, cli_opts.remotehost,
cli_opts.remoteport, exitmsg);
}
/* Do the cleanup first, since then the terminal will be reset */
session_cleanup();
/* Avoid printing onwards from terminal cruft */
fprintf(stderr, "\n");
dropbear_log(LOG_INFO, "%s", fullmsg);
exit(exitcode);
}
static void cli_dropbear_log(int priority,
const char* format, va_list param) {
char printbuf[1024];
const char *name;
name = cli_opts.progname;
if (!name) {
name = "dbclient";
}
vsnprintf(printbuf, sizeof(printbuf), format, param);
#ifndef DISABLE_SYSLOG
if (opts.usingsyslog) {
syslog(priority, "%s", printbuf);
}
#endif
fprintf(stderr, "%s: %s\n", name, printbuf);
fflush(stderr);
}
static void exec_proxy_cmd(const void *user_data_cmd) { static void exec_proxy_cmd(const void *user_data_cmd) {
const char *cmd = user_data_cmd; const char *cmd = user_data_cmd;
char *usershell; char *usershell;
@ -199,4 +145,5 @@ static void kill_proxy_sighandler(int UNUSED(signo)) {
kill_proxy_command(); kill_proxy_command();
_exit(1); _exit(1);
} }
#endif /* DROPBEAR_CLI_PROXYCMD */ #endif /* DROPBEAR_CLI_PROXYCMD */

View File

@ -352,6 +352,11 @@ static void cli_session_cleanup(void) {
(void)fcntl(cli_ses.stdoutcopy, F_SETFL, cli_ses.stdoutflags); (void)fcntl(cli_ses.stdoutcopy, F_SETFL, cli_ses.stdoutflags);
(void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags); (void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
/* Don't leak */
m_close(cli_ses.stdincopy);
m_close(cli_ses.stdoutcopy);
m_close(cli_ses.stderrcopy);
cli_tty_cleanup(); cli_tty_cleanup();
if (cli_ses.server_sig_algs) { if (cli_ses.server_sig_algs) {
buf_free(cli_ses.server_sig_algs); buf_free(cli_ses.server_sig_algs);
@ -407,3 +412,63 @@ static void recv_msg_global_request_cli(void) {
/* Send a proper rejection */ /* Send a proper rejection */
send_msg_request_failure(); send_msg_request_failure();
} }
void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
char exitmsg[150];
char fullmsg[300];
/* Note that exit message must be rendered before session cleanup */
/* Render the formatted exit message */
vsnprintf(exitmsg, sizeof(exitmsg), format, param);
TRACE(("Exited, cleaning up: %s", exitmsg))
/* Add the prefix depending on session/auth state */
if (!ses.init_done) {
snprintf(fullmsg, sizeof(fullmsg), "Exited: %s", exitmsg);
} else {
snprintf(fullmsg, sizeof(fullmsg),
"Connection to %s@%s:%s exited: %s",
cli_opts.username, cli_opts.remotehost,
cli_opts.remoteport, exitmsg);
}
/* Do the cleanup first, since then the terminal will be reset */
session_cleanup();
#if DROPBEAR_FUZZ
if (fuzz.do_jmp) {
longjmp(fuzz.jmp, 1);
}
#endif
/* Avoid printing onwards from terminal cruft */
fprintf(stderr, "\n");
dropbear_log(LOG_INFO, "%s", fullmsg);
exit(exitcode);
}
void cli_dropbear_log(int priority, const char* format, va_list param) {
char printbuf[1024];
const char *name;
name = cli_opts.progname;
if (!name) {
name = "dbclient";
}
vsnprintf(printbuf, sizeof(printbuf), format, param);
#ifndef DISABLE_SYSLOG
if (opts.usingsyslog) {
syslog(priority, "%s", printbuf);
}
#endif
fprintf(stderr, "%s: %s\n", name, printbuf);
fflush(stderr);
}

View File

@ -487,6 +487,12 @@ void recv_msg_kexinit() {
TRACE(("continue recv_msg_kexinit: sent kexinit")) TRACE(("continue recv_msg_kexinit: sent kexinit"))
} }
/* "Once a party has sent a SSH_MSG_KEXINIT message ...
further SSH_MSG_KEXINIT messages MUST NOT be sent" */
if (ses.kexstate.recvkexinit) {
dropbear_exit("Unexpected KEXINIT");
}
/* start the kex hash */ /* start the kex hash */
local_ident_len = strlen(LOCAL_IDENT); local_ident_len = strlen(LOCAL_IDENT);
remote_ident_len = strlen(ses.remoteident); remote_ident_len = strlen(ses.remoteident);

View File

@ -121,7 +121,6 @@ static void generic_dropbear_exit(int exitcode, const char* format,
_dropbear_log(LOG_INFO, fmtbuf, param); _dropbear_log(LOG_INFO, fmtbuf, param);
#if DROPBEAR_FUZZ #if DROPBEAR_FUZZ
/* longjmp before cleaning up svr_opts */
if (fuzz.do_jmp) { if (fuzz.do_jmp) {
longjmp(fuzz.jmp, 1); 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 FDIN = 0;
const int FDOUT = 1; 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 */ /* redirect stdin/stdout/stderr */
if (pipe(infds) != 0) { if (pipe(infds) != 0) {
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;

View File

@ -16,6 +16,7 @@ static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list
static void load_fixed_hostkeys(void); static void load_fixed_hostkeys(void);
void fuzz_common_setup(void) { void fuzz_common_setup(void) {
disallow_core();
fuzz.fuzzing = 1; fuzz.fuzzing = 1;
fuzz.wrapfds = 1; fuzz.wrapfds = 1;
fuzz.do_jmp = 1; fuzz.do_jmp = 1;
@ -36,7 +37,8 @@ int fuzz_set_input(const uint8_t *Data, size_t Size) {
memset(&ses, 0x0, sizeof(ses)); memset(&ses, 0x0, sizeof(ses));
memset(&svr_ses, 0x0, sizeof(svr_ses)); memset(&svr_ses, 0x0, sizeof(svr_ses));
wrapfd_setup(); memset(&cli_ses, 0x0, sizeof(cli_ses));
wrapfd_setup(fuzz.input);
fuzz_seed(); fuzz_seed();
@ -63,21 +65,32 @@ void fuzz_svr_setup(void) {
_dropbear_exit = svr_dropbear_exit; _dropbear_exit = svr_dropbear_exit;
char *argv[] = { char *argv[] = {
"dropbear",
"-E", "-E",
}; };
int argc = sizeof(argv) / sizeof(*argv); int argc = sizeof(argv) / sizeof(*argv);
svr_getopts(argc, argv); svr_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(); load_fixed_hostkeys();
} }
void fuzz_cli_setup(void) {
fuzz_common_setup();
_dropbear_exit = cli_dropbear_exit;
_dropbear_log = cli_dropbear_log;
char *argv[] = {
"dbclient",
"-y",
"localhost",
};
int argc = sizeof(argv) / sizeof(*argv);
cli_getopts(argc, argv);
}
static void load_fixed_hostkeys(void) { static void load_fixed_hostkeys(void) {
#include "fuzz-hostkeys.c" #include "fuzz-hostkeys.c"
@ -151,6 +164,17 @@ void fuzz_fake_send_kexdh_reply(void) {
finish_kexhashbuf(); 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) { int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
static int once = 0; static int once = 0;
if (!once) { if (!once) {
@ -164,7 +188,7 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
} }
/* /*
get prefix. input format is get prefix, allowing for future extensibility. input format is
string prefix string prefix
uint32 wrapfd seed uint32 wrapfd seed
... to be extended later ... to be extended later
@ -182,8 +206,7 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
uint32_t wrapseed = buf_getint(fuzz.input); uint32_t wrapseed = buf_getint(fuzz.input);
wrapfd_setseed(wrapseed); wrapfd_setseed(wrapseed);
int fakesock = 20; int fakesock = wrapfd_new();
wrapfd_add(fakesock, fuzz.input, PLAIN);
m_malloc_set_epoch(1); m_malloc_set_epoch(1);
if (setjmp(fuzz.jmp) == 0) { if (setjmp(fuzz.jmp) == 0) {
@ -198,6 +221,52 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
return 0; return 0;
} }
int fuzz_run_client(const uint8_t *Data, size_t Size, int skip_kexmaths) {
static int once = 0;
if (!once) {
fuzz_cli_setup();
fuzz.skip_kexmaths = skip_kexmaths;
once = 1;
}
if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
return 0;
}
/*
get prefix, allowing for future extensibility. input format is
string prefix
uint32 wrapfd seed
... to be extended later
[bytes] ssh input stream
*/
/* be careful to avoid triggering buffer.c assertions */
if (fuzz.input->len < 8) {
return 0;
}
size_t prefix_size = buf_getint(fuzz.input);
if (prefix_size != 4) {
return 0;
}
uint32_t wrapseed = buf_getint(fuzz.input);
wrapfd_setseed(wrapseed);
int fakesock = wrapfd_new();
m_malloc_set_epoch(1);
if (setjmp(fuzz.jmp) == 0) {
cli_session(fakesock, fakesock, NULL, 0);
m_malloc_free_epoch(1, 0);
} else {
m_malloc_free_epoch(1, 1);
TRACE(("dropbear_exit longjmped"))
/* dropbear_exit jumped here */
}
return 0;
}
const void* fuzz_get_algo(const algo_type *algos, const char* name) { const void* fuzz_get_algo(const algo_type *algos, const char* name) {
const algo_type *t; const algo_type *t;
for (t = algos; t->name; t++) { for (t = algos; t->name; t++) {

View File

@ -9,7 +9,6 @@ int main(int argc, char ** argv) {
buffer *input = buf_new(100000); buffer *input = buf_new(100000);
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
printf("arg %s\n", argv[i]);
#if DEBUG_TRACE #if DEBUG_TRACE
if (strcmp(argv[i], "-v") == 0) { if (strcmp(argv[i], "-v") == 0) {
debug_trace = 1; debug_trace = 1;
@ -30,6 +29,7 @@ int main(int argc, char ** argv) {
buf_readfile(input, fn); buf_readfile(input, fn);
buf_setpos(input, 0); buf_setpos(input, 0);
/* Run twice to catch problems with statefulness */
fuzz.wrapfds = old_fuzz_wrapfds; fuzz.wrapfds = old_fuzz_wrapfds;
printf("Running %s once \n", fn); printf("Running %s once \n", fn);
LLVMFuzzerTestOneInput(input->data, input->len); LLVMFuzzerTestOneInput(input->data, input->len);

View File

@ -17,25 +17,33 @@ static const double CHANCE_WRITE2 = 0.5;
struct fdwrap { struct fdwrap {
enum wrapfd_mode mode; enum wrapfd_mode mode;
buffer *buf;
int closein; int closein;
int closeout; int closeout;
}; };
static struct fdwrap wrap_fds[IOWRAP_MAXFD+1]; static struct fdwrap wrap_fds[IOWRAP_MAXFD+1] = {0};
/* for quick selection of in-use descriptors */ static int wrapfd_maxfd = -1;
static int wrap_used[IOWRAP_MAXFD+1];
static unsigned int nused;
static unsigned short rand_state[3]; static unsigned short rand_state[3];
static buffer *input_buf;
static int devnull_fd = -1;
void wrapfd_setup(void) { static void wrapfd_remove(int fd);
void wrapfd_setup(buffer *buf) {
TRACE(("wrapfd_setup")) TRACE(("wrapfd_setup"))
nused = 0;
memset(wrap_fds, 0x0, sizeof(wrap_fds)); // clean old ones
memset(wrap_used, 0x0, sizeof(wrap_used)); int i;
for (i = 0; i <= wrapfd_maxfd; i++) {
if (wrap_fds[i].mode == COMMONBUF) {
wrapfd_remove(i);
}
}
wrapfd_maxfd = -1;
memset(rand_state, 0x0, sizeof(rand_state)); memset(rand_state, 0x0, sizeof(rand_state));
wrapfd_setseed(50); wrapfd_setseed(50);
input_buf = buf;
} }
void wrapfd_setseed(uint32_t seed) { void wrapfd_setseed(uint32_t seed) {
@ -43,39 +51,30 @@ void wrapfd_setseed(uint32_t seed) {
nrand48(rand_state); nrand48(rand_state);
} }
void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode) { int wrapfd_new() {
TRACE(("wrapfd_add %d buf %p mode %d", fd, buf, mode)) if (devnull_fd == -1) {
assert(fd >= 0); devnull_fd = open("/dev/null", O_RDONLY);
assert(fd <= IOWRAP_MAXFD); assert(devnull_fd != -1);
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++;
} }
void wrapfd_remove(int fd) { int fd = dup(devnull_fd);
unsigned int i, j; assert(fd != -1);
assert(wrap_fds[fd].mode == UNUSED);
wrap_fds[fd].mode = COMMONBUF;
wrap_fds[fd].closein = 0;
wrap_fds[fd].closeout = 0;
wrapfd_maxfd = MAX(fd, wrapfd_maxfd);
return fd;
}
static void wrapfd_remove(int fd) {
TRACE(("wrapfd_remove %d", fd)) TRACE(("wrapfd_remove %d", fd))
assert(fd >= 0); assert(fd >= 0);
assert(fd <= IOWRAP_MAXFD); assert(fd <= IOWRAP_MAXFD);
assert(wrap_fds[fd].mode != UNUSED); assert(wrap_fds[fd].mode != UNUSED);
wrap_fds[fd].mode = UNUSED; wrap_fds[fd].mode = UNUSED;
m_close(fd);
/* 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) { int wrapfd_close(int fd) {
@ -115,15 +114,14 @@ int wrapfd_read(int fd, void *out, size_t count) {
return -1; return -1;
} }
buf = wrap_fds[fd].buf; if (input_buf) {
if (buf) { maxread = MIN(input_buf->len - input_buf->pos, count);
maxread = MIN(buf->len - buf->pos, count);
/* returns 0 if buf is EOF, as intended */ /* returns 0 if buf is EOF, as intended */
if (maxread > 0) { if (maxread > 0) {
maxread = nrand48(rand_state) % maxread + 1; maxread = nrand48(rand_state) % maxread + 1;
} }
memcpy(out, buf_getptr(buf, maxread), maxread); memcpy(out, buf_getptr(input_buf, maxread), maxread);
buf_incrpos(buf, maxread); buf_incrpos(input_buf, maxread);
return maxread; return maxread;
} }
@ -175,8 +173,6 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds,
int ret = 0; int ret = 0;
int fdlist[IOWRAP_MAXFD+1]; int fdlist[IOWRAP_MAXFD+1];
memset(fdlist, 0x0, sizeof(fdlist));
if (!fuzz.wrapfds) { if (!fuzz.wrapfds) {
return select(nfds, readfds, writefds, exceptfds, timeout); return select(nfds, readfds, writefds, exceptfds, timeout);
} }

View File

@ -5,15 +5,13 @@
enum wrapfd_mode { enum wrapfd_mode {
UNUSED = 0, UNUSED = 0,
PLAIN, COMMONBUF, // using the common buffer
INPROGRESS,
RANDOMIN
}; };
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); void wrapfd_setseed(uint32_t seed);
// doesn't take ownership of buf. buf is optional. int wrapfd_new();
void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode);
// called via #defines for read/write/select // called via #defines for read/write/select
int wrapfd_read(int fd, void *out, size_t count); int wrapfd_read(int fd, void *out, size_t count);

11
fuzz.h
View File

@ -13,6 +13,7 @@
// once per process // once per process
void fuzz_common_setup(void); void fuzz_common_setup(void);
void fuzz_svr_setup(void); void fuzz_svr_setup(void);
void fuzz_cli_setup(void);
// must be called once per fuzz iteration. // must be called once per fuzz iteration.
// returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE // returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE
@ -28,9 +29,12 @@ int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
const unsigned char* keyblob, unsigned int keybloblen); const unsigned char* keyblob, unsigned int keybloblen);
extern const char * const * fuzz_signkey_names; extern const char * const * fuzz_signkey_names;
void fuzz_seed(void); void fuzz_seed(void);
// helpers
void fuzz_get_socket_address(int fd, char **local_host, char **local_port, void fuzz_get_socket_address(int fd, char **local_host, char **local_port,
char **remote_host, char **remote_port, int host_lookup); char **remote_host, char **remote_port, int host_lookup);
void fuzz_fake_send_kexdh_reply(void); 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 // fake IO wrappers
#ifndef FUZZ_SKIP_WRAP #ifndef FUZZ_SKIP_WRAP
@ -56,13 +60,6 @@ struct dropbear_fuzz_options {
// dropbear_exit() jumps back // dropbear_exit() jumps back
int do_jmp; int do_jmp;
sigjmp_buf jmp; sigjmp_buf jmp;
uid_t pw_uid;
gid_t pw_gid;
char* pw_name;
char* pw_dir;
char* pw_shell;
char* pw_passwd;
}; };
extern struct dropbear_fuzz_options fuzz; extern struct dropbear_fuzz_options fuzz;

6
fuzzer-client.c Normal file
View File

@ -0,0 +1,6 @@
#include "fuzz.h"
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
return fuzz_run_client(Data, Size, 0);
}

6
fuzzer-client_nomaths.c Normal file
View File

@ -0,0 +1,6 @@
#include "fuzz.h"
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
return fuzz_run_client(Data, Size, 1);
}

View File

@ -64,6 +64,8 @@ void svr_dropbear_log(int priority, const char* format, va_list param);
/* Client */ /* Client */
void cli_session(int sock_in, int sock_out, struct dropbear_progress_connection *progress, pid_t proxy_cmd_pid) ATTRIB_NORETURN; void cli_session(int sock_in, int sock_out, struct dropbear_progress_connection *progress, pid_t proxy_cmd_pid) ATTRIB_NORETURN;
void cli_connected(int result, int sock, void* userdata, const char *errstring); void cli_connected(int result, int sock, void* userdata, const char *errstring);
void cli_dropbear_exit(int exitcode, const char* format, va_list param) ATTRIB_NORETURN;
void cli_dropbear_log(int priority, const char* format, va_list param);
void cleantext(char* dirtytext); void cleantext(char* dirtytext);
void kill_proxy_command(void); void kill_proxy_command(void);