From d7471c4f875a2be9dde4e65d06f71bf7c68d5ff5 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 12 May 2017 22:14:49 +0800 Subject: [PATCH 01/56] notsocket changes from afl branch --HG-- branch : fuzz --- netio.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/netio.c b/netio.c index b16ba00..d7a0630 100644 --- a/netio.c +++ b/netio.c @@ -485,14 +485,36 @@ void get_socket_address(int fd, char **local_host, char **local_port, if (local_host || local_port) { addrlen = sizeof(addr); if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) { - dropbear_exit("Failed socket address: %s", strerror(errno)); + if (errno == ENOTSOCK) { + // FUZZ + if (local_host) { + *local_host = m_strdup("notsocket"); + } + if (local_port) { + *local_port = m_strdup("999"); + } + return; + } else { + dropbear_exit("Failed socket address: %s", strerror(errno)); + } } getaddrstring(&addr, local_host, local_port, host_lookup); } if (remote_host || remote_port) { addrlen = sizeof(addr); if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) { - dropbear_exit("Failed socket address: %s", strerror(errno)); + if (errno == ENOTSOCK) { + // FUZZ + if (remote_host) { + *remote_host = m_strdup("notsocket"); + } + if (remote_port) { + *remote_port = m_strdup("999"); + } + return; + } else { + dropbear_exit("Failed socket address: %s", strerror(errno)); + } } getaddrstring(&addr, remote_host, remote_port, host_lookup); } @@ -546,6 +568,18 @@ void getaddrstring(struct sockaddr_storage* addr, return; } else { /* if we can't do a numeric lookup, something's gone terribly wrong */ + if (ret == EAI_FAMILY) { + // FUZZ + // Fake it for non-socket input + if (ret_host) { + *ret_host = m_strdup("0.0.0.0"); + } + if (ret_port) + { + *ret_port = m_strdup("999"); + } + return; + } dropbear_exit("Failed lookup: %s", gai_strerror(ret)); } } From 9f24cdf74c93aa75416687972e69b5b4c8be2698 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 12 May 2017 23:14:54 +0800 Subject: [PATCH 02/56] copy over some fuzzing code from AFL branch --HG-- branch : fuzz --- Makefile.in | 11 +++++ common-kex.c | 11 +++++ configure.ac | 9 ++++ dbrandom.c | 30 ++++++++++++ hostkeys.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ includes.h | 1 + packet.c | 14 ++++++ runopts.h | 23 +++++++++ svr-auth.c | 6 ++- svr-runopts.c | 59 +++++++++++++++++++++++ svr-session.c | 6 +++ 11 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 hostkeys.c diff --git a/Makefile.in b/Makefile.in index d9bfdfa..286e25a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -223,3 +223,14 @@ distclean: clean tidy tidy: -rm -f *~ *.gcov */*~ + +# run this manually for fuzzing. hostkeys.c is checked in. +hostkeys: + dropbearkey -t rsa -f keyr + dropbearkey -t dss -f keyd + dropbearkey -t ecdsa -size 256 -f keye + echo > hostkeys.c + /usr/bin/xxd -i -a keyr >> hostkeys.c + /usr/bin/xxd -i -a keye >> hostkeys.c + /usr/bin/xxd -i -a keyd >> hostkeys.c + diff --git a/common-kex.c b/common-kex.c index 5003d2f..a990cc1 100644 --- a/common-kex.c +++ b/common-kex.c @@ -943,6 +943,17 @@ static void read_kex_algos() { ses.newkeys->trans.algo_comp = s2c_comp_algo->val; } +#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; +#endif + /* reserved for future extensions */ buf_getint(ses.payload); diff --git a/configure.ac b/configure.ac index 893b904..03a1e4f 100644 --- a/configure.ac +++ b/configure.ac @@ -217,6 +217,15 @@ AC_ARG_ENABLE(shadow, AC_MSG_NOTICE(Using shadow passwords if available) ] ) + +AC_ARG_ENABLE(fuzz, + [ --enable-fuzz Build fuzzing], + [ + AC_DEFINE(DROPBEAR_FUZZ, 1, Fuzzing) + AC_MSG_NOTICE(Enabling fuzzing) + ] +) + # Checks for header files. diff --git a/dbrandom.c b/dbrandom.c index 239b044..f4fc94d 100644 --- a/dbrandom.c +++ b/dbrandom.c @@ -27,6 +27,7 @@ #include "dbutil.h" #include "bignum.h" #include "dbrandom.h" +#include "runopts.h" /* this is used to generate unique output from the same hashpool */ @@ -145,6 +146,12 @@ void addrandom(unsigned char * buf, unsigned int len) { hash_state hs; +#ifdef DROPBEAR_FUZZ + if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + return; + } +#endif + /* hash in the new seed data */ sha1_init(&hs); /* existing state (zeroes on startup) */ @@ -157,6 +164,11 @@ void addrandom(unsigned char * buf, unsigned int len) static void write_urandom() { +#ifdef DROPBEAR_FUZZ + if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + return; + } +#endif #ifndef DROPBEAR_PRNGD_SOCKET /* This is opportunistic, don't worry about failure */ unsigned char buf[INIT_SEED_SIZE]; @@ -170,6 +182,16 @@ static void write_urandom() #endif } +static void seedfuzz(void) { + hash_state hs; + sha1_init(&hs); + sha1_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz")); + sha1_done(&hs, hashpool); + + counter = 0; + donerandinit = 1; +} + /* Initialise the prng from /dev/urandom or prngd. This function can * be called multiple times */ void seedrandom() { @@ -180,8 +202,16 @@ void seedrandom() { struct timeval tv; clock_t clockval; +#ifdef DROPBEAR_FUZZ + if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + seedfuzz(); + return; + } +#endif + /* hash in the new seed data */ sha1_init(&hs); + /* existing state */ sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); diff --git a/hostkeys.c b/hostkeys.c new file mode 100644 index 0000000..dc1615d --- /dev/null +++ b/hostkeys.c @@ -0,0 +1,129 @@ + +unsigned char keyr[] = { + 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, 0x72, 0x73, 0x61, 0x00, + 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0xb1, + 0x06, 0x95, 0xc9, 0xa8, 0x38, 0xb9, 0x99, 0x91, 0xb5, 0x17, 0x39, 0xb9, + 0xfa, 0xa4, 0x49, 0xf8, 0x2a, 0x4c, 0x14, 0xbd, 0xb6, 0x85, 0xdb, 0x38, + 0x99, 0x44, 0xfa, 0xd6, 0xaa, 0x67, 0xef, 0x00, 0x75, 0x2b, 0x6a, 0x5c, + 0x1b, 0x50, 0xa8, 0x52, 0xf9, 0xa7, 0xee, 0xe2, 0xb3, 0x80, 0x38, 0x92, + 0x20, 0x86, 0x7c, 0xe5, 0x89, 0xb3, 0x06, 0xe4, 0x3b, 0xd1, 0xe2, 0x45, + 0xea, 0xc1, 0xd5, 0x8e, 0x05, 0xfb, 0x90, 0x29, 0xd9, 0x41, 0xb3, 0x05, + 0x31, 0x1e, 0xcc, 0xeb, 0x89, 0xdc, 0xd2, 0x6a, 0x99, 0x23, 0xbd, 0x7a, + 0xbe, 0x8c, 0xe3, 0x3f, 0xa1, 0xe8, 0xf5, 0xb4, 0x51, 0x40, 0xb4, 0xb1, + 0xc1, 0x16, 0x9f, 0x07, 0xbb, 0x99, 0xaa, 0x4b, 0x8f, 0x11, 0x19, 0x3c, + 0x18, 0xbd, 0x6e, 0xce, 0x14, 0x54, 0x2c, 0x16, 0x4a, 0x5f, 0x89, 0xe4, + 0x6b, 0x9f, 0x55, 0x68, 0xcc, 0x09, 0x8e, 0x4b, 0x92, 0xc8, 0x87, 0xfe, + 0x09, 0xed, 0x53, 0x6e, 0xff, 0x5f, 0x15, 0x0d, 0x19, 0x9d, 0xa6, 0x54, + 0xd2, 0xea, 0x59, 0x4f, 0xa1, 0x7c, 0xf6, 0xf5, 0x7f, 0x32, 0x23, 0xed, + 0x72, 0xa8, 0x96, 0x17, 0x87, 0x06, 0xf2, 0xc7, 0xcd, 0xda, 0x4a, 0x10, + 0xd1, 0xfd, 0xb8, 0xf1, 0xaf, 0x25, 0x55, 0x32, 0x45, 0x39, 0x95, 0xec, + 0x0c, 0xa9, 0xf0, 0x47, 0x8b, 0x66, 0xe0, 0xb7, 0xa2, 0xf6, 0x35, 0x50, + 0x27, 0xe7, 0x2f, 0x90, 0x35, 0x5b, 0xd5, 0x62, 0x19, 0xb4, 0x41, 0xd4, + 0x52, 0xe7, 0x7f, 0x97, 0xfc, 0x5b, 0x4a, 0x5b, 0x19, 0x06, 0x65, 0x2d, + 0x23, 0x29, 0x15, 0x8b, 0x05, 0xaf, 0xbe, 0xd3, 0x4a, 0x27, 0x5b, 0xc9, + 0xc0, 0xd0, 0xd2, 0xba, 0x8b, 0x00, 0x7a, 0x2f, 0x39, 0xa0, 0x13, 0xb9, + 0xe6, 0xf5, 0x4b, 0x21, 0x54, 0x57, 0xb3, 0xf9, 0x6c, 0x6f, 0xd0, 0x17, + 0xf4, 0x50, 0x9d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xf2, 0xda, 0x5f, 0xfb, + 0xe2, 0xda, 0xfc, 0xe0, 0xdf, 0x3a, 0x0e, 0x14, 0x18, 0xc1, 0xd9, 0x1f, + 0x43, 0xe3, 0x65, 0x3e, 0x07, 0xe7, 0x8d, 0xdc, 0x1d, 0x11, 0xc1, 0xd6, + 0xc0, 0xd8, 0xda, 0x53, 0xf5, 0x04, 0x73, 0x51, 0x1b, 0x26, 0xef, 0x4e, + 0xf5, 0xce, 0x3d, 0x77, 0x21, 0x94, 0xd0, 0xc7, 0xc1, 0xda, 0x19, 0x7d, + 0xf8, 0xc5, 0x4c, 0xc8, 0xee, 0x7d, 0xd1, 0xbb, 0x02, 0x90, 0x2b, 0xff, + 0x4e, 0x4d, 0xd7, 0x9d, 0x72, 0x0c, 0x60, 0x0f, 0x4b, 0x83, 0xf5, 0xc2, + 0x26, 0xd6, 0x22, 0xb8, 0x60, 0x3a, 0xf9, 0x2f, 0x92, 0x2a, 0x2e, 0x14, + 0xa7, 0x56, 0x1c, 0x56, 0x05, 0x41, 0x92, 0xac, 0xb1, 0x4e, 0x44, 0x1e, + 0x70, 0x42, 0xda, 0xc7, 0xc8, 0x9c, 0xae, 0x29, 0x2d, 0x0c, 0x3a, 0xff, + 0x9b, 0xb6, 0xad, 0xb4, 0xfb, 0x49, 0x28, 0x96, 0x74, 0xf5, 0x94, 0x74, + 0xb7, 0x40, 0x93, 0x2b, 0x34, 0x29, 0xd2, 0x8a, 0xf3, 0x99, 0xf9, 0xe9, + 0xd8, 0xcc, 0x48, 0x1d, 0x3e, 0xc1, 0x82, 0x35, 0x4f, 0xef, 0xb1, 0x81, + 0x3c, 0xe1, 0xa1, 0x03, 0x65, 0xac, 0x21, 0x21, 0x40, 0x61, 0xfb, 0xd3, + 0x54, 0xac, 0xa1, 0xf2, 0xf0, 0x61, 0xd9, 0x01, 0x4e, 0xc2, 0x28, 0xb1, + 0x7c, 0x27, 0x6e, 0x56, 0x68, 0x69, 0x8f, 0xc5, 0xfd, 0xca, 0x39, 0x6e, + 0x22, 0x09, 0xf1, 0xb4, 0xd5, 0xac, 0xb8, 0xe0, 0x1b, 0x21, 0x86, 0xf4, + 0xc8, 0x15, 0xc6, 0x1f, 0x21, 0xae, 0xcb, 0xab, 0x5a, 0x09, 0x30, 0x9e, + 0xdd, 0x6c, 0x38, 0x59, 0xec, 0x59, 0x3a, 0x08, 0xee, 0x46, 0x7b, 0x78, + 0x23, 0xbc, 0xfc, 0xe2, 0xda, 0xe8, 0x1a, 0x65, 0xe6, 0xe0, 0x78, 0xd3, + 0xb0, 0x03, 0x2e, 0xf1, 0xb8, 0xca, 0x8e, 0x90, 0x75, 0xaf, 0xf7, 0xa8, + 0x48, 0xed, 0x82, 0xc9, 0xcf, 0x44, 0x56, 0xfc, 0x05, 0xfd, 0x6b, 0x00, + 0x00, 0x00, 0x81, 0x00, 0xfc, 0x94, 0xdf, 0x42, 0xc7, 0x9a, 0xa2, 0xff, + 0x32, 0xdf, 0x06, 0xb6, 0x4d, 0x90, 0x31, 0x28, 0x28, 0xdb, 0x03, 0xf9, + 0xa6, 0xb3, 0xa2, 0x91, 0x4c, 0xdf, 0x6e, 0xf6, 0xb9, 0x44, 0x3b, 0xdd, + 0x17, 0xc1, 0xc8, 0x1d, 0xd1, 0xc0, 0xc0, 0x30, 0x22, 0xbe, 0x24, 0x2e, + 0x0e, 0xdf, 0xe0, 0x18, 0x37, 0x3e, 0xb8, 0x7f, 0xb2, 0x50, 0x34, 0xc4, + 0x08, 0x5e, 0x69, 0x1f, 0xd5, 0xc9, 0xce, 0x47, 0x7d, 0x75, 0x5e, 0x3b, + 0x87, 0xdd, 0x46, 0x35, 0x01, 0x0f, 0x17, 0x8a, 0xf1, 0xf1, 0xc4, 0xa9, + 0x94, 0xa7, 0x6e, 0xce, 0x80, 0xe3, 0x17, 0x2e, 0xb0, 0xef, 0x63, 0xa7, + 0x11, 0x86, 0x96, 0x4a, 0x63, 0x2d, 0x9e, 0x92, 0x62, 0x43, 0x43, 0x72, + 0xa5, 0xdc, 0xa0, 0xcd, 0x19, 0x93, 0xd7, 0xe0, 0x80, 0x41, 0x27, 0xea, + 0xe4, 0xe8, 0xc1, 0x91, 0x9e, 0x13, 0xb3, 0x9c, 0xd1, 0xed, 0xcb, 0xbf, + 0x00, 0x00, 0x00, 0x81, 0x00, 0xb3, 0x6b, 0xee, 0xa4, 0x70, 0x4e, 0xfb, + 0xf9, 0x7e, 0x2e, 0x74, 0x5d, 0x3e, 0x8b, 0x3f, 0xff, 0x8c, 0xde, 0x68, + 0x38, 0xda, 0xce, 0xc0, 0x66, 0x4b, 0xca, 0x35, 0xc3, 0x97, 0xa8, 0xf0, + 0x00, 0x8e, 0xb3, 0x46, 0x60, 0xd0, 0x4d, 0x7e, 0x7b, 0xdf, 0x17, 0x7b, + 0x2f, 0xc4, 0x16, 0xee, 0x45, 0xdb, 0xa5, 0x5d, 0xc0, 0x72, 0xe9, 0xc6, + 0x91, 0x0f, 0xd9, 0x30, 0x74, 0x6c, 0xde, 0x93, 0xb5, 0xb6, 0xaf, 0x52, + 0x53, 0x3c, 0x08, 0x55, 0xea, 0xb8, 0x66, 0x07, 0xbe, 0xce, 0xf9, 0x80, + 0x8d, 0xe0, 0xca, 0xdc, 0x63, 0xe8, 0x58, 0x94, 0x22, 0x4f, 0x08, 0x66, + 0x13, 0x9e, 0x63, 0x2e, 0x92, 0x7a, 0xb6, 0x66, 0x94, 0x9b, 0x71, 0x66, + 0xd3, 0x08, 0xc9, 0x89, 0xea, 0x78, 0x35, 0x0d, 0xf2, 0x25, 0x55, 0xd4, + 0xb0, 0x9b, 0xea, 0x18, 0x77, 0xf6, 0x25, 0x02, 0xb4, 0x5e, 0x71, 0xea, + 0xa3 +}; +unsigned int keyr_len = 805; +unsigned char keye[] = { + 0x00, 0x00, 0x00, 0x13, 0x65, 0x63, 0x64, 0x73, 0x61, 0x2d, 0x73, 0x68, + 0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35, 0x36, 0x00, + 0x00, 0x00, 0x08, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35, 0x36, 0x00, + 0x00, 0x00, 0x41, 0x04, 0x0a, 0x00, 0x6c, 0x7c, 0x1c, 0xc4, 0x03, 0x44, + 0x46, 0x70, 0xba, 0x00, 0x7c, 0x79, 0x89, 0x7b, 0xc3, 0xd6, 0x32, 0x98, + 0x34, 0xe7, 0x1c, 0x60, 0x04, 0x73, 0xd9, 0xb5, 0x7e, 0x94, 0x04, 0x04, + 0xea, 0xc8, 0xb8, 0xfb, 0xd4, 0x70, 0x9f, 0x29, 0xa7, 0x8d, 0x9a, 0x64, + 0x3a, 0x8c, 0x45, 0x23, 0x37, 0x5a, 0x2b, 0x4f, 0x54, 0x91, 0x80, 0xf1, + 0xac, 0x3a, 0xf5, 0x6d, 0xfa, 0xe8, 0x76, 0x20, 0x00, 0x00, 0x00, 0x21, + 0x00, 0xc2, 0xaf, 0xbe, 0xdc, 0x06, 0xff, 0x3d, 0x08, 0x9b, 0x73, 0xe0, + 0x3c, 0x58, 0x28, 0x70, 0x9b, 0x23, 0x39, 0x51, 0xd7, 0xbc, 0xa7, 0x1a, + 0xf5, 0xb4, 0x23, 0xd3, 0xf6, 0x17, 0xa6, 0x9c, 0x02 +}; +unsigned int keye_len = 141; +unsigned char keyd[] = { + 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, 0x64, 0x73, 0x73, 0x00, + 0x00, 0x00, 0x81, 0x00, 0xb0, 0x02, 0x19, 0x8b, 0xf3, 0x46, 0xf9, 0xc5, + 0x47, 0x78, 0x3d, 0x7f, 0x04, 0x10, 0x0a, 0x43, 0x8e, 0x00, 0x9e, 0xa4, + 0x30, 0xfd, 0x47, 0xb9, 0x05, 0x9e, 0x95, 0xaa, 0x37, 0x9a, 0x91, 0xbf, + 0xf8, 0xb9, 0xe0, 0x8d, 0x97, 0x49, 0x87, 0xe2, 0xe6, 0x90, 0xc1, 0xe4, + 0x61, 0x57, 0x77, 0xfd, 0x91, 0x1d, 0xe1, 0x4b, 0xa0, 0xb2, 0xbc, 0xa1, + 0x6a, 0x6a, 0xdd, 0x31, 0xda, 0xe7, 0x54, 0x03, 0xfd, 0x48, 0x62, 0x8a, + 0x1d, 0x1d, 0xe2, 0x26, 0x76, 0x29, 0x08, 0xab, 0x65, 0x88, 0x74, 0x02, + 0x1e, 0xa9, 0x29, 0x1b, 0x69, 0x3b, 0xb4, 0x5f, 0x62, 0x80, 0xa3, 0xa6, + 0x4b, 0xc3, 0x0e, 0x89, 0x24, 0xe4, 0x8a, 0x31, 0xae, 0x89, 0x7a, 0x7a, + 0x58, 0x44, 0x46, 0x77, 0x62, 0x33, 0xa2, 0x5d, 0x17, 0x0e, 0x0b, 0x64, + 0xee, 0x1a, 0x02, 0xbd, 0xf8, 0x27, 0x86, 0xe1, 0x87, 0x92, 0x84, 0xc7, + 0x00, 0x00, 0x00, 0x15, 0x00, 0xb3, 0x8b, 0x81, 0x39, 0x9c, 0xba, 0xe1, + 0x1d, 0x9a, 0x8b, 0x89, 0xb3, 0x08, 0x9b, 0x12, 0xa8, 0x7b, 0xea, 0x25, + 0x8d, 0x00, 0x00, 0x00, 0x80, 0x76, 0x3f, 0x72, 0xb2, 0xef, 0xc3, 0x16, + 0xd8, 0x09, 0x36, 0x23, 0x03, 0xf9, 0x5c, 0xac, 0x8b, 0x51, 0x35, 0x2e, + 0x36, 0xba, 0x39, 0xd0, 0x57, 0x19, 0x4f, 0x14, 0x8b, 0xea, 0x32, 0xfc, + 0x86, 0x41, 0xea, 0x85, 0x71, 0x4d, 0x52, 0x0c, 0xff, 0xc1, 0xd3, 0xd5, + 0xcd, 0x2e, 0x37, 0xcc, 0xe1, 0xcc, 0x22, 0x38, 0xa8, 0x47, 0x16, 0x34, + 0x3b, 0x32, 0x9c, 0x2f, 0x0f, 0xcd, 0x5f, 0x7f, 0x06, 0x64, 0x89, 0xc5, + 0x02, 0x4f, 0x9a, 0x70, 0x11, 0xf0, 0xaa, 0xe1, 0x7a, 0x75, 0x49, 0x8d, + 0x0f, 0x8d, 0x5b, 0x54, 0xe2, 0xe7, 0x10, 0x6e, 0xe5, 0xbd, 0xb7, 0x62, + 0xf7, 0x40, 0x59, 0x39, 0x31, 0xd9, 0x13, 0x7b, 0xa3, 0xdf, 0x0d, 0x31, + 0x52, 0x43, 0xe0, 0xaf, 0x19, 0x12, 0x15, 0x12, 0x34, 0x01, 0x6f, 0xcf, + 0x62, 0x21, 0xe4, 0xc8, 0x34, 0x69, 0xc9, 0x85, 0xe3, 0xde, 0xd7, 0x0c, + 0xac, 0x00, 0x00, 0x00, 0x80, 0x41, 0xa3, 0xc5, 0xa4, 0x89, 0x86, 0xc8, + 0x17, 0xf3, 0x8e, 0x68, 0x72, 0xbe, 0x13, 0x8b, 0x63, 0xe3, 0x07, 0xe3, + 0xd5, 0xa4, 0xa2, 0xd3, 0x2c, 0x2f, 0xbe, 0x16, 0x71, 0xc9, 0x79, 0x64, + 0x5a, 0x1e, 0x19, 0x82, 0x07, 0xe2, 0x93, 0xda, 0x22, 0xcf, 0x6d, 0xdd, + 0x38, 0xcb, 0x6e, 0x6b, 0x0f, 0x95, 0x8d, 0xfa, 0x3f, 0xbb, 0xb8, 0x6a, + 0x7d, 0xc3, 0x22, 0x1e, 0x49, 0xcf, 0x98, 0x73, 0x05, 0x5d, 0x97, 0xfa, + 0x4c, 0xf2, 0x82, 0x3d, 0x98, 0x61, 0x4e, 0x96, 0x80, 0x26, 0x79, 0xda, + 0x24, 0xf8, 0xa1, 0x9c, 0x71, 0x82, 0xe6, 0xc7, 0xdc, 0xc2, 0xa5, 0xd0, + 0xf4, 0x36, 0xba, 0xaa, 0xee, 0xd3, 0x43, 0x46, 0x1d, 0xaa, 0x53, 0xea, + 0x85, 0x2c, 0x1b, 0xc8, 0x7c, 0x3c, 0xe7, 0x06, 0x44, 0xab, 0x16, 0xad, + 0xc6, 0x54, 0x91, 0x9a, 0xb9, 0xc0, 0xeb, 0x93, 0x8c, 0xca, 0x39, 0xcf, + 0x6f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x90, 0x26, 0x0a, 0xfc, 0x15, 0x99, + 0x7b, 0xac, 0xaa, 0x0c, 0xa2, 0xca, 0x7b, 0xa8, 0xd4, 0xdf, 0x68, 0x56, + 0xf9, 0x39 +}; +unsigned int keyd_len = 458; diff --git a/includes.h b/includes.h index f91a2c2..e9fe1fd 100644 --- a/includes.h +++ b/includes.h @@ -57,6 +57,7 @@ #include #include #include +#include #ifdef HAVE_UTMP_H #include diff --git a/packet.c b/packet.c index 924554d..f10e639 100644 --- a/packet.c +++ b/packet.c @@ -35,6 +35,7 @@ #include "auth.h" #include "channel.h" #include "netio.h" +#include "runopts.h" static int read_packet_init(void); static void make_mac(unsigned int seqno, const struct key_context_directional * key_state, @@ -76,6 +77,15 @@ void write_packet() { /* This may return EAGAIN. The main loop sometimes calls write_packet() without bothering to test with select() since it's likely to be necessary */ +#ifdef DROPBEAR_FUZZ + if (opts.fuzz.fuzzing) { + // pretend to write one packet at a time + // TODO(fuzz): randomise amount written based on the fuzz input + written = iov[0].iov_len; + } + else +#endif + { written = writev(ses.sock_out, iov, iov_count); if (written < 0) { if (errno == EINTR || errno == EAGAIN) { @@ -85,6 +95,7 @@ void write_packet() { dropbear_exit("Error writing: %s", strerror(errno)); } } + } packet_queue_consume(&ses.writequeue, written); ses.writequeue_len -= written; @@ -94,6 +105,9 @@ void write_packet() { } #else /* No writev () */ +#ifdef DROPBEAR_FUZZ + _Static_assert(0, "No fuzzing code for no-writev writes"); +#endif /* Get the next buffer in the queue of encrypted packets to write*/ writebuf = (buffer*)examine(&ses.writequeue); diff --git a/runopts.h b/runopts.h index f7c869d..1f51b16 100644 --- a/runopts.h +++ b/runopts.h @@ -58,6 +58,29 @@ typedef struct runopts { char *mac_list; #endif +#ifdef DROPBEAR_FUZZ + struct { + int fuzzing; + + // to record an unencrypted stream + FILE* recordf; + + // fuzzing input + buffer *input; + + // dropbear_exit() jumps back + sigjmp_buf jmp; + + uid_t pw_uid; + gid_t pw_gid; + char* pw_name; + char* pw_dir; + char* pw_shell; + char* pw_passwd; + + } fuzz; +#endif + } runopts; extern runopts opts; diff --git a/svr-auth.c b/svr-auth.c index 577ea88..d00fa7a 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -358,7 +358,11 @@ void send_msg_userauth_failure(int partial, int incrfail) { genrandom((unsigned char*)&delay, sizeof(delay)); /* We delay for 300ms +- 50ms */ delay = 250000 + (delay % 100000); - usleep(delay); +#ifndef DROPBEAR_FUZZ + if (!opts.fuzz.fuzzing) { + usleep(delay); + } +#endif ses.authstate.failcount++; } diff --git a/svr-runopts.c b/svr-runopts.c index 8f60059..c0b7bf2 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -346,6 +346,19 @@ void svr_getopts(int argc, char ** argv) { } opts.idle_timeout_secs = val; } + +#ifdef DROPBEAR_FUZZ + if (opts.fuzz.fuzzing) { + struct passwd *pw; + /* user lookups might be slow, cache it */ + pw = getpwuid(getuid()); + dropbear_assert(pw); + opts.fuzz.pw_name = m_strdup(pw->pw_name); + opts.fuzz.pw_dir = m_strdup(pw->pw_dir); + opts.fuzz.pw_shell = m_strdup(pw->pw_shell); + opts.fuzz.pw_passwd = m_strdup("!!zzznope"); + } +#endif } static void addportandaddress(const char* spec) { @@ -475,11 +488,57 @@ static void addhostkey(const char *keyfile) { svr_opts.num_hostkey_files++; } +#ifdef DROPBEAR_FUZZ +static void load_fixed_hostkeys() { +#include "hostkeys.c" + + buffer *b = buf_new(3000); + enum signkey_type type; + + TRACE(("load fixed hostkeys")) + + svr_opts.hostkey = new_sign_key(); + + buf_setlen(b, 0); + buf_putbytes(b, keyr, keyr_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_RSA; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed rsa hostkey"); + } + + buf_setlen(b, 0); + buf_putbytes(b, keyd, keyd_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_DSS; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed dss hostkey"); + } + + buf_setlen(b, 0); + buf_putbytes(b, keye, keye_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_ECDSA_NISTP256; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed ecdsa hostkey"); + } + + buf_free(b); +} +#endif // DROPBEAR_FUZZ + void load_all_hostkeys() { int i; int disable_unset_keys = 1; int any_keys = 0; +#ifdef DROPBEAR_FUZZ + if (opts.fuzz.fuzzing) { + load_fixed_hostkeys(); + return; + } +#endif + svr_opts.hostkey = new_sign_key(); for (i = 0; i < svr_opts.num_hostkey_files; i++) { diff --git a/svr-session.c b/svr-session.c index 4f56cd9..0e6a9e8 100644 --- a/svr-session.c +++ b/svr-session.c @@ -191,6 +191,12 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { m_free(svr_opts.ports[i]); } +#ifdef DROPBEAR_FUZZ + if (opts.fuzz.fuzzing) { + longjmp(opts.fuzz.jmp, 1); + } +#endif + exit(exitcode); } From fb719e3d0ba3571d3abc5638d1fbbe9e1675d6a7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 13 May 2017 22:50:54 +0800 Subject: [PATCH 03/56] fuzz harness --HG-- branch : fuzz --- .hgignore | 2 + Makefile.in | 25 +++++++++-- dbrandom.c | 7 ++-- fuzz-common.c | 78 +++++++++++++++++++++++++++++++++++ fuzz-harness.c | 8 ++++ hostkeys.c => fuzz-hostkeys.c | 0 fuzz.h | 35 ++++++++++++++++ fuzzer-preauth.c | 31 ++++++++++++++ packet.c | 3 +- runopts.h | 23 ----------- svr-auth.c | 2 +- svr-runopts.c | 58 -------------------------- svr-session.c | 13 +++--- 13 files changed, 191 insertions(+), 94 deletions(-) create mode 100644 .hgignore create mode 100644 fuzz-common.c create mode 100644 fuzz-harness.c rename hostkeys.c => fuzz-hostkeys.c (100%) create mode 100644 fuzz.h create mode 100644 fuzzer-preauth.c diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..3938238 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +.*\.o +.*~ diff --git a/Makefile.in b/Makefile.in index 286e25a..d94f87b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -30,7 +30,7 @@ COMMONOBJS=dbutil.o buffer.o dbhelpers.o \ 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 + gensignkey.o gendss.o genrsa.o fuzz-common.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 \ @@ -224,8 +224,27 @@ distclean: clean tidy tidy: -rm -f *~ *.gcov */*~ -# run this manually for fuzzing. hostkeys.c is checked in. -hostkeys: +## Fuzzing targets + +# exclude svr-main.o to avoid duplicate main +svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs)) +CLANG=clang + +# fuzzers that don't use libfuzzer, just a standalone harness that feeds inputs +fuzzstandalone: LIBS+=fuzz-harness.o +fuzzstandalone: fuzz-harness.o fuzzers + +# build all the fuzzers. This will require fail to link unless built with +# make fuzzers LIBS=-lFuzzer.a +# or similar - the library provides main(). +fuzzers: fuzzer-preauth + +fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) + $(CC) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ + +# run this to update hardcoded hostkeys for for fuzzing. +# hostkeys.c is checked in to hg. +fuzz-hostkeys: dropbearkey -t rsa -f keyr dropbearkey -t dss -f keyd dropbearkey -t ecdsa -size 256 -f keye diff --git a/dbrandom.c b/dbrandom.c index f4fc94d..0197411 100644 --- a/dbrandom.c +++ b/dbrandom.c @@ -28,6 +28,7 @@ #include "bignum.h" #include "dbrandom.h" #include "runopts.h" +#include "fuzz.h" /* this is used to generate unique output from the same hashpool */ @@ -147,7 +148,7 @@ void addrandom(unsigned char * buf, unsigned int len) hash_state hs; #ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + if (fuzz.fuzzing || fuzz.recordf) { return; } #endif @@ -165,7 +166,7 @@ void addrandom(unsigned char * buf, unsigned int len) static void write_urandom() { #ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + if (fuzz.fuzzing || fuzz.recordf) { return; } #endif @@ -203,7 +204,7 @@ void seedrandom() { clock_t clockval; #ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + if (fuzz.fuzzing || fuzz.recordf) { seedfuzz(); return; } diff --git a/fuzz-common.c b/fuzz-common.c new file mode 100644 index 0000000..cc6c125 --- /dev/null +++ b/fuzz-common.c @@ -0,0 +1,78 @@ +#include "includes.h" + +#ifdef DROPBEAR_FUZZ + +#include "includes.h" +#include "fuzz.h" +#include "dbutil.h" +#include "runopts.h" + +struct dropbear_fuzz_options fuzz; + +static void load_fixed_hostkeys(void); + +static void common_setup_fuzzer(void) { + fuzz.fuzzing = 1; +} + +void svr_setup_fuzzer(void) { + struct passwd *pw; + + common_setup_fuzzer(); + + char *argv[] = { + "-E", + }; + + int argc = sizeof(argv) / sizeof(*argv); + svr_getopts(argc, argv); + + /* user lookups might be slow, cache it */ + pw = getpwuid(getuid()); + dropbear_assert(pw); + fuzz.pw_name = m_strdup(pw->pw_name); + fuzz.pw_dir = m_strdup(pw->pw_dir); + fuzz.pw_shell = m_strdup(pw->pw_shell); + fuzz.pw_passwd = m_strdup("!!zzznope"); + + load_fixed_hostkeys(); +} + +static void load_fixed_hostkeys(void) { +#include "fuzz-hostkeys.c" + + buffer *b = buf_new(3000); + enum signkey_type type; + + TRACE(("load fixed hostkeys")) + + svr_opts.hostkey = new_sign_key(); + + buf_setlen(b, 0); + buf_putbytes(b, keyr, keyr_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_RSA; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed rsa hostkey"); + } + + buf_setlen(b, 0); + buf_putbytes(b, keyd, keyd_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_DSS; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed dss hostkey"); + } + + buf_setlen(b, 0); + buf_putbytes(b, keye, keye_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_ECDSA_NISTP256; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed ecdsa hostkey"); + } + + buf_free(b); +} + +#endif /* DROPBEAR_FUZZ */ diff --git a/fuzz-harness.c b/fuzz-harness.c new file mode 100644 index 0000000..19b01e3 --- /dev/null +++ b/fuzz-harness.c @@ -0,0 +1,8 @@ +#include "includes.h" + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); + +int main(int argc, char ** argv) { + LLVMFuzzerTestOneInput("hello", 5); + return 0; +} diff --git a/hostkeys.c b/fuzz-hostkeys.c similarity index 100% rename from hostkeys.c rename to fuzz-hostkeys.c diff --git a/fuzz.h b/fuzz.h new file mode 100644 index 0000000..e7360e3 --- /dev/null +++ b/fuzz.h @@ -0,0 +1,35 @@ +#ifndef DROPBEAR_FUZZ_H +#define DROPBEAR_FUZZ_H + +#include "includes.h" +#include "buffer.h" + +#ifdef DROPBEAR_FUZZ + +void svr_setup_fuzzer(void); + +struct dropbear_fuzz_options { + int fuzzing; + + // to record an unencrypted stream + FILE* recordf; + + // fuzzing input + buffer input; + + // dropbear_exit() jumps back + 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; + +#endif + +#endif /* DROPBEAR_FUZZ_H */ diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c new file mode 100644 index 0000000..6a40108 --- /dev/null +++ b/fuzzer-preauth.c @@ -0,0 +1,31 @@ +#include "fuzz.h" +#include "dbrandom.h" +#include "session.h" + +static int setup_fuzzer(void) { + svr_setup_fuzzer(); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static int once = 0; + if (!once) { + setup_fuzzer(); + once = 1; + } + + fuzz.input.data = (unsigned char*)Data; + fuzz.input.size = Size; + fuzz.input.len = Size; + fuzz.input.pos = 0; + + seedrandom(); + + if (setjmp(fuzz.jmp) == 0) { + svr_session(-1, -1); + } else { + // dropbear_exit jumped here + } + + return 0; +} diff --git a/packet.c b/packet.c index f10e639..235069b 100644 --- a/packet.c +++ b/packet.c @@ -36,6 +36,7 @@ #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, @@ -78,7 +79,7 @@ void write_packet() { calls write_packet() without bothering to test with select() since it's likely to be necessary */ #ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing) { + if (fuzz.fuzzing) { // pretend to write one packet at a time // TODO(fuzz): randomise amount written based on the fuzz input written = iov[0].iov_len; diff --git a/runopts.h b/runopts.h index 1f51b16..f7c869d 100644 --- a/runopts.h +++ b/runopts.h @@ -58,29 +58,6 @@ typedef struct runopts { char *mac_list; #endif -#ifdef DROPBEAR_FUZZ - struct { - int fuzzing; - - // to record an unencrypted stream - FILE* recordf; - - // fuzzing input - buffer *input; - - // dropbear_exit() jumps back - sigjmp_buf jmp; - - uid_t pw_uid; - gid_t pw_gid; - char* pw_name; - char* pw_dir; - char* pw_shell; - char* pw_passwd; - - } fuzz; -#endif - } runopts; extern runopts opts; diff --git a/svr-auth.c b/svr-auth.c index d00fa7a..9156d77 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -359,7 +359,7 @@ void send_msg_userauth_failure(int partial, int incrfail) { /* We delay for 300ms +- 50ms */ delay = 250000 + (delay % 100000); #ifndef DROPBEAR_FUZZ - if (!opts.fuzz.fuzzing) { + if (!fuzz.fuzzing) { usleep(delay); } #endif diff --git a/svr-runopts.c b/svr-runopts.c index c0b7bf2..295b653 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -346,19 +346,6 @@ void svr_getopts(int argc, char ** argv) { } opts.idle_timeout_secs = val; } - -#ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing) { - struct passwd *pw; - /* user lookups might be slow, cache it */ - pw = getpwuid(getuid()); - dropbear_assert(pw); - opts.fuzz.pw_name = m_strdup(pw->pw_name); - opts.fuzz.pw_dir = m_strdup(pw->pw_dir); - opts.fuzz.pw_shell = m_strdup(pw->pw_shell); - opts.fuzz.pw_passwd = m_strdup("!!zzznope"); - } -#endif } static void addportandaddress(const char* spec) { @@ -488,57 +475,12 @@ static void addhostkey(const char *keyfile) { svr_opts.num_hostkey_files++; } -#ifdef DROPBEAR_FUZZ -static void load_fixed_hostkeys() { -#include "hostkeys.c" - - buffer *b = buf_new(3000); - enum signkey_type type; - - TRACE(("load fixed hostkeys")) - - svr_opts.hostkey = new_sign_key(); - - buf_setlen(b, 0); - buf_putbytes(b, keyr, keyr_len); - buf_setpos(b, 0); - type = DROPBEAR_SIGNKEY_RSA; - if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { - dropbear_exit("failed fixed rsa hostkey"); - } - - buf_setlen(b, 0); - buf_putbytes(b, keyd, keyd_len); - buf_setpos(b, 0); - type = DROPBEAR_SIGNKEY_DSS; - if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { - dropbear_exit("failed fixed dss hostkey"); - } - - buf_setlen(b, 0); - buf_putbytes(b, keye, keye_len); - buf_setpos(b, 0); - type = DROPBEAR_SIGNKEY_ECDSA_NISTP256; - if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { - dropbear_exit("failed fixed ecdsa hostkey"); - } - - buf_free(b); -} -#endif // DROPBEAR_FUZZ void load_all_hostkeys() { int i; int disable_unset_keys = 1; int any_keys = 0; -#ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing) { - load_fixed_hostkeys(); - return; - } -#endif - svr_opts.hostkey = new_sign_key(); for (i = 0; i < svr_opts.num_hostkey_files; i++) { diff --git a/svr-session.c b/svr-session.c index 0e6a9e8..41571bb 100644 --- a/svr-session.c +++ b/svr-session.c @@ -40,6 +40,7 @@ #include "auth.h" #include "runopts.h" #include "crypto_desc.h" +#include "fuzz.h" static void svr_remoteclosed(void); @@ -182,6 +183,13 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { session_cleanup(); } +#ifdef DROPBEAR_FUZZ + // longjmp before cleaning up svr_opts + if (fuzz.fuzzing) { + longjmp(fuzz.jmp, 1); + } +#endif + if (svr_opts.hostkey) { sign_key_free(svr_opts.hostkey); svr_opts.hostkey = NULL; @@ -191,11 +199,6 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { m_free(svr_opts.ports[i]); } -#ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing) { - longjmp(opts.fuzz.jmp, 1); - } -#endif exit(exitcode); From 06fd9e3771a81a556c0dd89bb07aa474a20365fe Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 13 May 2017 23:44:12 +0800 Subject: [PATCH 04/56] fix buf->pos when shrinking --HG-- branch : fuzz --- buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/buffer.c b/buffer.c index d646c0e..ec3d883 100644 --- a/buffer.c +++ b/buffer.c @@ -109,6 +109,7 @@ void buf_setlen(buffer* buf, unsigned int len) { dropbear_exit("Bad buf_setlen"); } buf->len = len; + buf->pos = 0; } /* Increment the length of the buffer */ From a7bfd792f732db76c80429b7ea6651f9df8b0b8b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 13 May 2017 23:45:51 +0800 Subject: [PATCH 05/56] crypto_init() --HG-- branch : fuzz --- fuzz-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzz-common.c b/fuzz-common.c index cc6c125..e0da913 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -13,6 +13,7 @@ static void load_fixed_hostkeys(void); static void common_setup_fuzzer(void) { fuzz.fuzzing = 1; + crypto_init(); } void svr_setup_fuzzer(void) { From b9b308f2fee82a488b58ce33099abdbb9e44c34a Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 13 May 2017 23:46:01 +0800 Subject: [PATCH 06/56] Use CXX to link fuzzer, also link with $FUZZLIB --HG-- branch : fuzz --- Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index d94f87b..25e0293 100644 --- a/Makefile.in +++ b/Makefile.in @@ -231,7 +231,7 @@ svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs)) CLANG=clang # fuzzers that don't use libfuzzer, just a standalone harness that feeds inputs -fuzzstandalone: LIBS+=fuzz-harness.o +fuzzstandalone: FUZZLIB=fuzz-harness.o fuzzstandalone: fuzz-harness.o fuzzers # build all the fuzzers. This will require fail to link unless built with @@ -240,7 +240,7 @@ fuzzstandalone: fuzz-harness.o fuzzers fuzzers: fuzzer-preauth fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) - $(CC) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ + $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ # run this to update hardcoded hostkeys for for fuzzing. # hostkeys.c is checked in to hg. From beaff53a79a98317650c47fd821be1075e4369fe Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 14 May 2017 00:00:21 +0800 Subject: [PATCH 07/56] rename fuzzer -> fuzz-target, add list-fuzz-targets --HG-- branch : fuzz --- Makefile.in | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Makefile.in b/Makefile.in index 25e0293..e324142 100644 --- a/Makefile.in +++ b/Makefile.in @@ -226,18 +226,23 @@ tidy: ## Fuzzing targets -# exclude svr-main.o to avoid duplicate main -svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs)) -CLANG=clang +# list of fuzz targets +FUZZ_TARGETS=fuzzer-preauth + +list-fuzz-targets: + @echo $(FUZZ_TARGETS) # fuzzers that don't use libfuzzer, just a standalone harness that feeds inputs fuzzstandalone: FUZZLIB=fuzz-harness.o -fuzzstandalone: fuzz-harness.o fuzzers +fuzzstandalone: fuzz-harness.o fuzz-targets + +# exclude svr-main.o to avoid duplicate main +svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs)) # build all the fuzzers. This will require fail to link unless built with # make fuzzers LIBS=-lFuzzer.a # or similar - the library provides main(). -fuzzers: fuzzer-preauth +fuzz-targets: $(FUZZ_TARGETS) fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ From 6d33a2b0bbbabe32fbb3128c72e8741f312e901e Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 18 May 2017 23:36:54 +0800 Subject: [PATCH 08/56] setup svr_dropbear_exit --HG-- branch : fuzz --- fuzz-common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fuzz-common.c b/fuzz-common.c index e0da913..d9587c9 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -6,6 +6,8 @@ #include "fuzz.h" #include "dbutil.h" #include "runopts.h" +#include "crypto_desc.h" +#include "session.h" struct dropbear_fuzz_options fuzz; @@ -20,6 +22,9 @@ void svr_setup_fuzzer(void) { struct passwd *pw; common_setup_fuzzer(); + + _dropbear_exit = svr_dropbear_exit; + _dropbear_log = svr_dropbear_log; char *argv[] = { "-E", From a3e01b88841da97aae3d80b155034cc5f9f2c03e Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 18 May 2017 23:38:30 +0800 Subject: [PATCH 09/56] better harness --HG-- branch : fuzz --- fuzz-harness.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/fuzz-harness.c b/fuzz-harness.c index 19b01e3..822d4ac 100644 --- a/fuzz-harness.c +++ b/fuzz-harness.c @@ -1,8 +1,25 @@ #include "includes.h" +#include "buffer.h" +#include "dbutil.h" extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); int main(int argc, char ** argv) { - LLVMFuzzerTestOneInput("hello", 5); + int i; + buffer *input = buf_new(100000); + + for (i = 1; i < argc; i++) { + char* fn = argv[i]; + buf_setlen(input, 0); + buf_readfile(input, fn); + buf_setpos(input, 0); + + printf("Running %s\n", fn); + LLVMFuzzerTestOneInput(input->data, input->len); + printf("Done %s\n", fn); + } + + printf("Finished\n"); + return 0; } From e7cdb2ebe5982e4fd881d9ee1e472ad922237b07 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 19 May 2017 00:48:46 +0800 Subject: [PATCH 10/56] add wrapfd. improve fuzzer in makefile --HG-- branch : fuzz --- Makefile.in | 7 +- configure.ac | 8 +- fuzz-common.c | 33 ++++++++ fuzz-wrapfd.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++ fuzz-wrapfd.h | 17 +++++ fuzz.h | 6 +- fuzzer-preauth.c | 16 ++-- 7 files changed, 268 insertions(+), 12 deletions(-) create mode 100644 fuzz-wrapfd.c create mode 100644 fuzz-wrapfd.h diff --git a/Makefile.in b/Makefile.in index 192d8f9..19c747f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -34,7 +34,7 @@ COMMONOBJS=dbutil.o buffer.o dbhelpers.o \ 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 @@ CONVERTOBJS=dropbearconvert.o keyimport.o 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 @@ fuzz-hostkeys: /usr/bin/xxd -i -a keyr >> hostkeys.c /usr/bin/xxd -i -a keye >> hostkeys.c /usr/bin/xxd -i -a keyd >> hostkeys.c + diff --git a/configure.ac b/configure.ac index 03a1e4f..9a7fbc9 100644 --- a/configure.ac +++ b/configure.ac @@ -223,10 +223,14 @@ AC_ARG_ENABLE(fuzz, [ 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 diff --git a/fuzz-common.c b/fuzz-common.c index d9587c9..bfd2634 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -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 load_fixed_hostkeys(void); 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; diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c new file mode 100644 index 0000000..7509afe --- /dev/null +++ b/fuzz-wrapfd.c @@ -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; +} diff --git a/fuzz-wrapfd.h b/fuzz-wrapfd.h new file mode 100644 index 0000000..d4578b7 --- /dev/null +++ b/fuzz-wrapfd.h @@ -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 diff --git a/fuzz.h b/fuzz.h index e7360e3..c46ded9 100644 --- a/fuzz.h +++ b/fuzz.h @@ -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 @@ struct dropbear_fuzz_options { FILE* recordf; // fuzzing input - buffer input; + buffer *input; // dropbear_exit() jumps back sigjmp_buf jmp; diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index 6a40108..7564973 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -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 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { 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 } From fdc6f323923b36add7ab7112b1b4d05368bd5902 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 20 May 2017 13:23:16 +0800 Subject: [PATCH 11/56] closer to working --HG-- branch : fuzz --- common-kex.c | 11 +++------ common-session.c | 7 +++++- dbrandom.c | 2 -- fuzz-common.c | 9 ++++---- fuzz-harness.c | 4 ++++ fuzz-wrapfd.c | 60 ++++++++++++++++++++++++++++++++++++------------ fuzz-wrapfd.h | 6 +++++ fuzz.h | 22 +++++++++++++++--- fuzzer-preauth.c | 1 + includes.h | 3 ++- netio.c | 1 + packet.c | 12 +++++++++- 12 files changed, 103 insertions(+), 35 deletions(-) diff --git a/common-kex.c b/common-kex.c index ac28fa3..5259fce 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 99a5470..aa97b65 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 f9da8bb..b4b63cc 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 bfd2634..cc3d4d6 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 822d4ac..e3f6617 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 7509afe..62f1c91 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 d4578b7..a73a7fe 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 c46ded9..5a1ab3e 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 7564973..7a56e6a 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 e9fe1fd..b3e1357 100644 --- a/includes.h +++ b/includes.h @@ -133,7 +133,6 @@ #include #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 17693ec..b482431 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 235069b..a02cb1b 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) { From c1694230516fe1c3d78e4fd23aebd5fbc00ce21c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 20 May 2017 22:47:19 +0800 Subject: [PATCH 12/56] glaring wrapfd problems fixed --HG-- branch : fuzz --- common-channel.c | 4 ++++ common-kex.c | 1 + common-session.c | 28 +++++++++++++++++++++------- dbrandom.c | 5 +++-- dbutil.c | 11 ++++++++++- debug.h | 1 + fuzz-common.c | 5 ++++- fuzz-harness.c | 4 +++- fuzz-wrapfd.c | 27 +++++++++++++++++++-------- fuzz.h | 1 + fuzzer-preauth.c | 2 ++ svr-session.c | 4 +++- 12 files changed, 72 insertions(+), 21 deletions(-) diff --git a/common-channel.c b/common-channel.c index 7383f47..14985a9 100644 --- a/common-channel.c +++ b/common-channel.c @@ -88,6 +88,10 @@ void chancleanup() { unsigned int i; + if (!ses.channels) { + return; + } + TRACE(("enter chancleanup")) for (i = 0; i < ses.chansize; i++) { if (ses.channels[i] != NULL) { diff --git a/common-kex.c b/common-kex.c index 5259fce..a95182c 100644 --- a/common-kex.c +++ b/common-kex.c @@ -403,6 +403,7 @@ static void gen_new_zstream_recv() { ses.newkeys->recv.zstream->zfree = Z_NULL; if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) { + m_free(ses.newkeys->recv.zstream); dropbear_exit("zlib error"); } } else { diff --git a/common-session.c b/common-session.c index aa97b65..aa0dddb 100644 --- a/common-session.c +++ b/common-session.c @@ -82,14 +82,18 @@ void common_session_init(int sock_in, int sock_out) { ses.last_packet_time_any_sent = 0; ses.last_packet_time_keepalive_sent = 0; - if (pipe(ses.signal_pipe) < 0) { - dropbear_exit("Signal pipe failed"); +#ifdef DROPBEAR_FUZZ + if (!fuzz.fuzzing) +#endif + { + if (pipe(ses.signal_pipe) < 0) { + dropbear_exit("Signal pipe failed"); + } + setnonblocking(ses.signal_pipe[0]); + setnonblocking(ses.signal_pipe[1]); + ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]); + ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]); } - setnonblocking(ses.signal_pipe[0]); - setnonblocking(ses.signal_pipe[1]); - - ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]); - ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]); ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN); ses.transseq = 0; @@ -311,6 +315,16 @@ void session_cleanup() { buf_free(dequeue(&ses.writequeue)); } + m_free(ses.newkeys); +#ifndef DISABLE_ZLIB + if (ses.keys->recv.zstream != NULL) { + if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) { + dropbear_exit("Crypto error"); + } + m_free(ses.keys->recv.zstream); + } +#endif + m_free(ses.remoteident); m_free(ses.authstate.pw_dir); m_free(ses.authstate.pw_name); diff --git a/dbrandom.c b/dbrandom.c index b4b63cc..bb9c4c8 100644 --- a/dbrandom.c +++ b/dbrandom.c @@ -181,7 +181,8 @@ static void write_urandom() #endif } -static void seedfuzz(void) { +#ifdef DROPBEAR_FUZZ +void seedfuzz(void) { hash_state hs; sha1_init(&hs); sha1_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz")); @@ -190,6 +191,7 @@ static void seedfuzz(void) { counter = 0; donerandinit = 1; } +#endif /* Initialise the prng from /dev/urandom or prngd. This function can * be called multiple times */ @@ -203,7 +205,6 @@ void seedrandom() { #ifdef DROPBEAR_FUZZ if (fuzz.fuzzing || fuzz.recordf) { - seedfuzz(); return; } #endif diff --git a/dbutil.c b/dbutil.c index 830e8d2..4c835a4 100644 --- a/dbutil.c +++ b/dbutil.c @@ -569,7 +569,16 @@ void setnonblocking(int fd) { * can't be set to non-blocking */ TRACE(("ignoring ENODEV for setnonblocking")) } else { - dropbear_exit("Couldn't set nonblocking"); +#ifdef DROPBEAR_FUZZ + if (fuzz.fuzzing) + { + TRACE(("fuzzing ignore setnonblocking failure for %d", fd)) + } + else +#endif + { + dropbear_exit("Couldn't set nonblocking"); + } } } TRACE(("leave setnonblocking")) diff --git a/debug.h b/debug.h index 93fbc1f..0cd611d 100644 --- a/debug.h +++ b/debug.h @@ -64,6 +64,7 @@ /* you don't need to touch this block */ #if DEBUG_TRACE +extern int debug_trace; #define TRACE(X) dropbear_trace X; #define TRACE2(X) dropbear_trace2 X; #else /*DEBUG_TRACE*/ diff --git a/fuzz-common.c b/fuzz-common.c index cc3d4d6..144bf14 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -27,6 +27,9 @@ int fuzzer_set_input(const uint8_t *Data, size_t Size) { fuzz.input->len = Size; fuzz.input->pos = 0; + memset(&ses, 0x0, sizeof(ses)); + memset(&svr_ses, 0x0, sizeof(svr_ses)); + // get prefix. input format is // string prefix // uint32 wrapfd seed @@ -44,7 +47,7 @@ int fuzzer_set_input(const uint8_t *Data, size_t Size) { uint32_t wrapseed = buf_getint(fuzz.input); wrapfd_setup(wrapseed); - seedrandom(); + seedfuzz(); return DROPBEAR_SUCCESS; } diff --git a/fuzz-harness.c b/fuzz-harness.c index e3f6617..c31d2b7 100644 --- a/fuzz-harness.c +++ b/fuzz-harness.c @@ -18,7 +18,9 @@ int main(int argc, char ** argv) { buf_readfile(input, fn); buf_setpos(input, 0); - printf("Running %s\n", fn); + printf("Running %s once \n", fn); + LLVMFuzzerTestOneInput(input->data, input->len); + printf("Running %s twice \n", fn); LLVMFuzzerTestOneInput(input->data, input->len); printf("Done %s\n", fn); } diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c index 62f1c91..215eb16 100644 --- a/fuzz-wrapfd.c +++ b/fuzz-wrapfd.c @@ -16,6 +16,8 @@ static const double CHANCE_WRITE2 = 0.3; struct fdwrap { enum wrapfd_mode mode; buffer *buf; + int closein; + int closeout; }; static struct fdwrap wrap_fds[IOWRAP_MAXFD+1]; @@ -28,21 +30,25 @@ void wrapfd_setup(uint32_t seed) { TRACE(("wrapfd_setup %x", seed)) nused = 0; memset(wrap_fds, 0x0, sizeof(wrap_fds)); + memset(wrap_used, 0x0, sizeof(wrap_used)); + memset(rand_state, 0x0, sizeof(rand_state)); *((uint32_t*)rand_state) = 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); - TRACE(("wrapfd_add %d buf %p mode %d", fd, buf, mode)) 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++; @@ -50,12 +56,12 @@ void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode) { 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; - TRACE(("wrapfd_remove %d", fd)) // remove from used list for (i = 0, j = 0; i < nused; i++) { @@ -67,6 +73,9 @@ void wrapfd_remove(int fd) { nused--; } +void wrapfd_close(int fd) { + wrapfd_remove(fd); +} int wrapfd_read(int fd, void *out, size_t count) { size_t maxread; @@ -85,9 +94,10 @@ int wrapfd_read(int fd, void *out, size_t count) { assert(count != 0); - if (erand48(rand_state) < CHANCE_CLOSE) { - wrapfd_remove(fd); - return 0; + if (wrap_fds[fd].closein || erand48(rand_state) < CHANCE_CLOSE) { + wrap_fds[fd].closein = 1; + errno = ECONNRESET; + return -1; } if (erand48(rand_state) < CHANCE_INTR) { @@ -135,9 +145,10 @@ int wrapfd_write(int fd, const void* in, size_t count) { (void)volin[i]; } - if (erand48(rand_state) < CHANCE_CLOSE) { - wrapfd_remove(fd); - return 0; + if (wrap_fds[fd].closeout || erand48(rand_state) < CHANCE_CLOSE) { + wrap_fds[fd].closeout = 1; + errno = ECONNRESET; + return -1; } if (erand48(rand_state) < CHANCE_INTR) { diff --git a/fuzz.h b/fuzz.h index 5a1ab3e..d0e13b3 100644 --- a/fuzz.h +++ b/fuzz.h @@ -23,6 +23,7 @@ void fuzz_kex_fakealgos(void); 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) +#define close(fd) wrapfd_close(fd) #endif // FUZZ_SKIP_WRAP struct dropbear_fuzz_options { diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index 7a56e6a..9ca7b84 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -2,9 +2,11 @@ #include "dbrandom.h" #include "session.h" #include "fuzz-wrapfd.h" +#include "debug.h" static void setup_fuzzer(void) { svr_setup_fuzzer(); + //debug_trace = 1; } int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { diff --git a/svr-session.c b/svr-session.c index 8cb0ea9..a1466fe 100644 --- a/svr-session.c +++ b/svr-session.c @@ -245,7 +245,9 @@ void svr_dropbear_log(int priority, const char* format, va_list param) { static void svr_remoteclosed() { m_close(ses.sock_in); - m_close(ses.sock_out); + if (ses.sock_in != ses.sock_out) { + m_close(ses.sock_out); + } ses.sock_in = -1; ses.sock_out = -1; dropbear_close("Exited normally"); From 9f1c8b2f8fe1722815af1cfb152c3f48aa9848ce Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 20 May 2017 23:39:01 +0800 Subject: [PATCH 13/56] make buf_getstring fail prior to malloc if the buffer is short --HG-- branch : fuzz --- buffer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/buffer.c b/buffer.c index 0ca50b4..a462374 100644 --- a/buffer.c +++ b/buffer.c @@ -209,6 +209,7 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) { unsigned int len; char* ret; + void* src = NULL; len = buf_getint(buf); if (len > MAX_STRING_LEN) { dropbear_exit("String too long"); @@ -217,8 +218,9 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) { if (retlen != NULL) { *retlen = len; } + src = buf_getptr(buf, len); ret = m_malloc(len+1); - memcpy(ret, buf_getptr(buf, len), len); + memcpy(ret, src, len); buf_incrpos(buf, len); ret[len] = '\0'; From 1abd239b9ded3386fac8d553aa4652da88a52d58 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 21 May 2017 10:48:18 +0800 Subject: [PATCH 14/56] ignore wrapfd_close for unknown --HG-- branch : fuzz --- fuzz-wrapfd.c | 12 +++++++++--- fuzz-wrapfd.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c index 215eb16..c65ed38 100644 --- a/fuzz-wrapfd.c +++ b/fuzz-wrapfd.c @@ -44,7 +44,6 @@ void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode) { 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; @@ -73,8 +72,15 @@ void wrapfd_remove(int fd) { nused--; } -void wrapfd_close(int fd) { - wrapfd_remove(fd); +int wrapfd_close(int fd) { + if (fd >= 0 && fd <= IOWRAP_MAXFD && wrap_fds[fd].mode != UNUSED) + { + wrapfd_remove(fd); + return 0; + } + else { + return close(fd); + } } int wrapfd_read(int fd, void *out, size_t count) { diff --git a/fuzz-wrapfd.h b/fuzz-wrapfd.h index a73a7fe..9358c1a 100644 --- a/fuzz-wrapfd.h +++ b/fuzz-wrapfd.h @@ -19,5 +19,6 @@ 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); +int wrapfd_close(int fd); #endif // FUZZ_WRAPFD_H From fb8fb7fed0bb822ccc11ed20229db51a3991a0e5 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 21 May 2017 10:54:11 +0800 Subject: [PATCH 15/56] add dbmalloc epoch cleanup --HG-- branch : fuzz --- Makefile.in | 1 + dbmalloc.c | 128 ++++++++++++++++++++++ dbmalloc.h | 17 +++ dbutil.c | 39 ------- dbutil.h | 5 +- fuzzer-preauth.c | 2 + libtomcrypt/src/headers/tomcrypt_custom.h | 11 ++ libtommath/tommath_class.h | 11 ++ 8 files changed, 171 insertions(+), 43 deletions(-) create mode 100644 dbmalloc.c create mode 100644 dbmalloc.h diff --git a/Makefile.in b/Makefile.in index 19c747f..d2f2eeb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -34,6 +34,7 @@ COMMONOBJS=dbutil.o buffer.o dbhelpers.o \ queue.o \ atomicio.o compat.o fake-rfc2553.o \ ltc_prng.o ecc.o ecdsa.o crypto_desc.o \ + dbmalloc.o \ gensignkey.o gendss.o genrsa.o SVROBJS=svr-kex.o svr-auth.o sshpty.o \ diff --git a/dbmalloc.c b/dbmalloc.c new file mode 100644 index 0000000..e62e020 --- /dev/null +++ b/dbmalloc.c @@ -0,0 +1,128 @@ +#include "dbmalloc.h" +#include "dbutil.h" + +#define LIST_SIZE 1000 + +struct dbmalloc_header { + unsigned int index; + unsigned int epoch; +}; + +static struct dbmalloc_header* dbmalloc_list[LIST_SIZE]; + +unsigned int current_epoch = 0; + +void m_malloc_set_epoch(unsigned int epoch) { + current_epoch = epoch; +} + +void m_malloc_free_epoch(unsigned int epoch) { + unsigned int i; + unsigned int freed = 0; + for (i = 0; i < LIST_SIZE; i++) { + if (dbmalloc_list[i] != NULL) { + assert(dbmalloc_list[i]->index == i); + if (dbmalloc_list[i]->epoch == epoch) { + free(dbmalloc_list[i]); + dbmalloc_list[i] = NULL; + freed++; + } + } + } + TRACE(("free_epoch freed %d", freed)) +} + +static void put_alloc(struct dbmalloc_header *header) { + unsigned int i; + for (i = 0; i < LIST_SIZE; i++) { + if (dbmalloc_list[i] == NULL) { + dbmalloc_list[i] = header; + header->index = i; + return; + } + } + dropbear_exit("ran out of dbmalloc entries"); +} + +static void remove_alloc(struct dbmalloc_header *header) { + assert(header->index < LIST_SIZE); + assert(dbmalloc_list[header->index] == header); + assert(header->epoch == current_epoch); + dbmalloc_list[header->index] = NULL; +} + +static struct dbmalloc_header* get_header(void* ptr) { + char* bptr = ptr; + return (struct dbmalloc_header*)&bptr[-sizeof(struct dbmalloc_header)]; +} + +void * m_malloc(size_t size) { + char* mem = NULL; + struct dbmalloc_header* header = NULL; + + if (size == 0 || size > 1e9) { + dropbear_exit("m_malloc failed"); + } + + size = size + sizeof(struct dbmalloc_header); + + mem = calloc(1, size); + if (mem == NULL) { + dropbear_exit("m_malloc failed"); + } + header = (struct dbmalloc_header*)mem; + put_alloc(header); + header->epoch = current_epoch; + return &mem[sizeof(struct dbmalloc_header)]; +} + +void * m_calloc(size_t nmemb, size_t size) { + assert(nmemb <= 1000 && size <= 10000); + return m_malloc(nmemb*size); +} + +void * m_realloc(void* ptr, size_t size) { + char* mem = NULL; + struct dbmalloc_header* header = NULL; + if (size == 0 || size > 1e9) { + dropbear_exit("m_realloc failed"); + } + + header = get_header(ptr); + remove_alloc(header); + + size = size + sizeof(struct dbmalloc_header); + mem = realloc(header, size); + if (mem == NULL) { + dropbear_exit("m_realloc failed"); + } + + header = (struct dbmalloc_header*)mem; + put_alloc(header); + return &mem[sizeof(struct dbmalloc_header)]; +} + +void m_free_direct(void* ptr) { + struct dbmalloc_header* header = NULL; + if (!ptr) { + return; + } + header = get_header(ptr); + remove_alloc(header); + free(header); +} + +void * m_strdup(const char * str) { + char* ret; + unsigned int len; + len = strlen(str); + + ret = m_malloc(len+1); + if (ret == NULL) { + dropbear_exit("m_strdup failed"); + } + memcpy(ret, str, len+1); + return ret; +} + + diff --git a/dbmalloc.h b/dbmalloc.h new file mode 100644 index 0000000..f6cccbb --- /dev/null +++ b/dbmalloc.h @@ -0,0 +1,17 @@ +#ifndef DBMALLOC_H_ +#define DBMALLOC_H_ + +#include "includes.h" + +void * m_malloc(size_t size); +/* m_calloc is limited in size, enough for libtomcrypt */ +void * m_calloc(size_t nmemb, size_t size); +void * m_strdup(const char * str); +void * m_realloc(void* ptr, size_t size); +void m_free_direct(void* ptr); +#define m_free(X) do {m_free_direct(X); (X) = NULL;} while (0) + +void m_malloc_set_epoch(unsigned int epoch); +void m_malloc_free_epoch(unsigned int epoch); + +#endif /* DBMALLOC_H_ */ diff --git a/dbutil.c b/dbutil.c index 4c835a4..e7e486a 100644 --- a/dbutil.c +++ b/dbutil.c @@ -520,45 +520,6 @@ void m_close(int fd) { } } -void * m_malloc(size_t size) { - - void* ret; - - if (size == 0) { - dropbear_exit("m_malloc failed"); - } - ret = calloc(1, size); - if (ret == NULL) { - dropbear_exit("m_malloc failed"); - } - return ret; - -} - -void * m_strdup(const char * str) { - char* ret; - - ret = strdup(str); - if (ret == NULL) { - dropbear_exit("m_strdup failed"); - } - return ret; -} - -void * m_realloc(void* ptr, size_t size) { - - void *ret; - - if (size == 0) { - dropbear_exit("m_realloc failed"); - } - ret = realloc(ptr, size); - if (ret == NULL) { - dropbear_exit("m_realloc failed"); - } - return ret; -} - void setnonblocking(int fd) { TRACE(("setnonblocking: %d", fd)) diff --git a/dbutil.h b/dbutil.h index d83b20a..3b3705e 100644 --- a/dbutil.h +++ b/dbutil.h @@ -30,6 +30,7 @@ #include "buffer.h" #include "queue.h" #include "dbhelpers.h" +#include "dbmalloc.h" #ifndef DISABLE_SYSLOG void startsyslog(const char *ident); @@ -66,10 +67,6 @@ int buf_readfile(buffer* buf, const char* filename); int buf_getline(buffer * line, FILE * authfile); void m_close(int fd); -void * m_malloc(size_t size); -void * m_strdup(const char * str); -void * m_realloc(void* ptr, size_t size); -#define m_free(X) do {free(X); (X) = NULL;} while (0) void setnonblocking(int fd); void disallow_core(void); int m_str_to_uint(const char* str, unsigned int *val); diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index 9ca7b84..7f0e136 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -23,9 +23,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { int fakesock = 1; wrapfd_add(fakesock, fuzz.input, PLAIN); + m_malloc_set_epoch(1); if (setjmp(fuzz.jmp) == 0) { svr_session(fakesock, fakesock); } else { + m_malloc_free_epoch(1); TRACE(("dropbear_exit longjmped")) // dropbear_exit jumped here } diff --git a/libtomcrypt/src/headers/tomcrypt_custom.h b/libtomcrypt/src/headers/tomcrypt_custom.h index 82cb26e..7f13dbd 100644 --- a/libtomcrypt/src/headers/tomcrypt_custom.h +++ b/libtomcrypt/src/headers/tomcrypt_custom.h @@ -4,6 +4,17 @@ /* compile options depend on Dropbear options.h */ #include "options.h" +void * m_malloc(size_t size); +/* m_calloc is limited in size, enough for libtomcrypt */ +void * m_calloc(size_t nmemb, size_t size); +void * m_realloc(void* ptr, size_t size); +void m_free_direct(void* ptr); + +#define XMALLOC m_malloc +#define XFREE m_free_direct +#define XREALLOC m_realloc +#define XCALLOC m_calloc + /* macros for various libc functions you can change for embedded targets */ #ifndef XMALLOC #ifdef malloc diff --git a/libtommath/tommath_class.h b/libtommath/tommath_class.h index a4e275a..ea7fa5c 100644 --- a/libtommath/tommath_class.h +++ b/libtommath/tommath_class.h @@ -1000,6 +1000,17 @@ #undef BN_MP_TOOM_MUL_C #undef BN_MP_TOOM_SQR_C +void * m_malloc(size_t size); +/* m_calloc is limited in size, enough for libtomcrypt */ +void * m_calloc(size_t nmemb, size_t size); +void * m_realloc(void* ptr, size_t size); +void m_free_direct(void* ptr); + +#define XMALLOC m_malloc +#define XFREE m_free_direct +#define XREALLOC m_realloc +#define XCALLOC m_calloc + /* $Source: /cvs/libtom/libtommath/tommath_class.h,v $ */ /* $Revision: 1.3 $ */ /* $Date: 2005/07/28 11:59:32 $ */ From 30d3ccd419b18e1ffbc214503446d1381119825a Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 21 May 2017 18:53:09 +0800 Subject: [PATCH 16/56] Fix null pointer dereference found by libfuzzer --HG-- branch : fuzz --- signkey.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/signkey.c b/signkey.c index 2c29431..192ba18 100644 --- a/signkey.c +++ b/signkey.c @@ -102,7 +102,8 @@ enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen) return DROPBEAR_SIGNKEY_NONE; } -/* Returns a pointer to the key part specific to "type" */ +/* Returns a pointer to the key part specific to "type". +Be sure to check both (ret != NULL) and (*ret != NULL) */ void ** signkey_key_ptr(sign_key *key, enum signkey_type type) { switch (type) { @@ -294,7 +295,7 @@ void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type) { #if DROPBEAR_ECDSA if (signkey_is_ecdsa(type)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); - if (eck) { + if (eck && *eck) { buf_put_ecdsa_pub_key(pubkeys, *eck); } } @@ -331,7 +332,7 @@ void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type) { #if DROPBEAR_ECDSA if (signkey_is_ecdsa(type)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); - if (eck) { + if (eck && *eck) { buf_put_ecdsa_priv_key(buf, *eck); TRACE(("leave buf_put_priv_key: ecdsa done")) return; @@ -495,7 +496,7 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, #if DROPBEAR_ECDSA if (signkey_is_ecdsa(type)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); - if (eck) { + if (eck && *eck) { buf_put_ecdsa_sign(sigblob, *eck, data_buf); } } From 18ea116827eda29ba71ac30e96257937e965941b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 21 May 2017 18:53:33 +0800 Subject: [PATCH 17/56] -v for debug_trace --HG-- branch : fuzz --- fuzz-harness.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fuzz-harness.c b/fuzz-harness.c index c31d2b7..41bd946 100644 --- a/fuzz-harness.c +++ b/fuzz-harness.c @@ -8,11 +8,20 @@ int main(int argc, char ** argv) { int i; buffer *input = buf_new(100000); -#if DROPBEAR_TRACE - debug_trace = 1; + for (i = 1; i < argc; i++) { +#if DEBUG_TRACE + if (strcmp(argv[i], "-v") == 0) { + debug_trace = 1; + } #endif + } for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + // ignore arguments + continue; + } + char* fn = argv[i]; buf_setlen(input, 0); buf_readfile(input, fn); From 50bde9976b7ef51a7cd4f00cf9734387aa4ce33e Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 21 May 2017 18:53:44 +0800 Subject: [PATCH 18/56] seed_fuzz() prototype --HG-- branch : fuzz --- dbrandom.h | 3 +++ fuzzer-preauth.c | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dbrandom.h b/dbrandom.h index 6bfb866..4ccd622 100644 --- a/dbrandom.h +++ b/dbrandom.h @@ -31,5 +31,8 @@ void seedrandom(void); void genrandom(unsigned char* buf, unsigned int len); void addrandom(unsigned char * buf, unsigned int len); void gen_random_mpint(mp_int *max, mp_int *rand); +#ifdef DROPBEAR_FUZZ +void seedfuzz(void); +#endif #endif /* DROPBEAR_RANDOM_H_ */ diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index 7f0e136..e1340da 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -1,5 +1,4 @@ #include "fuzz.h" -#include "dbrandom.h" #include "session.h" #include "fuzz-wrapfd.h" #include "debug.h" From 114438e66979926ad88756fb491d70172603abad Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 22 May 2017 22:09:26 +0800 Subject: [PATCH 19/56] zlib can use m_malloc/m_free too --HG-- branch : fuzz --- common-kex.c | 17 ++++++++++++----- dbmalloc.c | 4 +++- dbmalloc.h | 1 - 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/common-kex.c b/common-kex.c index a95182c..1f3d51b 100644 --- a/common-kex.c +++ b/common-kex.c @@ -391,6 +391,14 @@ int is_compress_recv() { && ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY); } +static void* dropbear_zalloc(void* UNUSED(opaque), uInt items, uInt size) { + return m_calloc(items, size); +} + +static void dropbear_zfree(void* UNUSED(opaque), void* ptr) { + m_free(ptr); +} + /* Set up new zlib compression streams, close the old ones. Only * called from gen_new_keys() */ static void gen_new_zstream_recv() { @@ -399,11 +407,10 @@ static void gen_new_zstream_recv() { if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB || ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream)); - ses.newkeys->recv.zstream->zalloc = Z_NULL; - ses.newkeys->recv.zstream->zfree = Z_NULL; + ses.newkeys->recv.zstream->zalloc = dropbear_zalloc; + ses.newkeys->recv.zstream->zfree = dropbear_zfree; if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) { - m_free(ses.newkeys->recv.zstream); dropbear_exit("zlib error"); } } else { @@ -424,8 +431,8 @@ static void gen_new_zstream_trans() { if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB || ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream)); - ses.newkeys->trans.zstream->zalloc = Z_NULL; - ses.newkeys->trans.zstream->zfree = Z_NULL; + ses.newkeys->trans.zstream->zalloc = dropbear_zalloc; + ses.newkeys->trans.zstream->zfree = dropbear_zfree; if (deflateInit2(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, DROPBEAR_ZLIB_WINDOW_BITS, diff --git a/dbmalloc.c b/dbmalloc.c index e62e020..0542bf9 100644 --- a/dbmalloc.c +++ b/dbmalloc.c @@ -77,7 +77,9 @@ void * m_malloc(size_t size) { } void * m_calloc(size_t nmemb, size_t size) { - assert(nmemb <= 1000 && size <= 10000); + if (SIZE_T_MAX / nmemb < size) { + dropbear_exit("m_calloc failed"); + } return m_malloc(nmemb*size); } diff --git a/dbmalloc.h b/dbmalloc.h index f6cccbb..f27ab39 100644 --- a/dbmalloc.h +++ b/dbmalloc.h @@ -4,7 +4,6 @@ #include "includes.h" void * m_malloc(size_t size); -/* m_calloc is limited in size, enough for libtomcrypt */ void * m_calloc(size_t nmemb, size_t size); void * m_strdup(const char * str); void * m_realloc(void* ptr, size_t size); From 84a143a605cd2658ad359feafda8fc48c2f0d575 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 22 May 2017 22:09:38 +0800 Subject: [PATCH 20/56] remove unneeded check --HG-- branch : fuzz --- common-channel.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common-channel.c b/common-channel.c index 14985a9..7383f47 100644 --- a/common-channel.c +++ b/common-channel.c @@ -88,10 +88,6 @@ void chancleanup() { unsigned int i; - if (!ses.channels) { - return; - } - TRACE(("enter chancleanup")) for (i = 0; i < ses.chansize; i++) { if (ses.channels[i] != NULL) { From a43b6b032369853d92cb6ab11bf89906f256c6a7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 22 May 2017 22:09:46 +0800 Subject: [PATCH 21/56] define SIZE_T_MAX --HG-- branch : fuzz --- includes.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes.h b/includes.h index b3e1357..0dd1417 100644 --- a/includes.h +++ b/includes.h @@ -156,6 +156,10 @@ typedef unsigned int u_int32_t; typedef u_int32_t uint32_t; #endif /* HAVE_UINT32_T */ +#ifndef SIZE_T_MAX +#define SIZE_T_MAX ULONG_MAX +#endif /* SIZE_T_MAX */ + #ifdef SO_PRIORITY #include #include From a582c4cdb60dbba5c34e33c0e434bdc52f09ad2c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 23 May 2017 22:29:21 +0800 Subject: [PATCH 22/56] split out checkpubkey_line() separately --HG-- branch : fuzz --- svr-authpubkey.c | 204 +++++++++++++++++++++++++---------------------- 1 file changed, 108 insertions(+), 96 deletions(-) diff --git a/svr-authpubkey.c b/svr-authpubkey.c index acc660d..6612194 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -188,6 +188,103 @@ static void send_msg_userauth_pk_ok(char* algo, unsigned int algolen, } +static int checkpubkey_line(buffer* line, int line_num, char* filename, + const char* algo, unsigned int algolen, + const unsigned char* keyblob, unsigned int keybloblen) { + buffer *options_buf = NULL; + unsigned int pos, len; + int ret = DROPBEAR_FAILURE; + + if (line->len < MIN_AUTHKEYS_LINE) { + TRACE(("checkpubkey: line too short")) + return DROPBEAR_FAILURE; /* line is too short for it to be a valid key */ + } + + /* check the key type */ + if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) { + int is_comment = 0; + unsigned char *options_start = NULL; + int options_len = 0; + int escape, quoted; + + /* skip over any comments or leading whitespace */ + while (line->pos < line->len) { + const char c = buf_getbyte(line); + if (c == ' ' || c == '\t') { + continue; + } else if (c == '#') { + is_comment = 1; + break; + } + buf_incrpos(line, -1); + break; + } + if (is_comment) { + /* next line */ + goto out; + } + + /* remember start of options */ + options_start = buf_getptr(line, 1); + quoted = 0; + escape = 0; + options_len = 0; + + /* figure out where the options are */ + while (line->pos < line->len) { + const char c = buf_getbyte(line); + if (!quoted && (c == ' ' || c == '\t')) { + break; + } + escape = (!escape && c == '\\'); + if (!escape && c == '"') { + quoted = !quoted; + } + options_len++; + } + options_buf = buf_new(options_len); + buf_putbytes(options_buf, options_start, options_len); + + /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */ + if (line->pos + algolen+3 > line->len) { + goto out; + } + if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) { + goto out; + } + } + buf_incrpos(line, algolen); + + /* check for space (' ') character */ + if (buf_getbyte(line) != ' ') { + TRACE(("checkpubkey: space character expected, isn't there")) + goto out; + } + + /* truncate the line at the space after the base64 data */ + pos = line->pos; + for (len = 0; line->pos < line->len; len++) { + if (buf_getbyte(line) == ' ') break; + } + buf_setpos(line, pos); + buf_setlen(line, line->pos + len); + + TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len)) + + ret = cmp_base64_key(keyblob, keybloblen, (const unsigned char *) algo, algolen, line, NULL); + + if (ret == DROPBEAR_SUCCESS && options_buf) { + ret = svr_add_pubkey_options(options_buf, line_num, filename); + } + +out: + if (options_buf) { + buf_free(options_buf); + } + return ret; +} + + /* Checks whether a specified publickey (and associated algorithm) is an * acceptable key for authentication */ /* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */ @@ -198,8 +295,7 @@ static int checkpubkey(char* algo, unsigned int algolen, char * filename = NULL; int ret = DROPBEAR_FAILURE; buffer * line = NULL; - unsigned int len, pos; - buffer * options_buf = NULL; + unsigned int len; int line_num; uid_t origuid; gid_t origgid; @@ -254,12 +350,6 @@ static int checkpubkey(char* algo, unsigned int algolen, /* iterate through the lines */ do { - /* new line : potentially new options */ - if (options_buf) { - buf_free(options_buf); - options_buf = NULL; - } - if (buf_getline(line, authfile) == DROPBEAR_FAILURE) { /* EOF reached */ TRACE(("checkpubkey: authorized_keys EOF reached")) @@ -267,91 +357,8 @@ static int checkpubkey(char* algo, unsigned int algolen, } line_num++; - if (line->len < MIN_AUTHKEYS_LINE) { - TRACE(("checkpubkey: line too short")) - continue; /* line is too short for it to be a valid key */ - } - - /* check the key type - will fail if there are options */ - TRACE(("a line!")) - - if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) { - int is_comment = 0; - unsigned char *options_start = NULL; - int options_len = 0; - int escape, quoted; - - /* skip over any comments or leading whitespace */ - while (line->pos < line->len) { - const char c = buf_getbyte(line); - if (c == ' ' || c == '\t') { - continue; - } else if (c == '#') { - is_comment = 1; - break; - } - buf_incrpos(line, -1); - break; - } - if (is_comment) { - /* next line */ - continue; - } - - /* remember start of options */ - options_start = buf_getptr(line, 1); - quoted = 0; - escape = 0; - options_len = 0; - - /* figure out where the options are */ - while (line->pos < line->len) { - const char c = buf_getbyte(line); - if (!quoted && (c == ' ' || c == '\t')) { - break; - } - escape = (!escape && c == '\\'); - if (!escape && c == '"') { - quoted = !quoted; - } - options_len++; - } - options_buf = buf_new(options_len); - buf_putbytes(options_buf, options_start, options_len); - - /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */ - if (line->pos + algolen+3 > line->len) { - continue; - } - if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) { - continue; - } - } - buf_incrpos(line, algolen); - - /* check for space (' ') character */ - if (buf_getbyte(line) != ' ') { - TRACE(("checkpubkey: space character expected, isn't there")) - continue; - } - - /* truncate the line at the space after the base64 data */ - pos = line->pos; - for (len = 0; line->pos < line->len; len++) { - if (buf_getbyte(line) == ' ') break; - } - buf_setpos(line, pos); - buf_setlen(line, line->pos + len); - - TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len)) - - ret = cmp_base64_key(keyblob, keybloblen, (const unsigned char *) algo, algolen, line, NULL); - - if (ret == DROPBEAR_SUCCESS && options_buf) { - ret = svr_add_pubkey_options(options_buf, line_num, filename); - } - - if (ret == DROPBEAR_SUCCESS) { + if (checkpubkey_line(line, line_num, filename, + algo, algolen, keyblob, keybloblen) == DROPBEAR_SUCCESS) { break; } @@ -367,9 +374,6 @@ out: buf_free(line); } m_free(filename); - if (options_buf) { - buf_free(options_buf); - } TRACE(("leave checkpubkey: ret=%d", ret)) return ret; } @@ -465,4 +469,12 @@ static int checkfileperm(char * filename) { return DROPBEAR_SUCCESS; } +#ifdef DROPBEAR_FUZZ +int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename, + const char* algo, unsigned int algolen, + const unsigned char* keyblob, unsigned int keybloblen) { + return checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen); +} +#endif + #endif From 0363d3c32e08d0c2d5ed0035a1b045259677f319 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 23 May 2017 22:43:34 +0800 Subject: [PATCH 23/56] fuzzer-pubkey --HG-- branch : fuzz --- Makefile.in | 5 ++++- dbrandom.c | 2 +- dbrandom.h | 3 --- dbutil.c | 7 +++++++ fuzz-common.c | 4 ++-- fuzz.h | 7 +++++++ fuzzer-pubkey.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ signkey.c | 5 +++++ 8 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 fuzzer-pubkey.c diff --git a/Makefile.in b/Makefile.in index d2f2eeb..ea41916 100644 --- a/Makefile.in +++ b/Makefile.in @@ -245,7 +245,7 @@ default_options.h: default_options.h.in ## Fuzzing targets # list of fuzz targets -FUZZ_TARGETS=fuzzer-preauth +FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey list-fuzz-targets: @echo $(FUZZ_TARGETS) @@ -265,6 +265,9 @@ fuzz-targets: $(FUZZ_TARGETS) fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ +fuzzer-pubkey: fuzzer-pubkey.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) + $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ + # run this to update hardcoded hostkeys for for fuzzing. # hostkeys.c is checked in to hg. fuzz-hostkeys: diff --git a/dbrandom.c b/dbrandom.c index bb9c4c8..3e6e78f 100644 --- a/dbrandom.c +++ b/dbrandom.c @@ -182,7 +182,7 @@ static void write_urandom() } #ifdef DROPBEAR_FUZZ -void seedfuzz(void) { +void fuzz_seed(void) { hash_state hs; sha1_init(&hs); sha1_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz")); diff --git a/dbrandom.h b/dbrandom.h index 4ccd622..6bfb866 100644 --- a/dbrandom.h +++ b/dbrandom.h @@ -31,8 +31,5 @@ void seedrandom(void); void genrandom(unsigned char* buf, unsigned int len); void addrandom(unsigned char * buf, unsigned int len); void gen_random_mpint(mp_int *max, mp_int *rand); -#ifdef DROPBEAR_FUZZ -void seedfuzz(void); -#endif #endif /* DROPBEAR_RANDOM_H_ */ diff --git a/dbutil.c b/dbutil.c index e7e486a..d9bb2dd 100644 --- a/dbutil.c +++ b/dbutil.c @@ -120,6 +120,13 @@ static void generic_dropbear_exit(int exitcode, const char* format, _dropbear_log(LOG_INFO, fmtbuf, param); +#ifdef DROPBEAR_FUZZ + // longjmp before cleaning up svr_opts + if (fuzz.fuzzing) { + longjmp(fuzz.jmp, 1); + } +#endif + exit(exitcode); } diff --git a/fuzz-common.c b/fuzz-common.c index 144bf14..42f6aab 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -13,7 +13,7 @@ struct dropbear_fuzz_options fuzz; static void load_fixed_hostkeys(void); -static void common_setup_fuzzer(void) { +void common_setup_fuzzer(void) { fuzz.fuzzing = 1; fuzz.wrapfds = 1; fuzz.input = m_malloc(sizeof(buffer)); @@ -47,7 +47,7 @@ int fuzzer_set_input(const uint8_t *Data, size_t Size) { uint32_t wrapseed = buf_getint(fuzz.input); wrapfd_setup(wrapseed); - seedfuzz(); + fuzz_seed(); return DROPBEAR_SUCCESS; } diff --git a/fuzz.h b/fuzz.h index d0e13b3..8a55976 100644 --- a/fuzz.h +++ b/fuzz.h @@ -10,12 +10,19 @@ #include "fuzz-wrapfd.h" // once per process +void common_setup_fuzzer(void); 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); +// fuzzer functions that intrude into general code void fuzz_kex_fakealgos(void); +int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename, + const char* algo, unsigned int algolen, + const unsigned char* keyblob, unsigned int keybloblen); +extern const char * const * fuzz_signkey_names; +void fuzz_seed(void); // fake IO wrappers #ifndef FUZZ_SKIP_WRAP diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c new file mode 100644 index 0000000..bed0798 --- /dev/null +++ b/fuzzer-pubkey.c @@ -0,0 +1,49 @@ +#include "fuzz.h" +#include "session.h" +#include "fuzz-wrapfd.h" +#include "debug.h" + +static void setup_fuzzer(void) { + common_setup_fuzzer(); +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static int once = 0; + if (!once) { + setup_fuzzer(); + once = 1; + } + + m_malloc_set_epoch(1); + + fuzz_seed(); + fuzz.input->data = (unsigned char*)Data; + fuzz.input->len = Size; + fuzz.input->size = Size; + fuzz.input->pos = 0; + + if (Size < 4) { + return 0; + } + + // choose a keytype based on input + uint8_t b = 0; + size_t i; + for (i = 0; i < Size; i++) { + b ^= Data[i]; + } + const char* algoname = fuzz_signkey_names[b%DROPBEAR_SIGNKEY_NUM_NAMED]; + const char* keyblob = "fakekeyblob"; + + if (setjmp(fuzz.jmp) == 0) { + fuzz_checkpubkey_line(fuzz.input, 5, "/home/me/authorized_keys", + algoname, strlen(algoname), + keyblob, strlen(keyblob)); + } else { + m_malloc_free_epoch(1); + TRACE(("dropbear_exit longjmped")) + // dropbear_exit jumped here + } + + return 0; +} diff --git a/signkey.c b/signkey.c index 192ba18..3d78e35 100644 --- a/signkey.c +++ b/signkey.c @@ -620,3 +620,8 @@ out: return ret; } #endif + +#ifdef DROPBEAR_FUZZ +const char * const * fuzz_signkey_names = signkey_names; + +#endif From 3a8517b06fa64c2052e182733dd1b319566a2315 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 23 May 2017 22:43:52 +0800 Subject: [PATCH 24/56] create fuzzer .options files --HG-- branch : fuzz --- Makefile.in | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index ea41916..94637ed 100644 --- a/Makefile.in +++ b/Makefile.in @@ -247,6 +247,8 @@ default_options.h: default_options.h.in # list of fuzz targets FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey +FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS)) + list-fuzz-targets: @echo $(FUZZ_TARGETS) @@ -260,7 +262,7 @@ svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs)) # build all the fuzzers. This will require fail to link unless built with # make fuzzers LIBS=-lFuzzer.a # or similar - the library provides main(). -fuzz-targets: $(FUZZ_TARGETS) +fuzz-targets: $(FUZZ_TARGETS) $(FUZZER_OPTIONS) fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ @@ -268,6 +270,10 @@ fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobj fuzzer-pubkey: fuzzer-pubkey.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ +fuzzer-%.options: Makefile + echo "[libfuzzer]" > $@ + echo "max_len = 50000" >> $@ + # run this to update hardcoded hostkeys for for fuzzing. # hostkeys.c is checked in to hg. fuzz-hostkeys: From ee5769f31fc5bef0c0f5f6977523918af892fa3b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2017 00:09:40 +0800 Subject: [PATCH 25/56] avoid NULL argument to base64 decode --HG-- branch : fuzz --- signkey.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/signkey.c b/signkey.c index 3d78e35..19a2c08 100644 --- a/signkey.c +++ b/signkey.c @@ -577,6 +577,10 @@ int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, /* now we have the actual data */ len = line->len - line->pos; + if (len == 0) { + /* base64_decode doesn't like NULL argument */ + return DROPBEAR_FAILURE; + } decodekeylen = len * 2; /* big to be safe */ decodekey = buf_new(decodekeylen); From 2bc55ff428f27ef174c5bcfd6e0d18690932eb42 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2017 00:10:18 +0800 Subject: [PATCH 26/56] don't exit encountering short lines --HG-- branch : fuzz --- svr-authpubkey.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 6612194..04d1b13 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -200,6 +200,10 @@ static int checkpubkey_line(buffer* line, int line_num, char* filename, return DROPBEAR_FAILURE; /* line is too short for it to be a valid key */ } + /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */ + if (line->pos + algolen+3 > line->len) { + goto out; + } /* check the key type */ if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) { int is_comment = 0; From cf2c4f44a2476172866f2d460ede18c0ea015968 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2017 00:12:07 +0800 Subject: [PATCH 27/56] fuzzers disable logging by default --HG-- branch : fuzz --- fuzz-common.c | 14 +++++++++++++- fuzz-harness.c | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fuzz-common.c b/fuzz-common.c index 42f6aab..d5fc9db 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -11,12 +11,14 @@ struct dropbear_fuzz_options fuzz; +static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param); static void load_fixed_hostkeys(void); void common_setup_fuzzer(void) { fuzz.fuzzing = 1; fuzz.wrapfds = 1; fuzz.input = m_malloc(sizeof(buffer)); + _dropbear_log = fuzz_dropbear_log; crypto_init(); } @@ -52,6 +54,17 @@ int fuzzer_set_input(const uint8_t *Data, size_t Size) { return DROPBEAR_SUCCESS; } +static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param) { + + char printbuf[1024]; + +#if DEBUG_TRACE + if (debug_trace) { + vsnprintf(printbuf, sizeof(printbuf), format, param); + fprintf(stderr, "%s\n", printbuf); + } +#endif +} void svr_setup_fuzzer(void) { struct passwd *pw; @@ -59,7 +72,6 @@ void svr_setup_fuzzer(void) { common_setup_fuzzer(); _dropbear_exit = svr_dropbear_exit; - _dropbear_log = svr_dropbear_log; char *argv[] = { "-E", diff --git a/fuzz-harness.c b/fuzz-harness.c index 41bd946..8f370ca 100644 --- a/fuzz-harness.c +++ b/fuzz-harness.c @@ -12,6 +12,7 @@ int main(int argc, char ** argv) { #if DEBUG_TRACE if (strcmp(argv[i], "-v") == 0) { debug_trace = 1; + TRACE(("debug printing on")) } #endif } From b17254925ddd82aa633ca8128751b93840fabc9b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2017 22:19:46 +0800 Subject: [PATCH 28/56] Just use memset, it should'be be optimised out in a separate file --HG-- branch : fuzz --- dbhelpers.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/dbhelpers.c b/dbhelpers.c index f7461d9..bb9c2fc 100644 --- a/dbhelpers.c +++ b/dbhelpers.c @@ -9,16 +9,8 @@ void m_burn(void *data, unsigned int len) { #elif defined(HAVE_EXPLICIT_BZERO) explicit_bzero(data, len); #else -/* Based on the method in David Wheeler's - * "Secure Programming for Linux and Unix HOWTO". May not be safe - * against link-time optimisation. */ - volatile char *p = data; - - if (data == NULL) - return; - while (len--) { - *p++ = 0x0; - } + volatile void *p = data; + memset(p, 0x0, len); #endif } From 87c4586d61da2b11d3310f6ef7705886a7155edd Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2017 22:20:10 +0800 Subject: [PATCH 29/56] fuzzing has a constant time --HG-- branch : fuzz --- dbutil.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dbutil.c b/dbutil.c index d9bb2dd..64e87e8 100644 --- a/dbutil.c +++ b/dbutil.c @@ -625,7 +625,14 @@ static clockid_t get_linux_clock_source() { #endif time_t monotonic_now() { +#ifdef DROPBEAR_FUZZ + if (fuzz.fuzzing) { + /* time stands still when fuzzing */ + return 5; + } +#endif #if defined(__linux__) && defined(SYS_clock_gettime) + { static clockid_t clock_source = -2; if (clock_source == -2) { @@ -642,9 +649,11 @@ time_t monotonic_now() { } return ts.tv_sec; } + } #endif /* linux clock_gettime */ #if defined(HAVE_MACH_ABSOLUTE_TIME) + { /* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */ static mach_timebase_info_data_t timebase_info; if (timebase_info.denom == 0) { @@ -652,6 +661,7 @@ time_t monotonic_now() { } return mach_absolute_time() * timebase_info.numer / timebase_info.denom / 1e9; + } #endif /* osx mach_absolute_time */ /* Fallback for everything else - this will sometimes go backwards */ From 095b067857df04ea498e7adf76dc0380d7deeb1d Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2017 22:21:23 +0800 Subject: [PATCH 30/56] limit input size --HG-- branch : fuzz --- svr-authpubkey.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 04d1b13..fbee63f 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -195,9 +195,9 @@ static int checkpubkey_line(buffer* line, int line_num, char* filename, unsigned int pos, len; int ret = DROPBEAR_FAILURE; - if (line->len < MIN_AUTHKEYS_LINE) { - TRACE(("checkpubkey: line too short")) - return DROPBEAR_FAILURE; /* line is too short for it to be a valid key */ + if (line->len < MIN_AUTHKEYS_LINE || line->len > MAX_AUTHKEYS_LINE) { + TRACE(("checkpubkey: bad line length %d", line->len)) + return DROPBEAR_FAILURE; } /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */ From b8fa71284709e4cbaef7398dc9fc17155f8d2f7a Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2017 22:21:49 +0800 Subject: [PATCH 31/56] rearrange, all fuzzers now call fuzzer_set_input() --HG-- branch : fuzz --- fuzz-common.c | 18 +----------------- fuzz-wrapfd.c | 6 +++++- fuzz-wrapfd.h | 3 ++- fuzz.h | 3 ++- fuzzer-preauth.c | 17 +++++++++++++++++ fuzzer-pubkey.c | 30 ++++++++++++------------------ 6 files changed, 39 insertions(+), 38 deletions(-) diff --git a/fuzz-common.c b/fuzz-common.c index d5fc9db..4c5da70 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -31,23 +31,7 @@ int fuzzer_set_input(const uint8_t *Data, size_t Size) { memset(&ses, 0x0, sizeof(ses)); memset(&svr_ses, 0x0, sizeof(svr_ses)); - - // get prefix. 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 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); + wrapfd_setup(); fuzz_seed(); diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c index c65ed38..759ccba 100644 --- a/fuzz-wrapfd.c +++ b/fuzz-wrapfd.c @@ -26,13 +26,17 @@ static int wrap_used[IOWRAP_MAXFD+1]; static unsigned int nused; static unsigned short rand_state[3]; -void wrapfd_setup(uint32_t seed) { +void wrapfd_setup() { TRACE(("wrapfd_setup %x", seed)) 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); +} + +void wrapfd_setseed(uint32_t seed) { *((uint32_t*)rand_state) = seed; nrand48(rand_state); } diff --git a/fuzz-wrapfd.h b/fuzz-wrapfd.h index 9358c1a..04477b9 100644 --- a/fuzz-wrapfd.h +++ b/fuzz-wrapfd.h @@ -10,7 +10,8 @@ enum wrapfd_mode { RANDOMIN, }; -void wrapfd_setup(uint32_t wrapseed); +void wrapfd_setup(); +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); diff --git a/fuzz.h b/fuzz.h index 8a55976..ae1a3dc 100644 --- a/fuzz.h +++ b/fuzz.h @@ -13,7 +13,8 @@ void common_setup_fuzzer(void); void svr_setup_fuzzer(void); -// once per input. returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE +// must be called once per fuzz iteration. +// returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE int fuzzer_set_input(const uint8_t *Data, size_t Size); // fuzzer functions that intrude into general code diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index e1340da..110624e 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -19,6 +19,23 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; } + // get prefix. 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 = 1; wrapfd_add(fakesock, fuzz.input, PLAIN); diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c index bed0798..a5ec96e 100644 --- a/fuzzer-pubkey.c +++ b/fuzzer-pubkey.c @@ -14,26 +14,20 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { once = 1; } + if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) { + return 0; + } + m_malloc_set_epoch(1); - fuzz_seed(); - fuzz.input->data = (unsigned char*)Data; - fuzz.input->len = Size; - fuzz.input->size = Size; - fuzz.input->pos = 0; - - if (Size < 4) { - return 0; - } - - // choose a keytype based on input - uint8_t b = 0; - size_t i; - for (i = 0; i < Size; i++) { - b ^= Data[i]; - } - const char* algoname = fuzz_signkey_names[b%DROPBEAR_SIGNKEY_NUM_NAMED]; - const char* keyblob = "fakekeyblob"; + // choose a keytype based on input + uint8_t b = 0; + size_t i; + for (i = 0; i < Size; i++) { + b ^= Data[i]; + } + const char* algoname = fuzz_signkey_names[b%DROPBEAR_SIGNKEY_NUM_NAMED]; + const char* keyblob = "blob"; // keep short if (setjmp(fuzz.jmp) == 0) { fuzz_checkpubkey_line(fuzz.input, 5, "/home/me/authorized_keys", From 65baa71b586bfbcda04cab2e4da29abeee4ba7a6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 26 May 2017 00:19:39 +0800 Subject: [PATCH 32/56] linked list dbmalloc now add non-free m_malloc_free_epoch() argument for leak detection --HG-- branch : fuzz --- dbmalloc.c | 68 +++++++++++++++++++++++++++++------------------- dbmalloc.h | 2 +- fuzzer-preauth.c | 3 ++- fuzzer-pubkey.c | 3 ++- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/dbmalloc.c b/dbmalloc.c index 0542bf9..e887d36 100644 --- a/dbmalloc.c +++ b/dbmalloc.c @@ -1,14 +1,17 @@ #include "dbmalloc.h" #include "dbutil.h" -#define LIST_SIZE 1000 - struct dbmalloc_header { - unsigned int index; unsigned int epoch; + struct dbmalloc_header *prev; + struct dbmalloc_header *next; }; -static struct dbmalloc_header* dbmalloc_list[LIST_SIZE]; +static void put_alloc(struct dbmalloc_header *header); +static void remove_alloc(struct dbmalloc_header *header); + +/* end of the linked list */ +static struct dbmalloc_header* staple; unsigned int current_epoch = 0; @@ -16,39 +19,50 @@ void m_malloc_set_epoch(unsigned int epoch) { current_epoch = epoch; } -void m_malloc_free_epoch(unsigned int epoch) { - unsigned int i; - unsigned int freed = 0; - for (i = 0; i < LIST_SIZE; i++) { - if (dbmalloc_list[i] != NULL) { - assert(dbmalloc_list[i]->index == i); - if (dbmalloc_list[i]->epoch == epoch) { - free(dbmalloc_list[i]); - dbmalloc_list[i] = NULL; - freed++; +void m_malloc_free_epoch(unsigned int epoch, int dofree) { + struct dbmalloc_header* header; + struct dbmalloc_header* nextheader = NULL; + struct dbmalloc_header* oldstaple = staple; + staple = NULL; + /* free allocations from this epoch, create a new staple-anchored list from + the remainder */ + for (header = oldstaple; header; header = nextheader) + { + nextheader = header->next; + if (header->epoch == epoch) { + if (dofree) { + free(header); } + } else { + header->prev = NULL; + header->next = NULL; + put_alloc(header); } } - TRACE(("free_epoch freed %d", freed)) } static void put_alloc(struct dbmalloc_header *header) { - unsigned int i; - for (i = 0; i < LIST_SIZE; i++) { - if (dbmalloc_list[i] == NULL) { - dbmalloc_list[i] = header; - header->index = i; - return; - } + assert(header->next == NULL); + assert(header->prev == NULL); + if (staple) { + staple->prev = header; } - dropbear_exit("ran out of dbmalloc entries"); + header->next = staple; + staple = header; } static void remove_alloc(struct dbmalloc_header *header) { - assert(header->index < LIST_SIZE); - assert(dbmalloc_list[header->index] == header); - assert(header->epoch == current_epoch); - dbmalloc_list[header->index] = NULL; + if (header->prev) { + header->prev->next = header->next; + } + if (header->next) { + header->next->prev = header->prev; + } + if (staple == header) { + staple = header->next; + } + header->prev = NULL; + header->next = NULL; } static struct dbmalloc_header* get_header(void* ptr) { diff --git a/dbmalloc.h b/dbmalloc.h index f27ab39..f05f8f5 100644 --- a/dbmalloc.h +++ b/dbmalloc.h @@ -11,6 +11,6 @@ void m_free_direct(void* ptr); #define m_free(X) do {m_free_direct(X); (X) = NULL;} while (0) void m_malloc_set_epoch(unsigned int epoch); -void m_malloc_free_epoch(unsigned int epoch); +void m_malloc_free_epoch(unsigned int epoch, int dofree); #endif /* DBMALLOC_H_ */ diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index 110624e..247f11b 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -42,8 +42,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { m_malloc_set_epoch(1); if (setjmp(fuzz.jmp) == 0) { svr_session(fakesock, fakesock); + m_malloc_free_epoch(1, 0); } else { - m_malloc_free_epoch(1); + m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) // dropbear_exit jumped here } diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c index a5ec96e..c5b0e00 100644 --- a/fuzzer-pubkey.c +++ b/fuzzer-pubkey.c @@ -33,8 +33,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { fuzz_checkpubkey_line(fuzz.input, 5, "/home/me/authorized_keys", algoname, strlen(algoname), keyblob, strlen(keyblob)); + m_malloc_free_epoch(1, 0); } else { - m_malloc_free_epoch(1); + m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) // dropbear_exit jumped here } From f24d93d4e411eb3bf192abdfc90312da62cd69a6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 26 May 2017 00:19:53 +0800 Subject: [PATCH 33/56] fix null pointer crash --HG-- branch : fuzz --- signkey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/signkey.c b/signkey.c index 19a2c08..fa66a1b 100644 --- a/signkey.c +++ b/signkey.c @@ -547,7 +547,7 @@ int buf_verify(buffer * buf, sign_key *key, buffer *data_buf) { #if DROPBEAR_ECDSA if (signkey_is_ecdsa(type)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); - if (eck) { + if (eck && *eck) { return buf_ecdsa_verify(buf, *eck, data_buf); } } From cbd5be1b82c182d2e55656d29f19ae7f3ac91dea Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 26 May 2017 00:20:01 +0800 Subject: [PATCH 34/56] add fuzzer-verify --HG-- branch : fuzz --- Makefile.in | 5 ++++- fuzzer-verify.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 fuzzer-verify.c diff --git a/Makefile.in b/Makefile.in index 94637ed..0ed124d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -245,7 +245,7 @@ default_options.h: default_options.h.in ## Fuzzing targets # list of fuzz targets -FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey +FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS)) @@ -270,6 +270,9 @@ fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobj fuzzer-pubkey: fuzzer-pubkey.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ +fuzzer-verify: fuzzer-verify.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) + $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ + fuzzer-%.options: Makefile echo "[libfuzzer]" > $@ echo "max_len = 50000" >> $@ diff --git a/fuzzer-verify.c b/fuzzer-verify.c new file mode 100644 index 0000000..7e23bc9 --- /dev/null +++ b/fuzzer-verify.c @@ -0,0 +1,44 @@ +#include "fuzz.h" +#include "session.h" +#include "fuzz-wrapfd.h" +#include "debug.h" + +static void setup_fuzzer(void) { + common_setup_fuzzer(); +} + +static buffer *verifydata; + +/* Tests reading a public key and verifying a signature */ +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static int once = 0; + if (!once) { + setup_fuzzer(); + verifydata = buf_new(30); + buf_putstring(verifydata, "x", 1); + once = 1; + } + + if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) { + return 0; + } + + m_malloc_set_epoch(1); + + if (setjmp(fuzz.jmp) == 0) { + sign_key *key = new_sign_key(); + enum signkey_type type = DROPBEAR_SIGNKEY_ANY; + if (buf_get_pub_key(fuzz.input, key, &type) == DROPBEAR_SUCCESS) { + /* Don't expect random fuzz input to verify */ + assert(buf_verify(fuzz.input, key, verifydata) == DROPBEAR_FAILURE); + } + sign_key_free(key); + m_malloc_free_epoch(1, 0); + } else { + m_malloc_free_epoch(1, 1); + TRACE(("dropbear_exit longjmped")) + // dropbear_exit jumped here + } + + return 0; +} From 25607c04a7210fd309f83226c3bb4de74e0c9d4d Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 26 May 2017 21:08:43 +0800 Subject: [PATCH 35/56] add m_mp_free_multi, be more careful freeing when failing to load keys --HG-- branch : fuzz --- bignum.c | 16 ++++++++++++++++ bignum.h | 1 + dss.c | 37 +++++++++++++------------------------ rsa.c | 28 +++------------------------- signkey.c | 13 ++++++++----- 5 files changed, 41 insertions(+), 54 deletions(-) diff --git a/bignum.c b/bignum.c index 3758052..b3e2b99 100644 --- a/bignum.c +++ b/bignum.c @@ -68,6 +68,22 @@ void m_mp_alloc_init_multi(mp_int **mp, ...) va_end(args); } +void m_mp_free_multi(mp_int **mp, ...) +{ + mp_int** cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (*cur_arg) { + mp_clear(*cur_arg); + } + m_free(*cur_arg); + cur_arg = va_arg(args, mp_int**); + } + va_end(args); +} + void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len) { if (mp_read_unsigned_bin(mp, (unsigned char*)bytes, len) != MP_OKAY) { diff --git a/bignum.h b/bignum.h index 59702c3..bab65ef 100644 --- a/bignum.h +++ b/bignum.h @@ -30,6 +30,7 @@ void m_mp_init(mp_int *mp); void m_mp_init_multi(mp_int *mp, ...) ATTRIB_SENTINEL; void m_mp_alloc_init_multi(mp_int **mp, ...) ATTRIB_SENTINEL; +void m_mp_free_multi(mp_int **mp, ...) ATTRIB_SENTINEL; void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len); void hash_process_mp(const struct ltc_hash_descriptor *hash_desc, hash_state *hs, mp_int *mp); diff --git a/dss.c b/dss.c index 7754107..1b15cf2 100644 --- a/dss.c +++ b/dss.c @@ -44,6 +44,7 @@ * These should be freed with dss_key_free. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key) { + int ret = DROPBEAR_FAILURE; TRACE(("enter buf_get_dss_pub_key")) dropbear_assert(key != NULL); @@ -56,17 +57,24 @@ int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key) { || buf_getmpint(buf, key->g) == DROPBEAR_FAILURE || buf_getmpint(buf, key->y) == DROPBEAR_FAILURE) { TRACE(("leave buf_get_dss_pub_key: failed reading mpints")) - return DROPBEAR_FAILURE; + ret = DROPBEAR_FAILURE; + goto out; } if (mp_count_bits(key->p) < MIN_DSS_KEYLEN) { dropbear_log(LOG_WARNING, "DSS key too short"); TRACE(("leave buf_get_dss_pub_key: short key")) - return DROPBEAR_FAILURE; + ret = DROPBEAR_FAILURE; + goto out; } + ret = DROPBEAR_SUCCESS; TRACE(("leave buf_get_dss_pub_key: success")) - return DROPBEAR_SUCCESS; +out: + if (ret == DROPBEAR_FAILURE) { + m_mp_free_multi(&key->p, &key->q, &key->g, &key->y, NULL); + } + return ret; } /* Same as buf_get_dss_pub_key, but reads a private "x" key at the end. @@ -86,7 +94,7 @@ int buf_get_dss_priv_key(buffer* buf, dropbear_dss_key *key) { m_mp_alloc_init_multi(&key->x, NULL); ret = buf_getmpint(buf, key->x); if (ret == DROPBEAR_FAILURE) { - m_free(key->x); + m_mp_free_multi(&key->x); } return ret; @@ -101,26 +109,7 @@ void dss_key_free(dropbear_dss_key *key) { TRACE2(("enter dsa_key_free: key == NULL")) return; } - if (key->p) { - mp_clear(key->p); - m_free(key->p); - } - if (key->q) { - mp_clear(key->q); - m_free(key->q); - } - if (key->g) { - mp_clear(key->g); - m_free(key->g); - } - if (key->y) { - mp_clear(key->y); - m_free(key->y); - } - if (key->x) { - mp_clear(key->x); - m_free(key->x); - } + m_mp_free_multi(&key->p, &key->q, &key->g, &key->y, &key->x, NULL); m_free(key); TRACE2(("leave dsa_key_free")) } diff --git a/rsa.c b/rsa.c index 0e6639e..f11f4c3 100644 --- a/rsa.c +++ b/rsa.c @@ -72,8 +72,7 @@ int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key) { ret = DROPBEAR_SUCCESS; out: if (ret == DROPBEAR_FAILURE) { - m_free(key->e); - m_free(key->n); + m_mp_free_multi(&key->e, &key->n, NULL); } return ret; } @@ -121,9 +120,7 @@ int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key) { ret = DROPBEAR_SUCCESS; out: if (ret == DROPBEAR_FAILURE) { - m_free(key->d); - m_free(key->p); - m_free(key->q); + m_mp_free_multi(&key->d, &key->p, &key->q, NULL); } TRACE(("leave buf_get_rsa_priv_key")) return ret; @@ -139,26 +136,7 @@ void rsa_key_free(dropbear_rsa_key *key) { TRACE2(("leave rsa_key_free: key == NULL")) return; } - if (key->d) { - mp_clear(key->d); - m_free(key->d); - } - if (key->e) { - mp_clear(key->e); - m_free(key->e); - } - if (key->n) { - mp_clear(key->n); - m_free(key->n); - } - if (key->p) { - mp_clear(key->p); - m_free(key->p); - } - if (key->q) { - mp_clear(key->q); - m_free(key->q); - } + m_mp_free_multi(&key->d, &key->e, &key->p, &key->q, &key->n, NULL); m_free(key); TRACE2(("leave rsa_key_free")) } diff --git a/signkey.c b/signkey.c index fa66a1b..f0c0f97 100644 --- a/signkey.c +++ b/signkey.c @@ -168,7 +168,8 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { key->dsskey = m_malloc(sizeof(*key->dsskey)); ret = buf_get_dss_pub_key(buf, key->dsskey); if (ret == DROPBEAR_FAILURE) { - m_free(key->dsskey); + dss_key_free(key->dsskey); + key->dsskey = NULL; } } #endif @@ -178,7 +179,8 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { key->rsakey = m_malloc(sizeof(*key->rsakey)); ret = buf_get_rsa_pub_key(buf, key->rsakey); if (ret == DROPBEAR_FAILURE) { - m_free(key->rsakey); + rsa_key_free(key->rsakey); + key->rsakey = NULL; } } #endif @@ -202,7 +204,6 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { TRACE2(("leave buf_get_pub_key")) return ret; - } /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail. @@ -237,7 +238,8 @@ int buf_get_priv_key(buffer *buf, sign_key *key, enum signkey_type *type) { key->dsskey = m_malloc(sizeof(*key->dsskey)); ret = buf_get_dss_priv_key(buf, key->dsskey); if (ret == DROPBEAR_FAILURE) { - m_free(key->dsskey); + dss_key_free(key->dsskey); + key->dsskey = NULL; } } #endif @@ -247,7 +249,8 @@ int buf_get_priv_key(buffer *buf, sign_key *key, enum signkey_type *type) { key->rsakey = m_malloc(sizeof(*key->rsakey)); ret = buf_get_rsa_priv_key(buf, key->rsakey); if (ret == DROPBEAR_FAILURE) { - m_free(key->rsakey); + rsa_key_free(key->rsakey); + key->rsakey = NULL; } } #endif From 2e298b25e43199162e1209616acf0485f0f94974 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 26 May 2017 22:09:30 +0800 Subject: [PATCH 36/56] fix building with DEBUG_TRACE --HG-- branch : fuzz --- fuzz-wrapfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c index 759ccba..dde8dde 100644 --- a/fuzz-wrapfd.c +++ b/fuzz-wrapfd.c @@ -27,7 +27,7 @@ static unsigned int nused; static unsigned short rand_state[3]; void wrapfd_setup() { - TRACE(("wrapfd_setup %x", seed)) + TRACE(("wrapfd_setup")) nused = 0; memset(wrap_fds, 0x0, sizeof(wrap_fds)); memset(wrap_used, 0x0, sizeof(wrap_used)); From 4d07aa315b8e019a3795da9a290ca4d71d7f87c2 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 26 May 2017 22:10:51 +0800 Subject: [PATCH 37/56] Disable setnonblocking(), get_socket_address(), set_sock_priority() for fuzzing --HG-- branch : fuzz --- dbutil.c | 19 +++++++++--------- fuzz-common.c | 16 +++++++++++++++ fuzz.h | 2 ++ fuzzer-preauth.c | 2 +- fuzzer-pubkey.c | 4 ++-- netio.c | 51 ++++++++++++++---------------------------------- 6 files changed, 45 insertions(+), 49 deletions(-) diff --git a/dbutil.c b/dbutil.c index 64e87e8..d04afb6 100644 --- a/dbutil.c +++ b/dbutil.c @@ -531,22 +531,21 @@ void setnonblocking(int fd) { TRACE(("setnonblocking: %d", fd)) +#ifdef DROPBEAR_FUZZ + if (fuzz.fuzzing) { + return; + } +#endif + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { if (errno == ENODEV) { /* Some devices (like /dev/null redirected in) * can't be set to non-blocking */ TRACE(("ignoring ENODEV for setnonblocking")) } else { -#ifdef DROPBEAR_FUZZ - if (fuzz.fuzzing) - { - TRACE(("fuzzing ignore setnonblocking failure for %d", fd)) - } - else -#endif - { - dropbear_exit("Couldn't set nonblocking"); - } + { + dropbear_exit("Couldn't set nonblocking"); + } } } TRACE(("leave setnonblocking")) diff --git a/fuzz-common.c b/fuzz-common.c index 4c5da70..2d9044c 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -115,3 +115,19 @@ static void load_fixed_hostkeys(void) { void fuzz_kex_fakealgos(void) { ses.newkeys->recv.crypt_mode = &dropbear_mode_none; } + +void fuzz_get_socket_address(int UNUSED(fd), char **local_host, char **local_port, + char **remote_host, char **remote_port, int UNUSED(host_lookup)) { + if (local_host) { + *local_host = m_strdup("fuzzlocalhost"); + } + if (local_port) { + *local_port = m_strdup("1234"); + } + if (remote_host) { + *remote_host = m_strdup("fuzzremotehost"); + } + if (remote_port) { + *remote_port = m_strdup("9876"); + } +} diff --git a/fuzz.h b/fuzz.h index ae1a3dc..9234440 100644 --- a/fuzz.h +++ b/fuzz.h @@ -24,6 +24,8 @@ int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename, const unsigned char* keyblob, unsigned int keybloblen); extern const char * const * fuzz_signkey_names; 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); // fake IO wrappers #ifndef FUZZ_SKIP_WRAP diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index 247f11b..7f31471 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -36,7 +36,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { uint32_t wrapseed = buf_getint(fuzz.input); wrapfd_setseed(wrapseed); - int fakesock = 1; + int fakesock = 20; wrapfd_add(fakesock, fuzz.input, PLAIN); m_malloc_set_epoch(1); diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c index c5b0e00..0b7c0f3 100644 --- a/fuzzer-pubkey.c +++ b/fuzzer-pubkey.c @@ -32,8 +32,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (setjmp(fuzz.jmp) == 0) { fuzz_checkpubkey_line(fuzz.input, 5, "/home/me/authorized_keys", algoname, strlen(algoname), - keyblob, strlen(keyblob)); - m_malloc_free_epoch(1, 0); + (unsigned char*)keyblob, strlen(keyblob)); + m_malloc_free_epoch(1, 0); } else { m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) diff --git a/netio.c b/netio.c index b482431..f5e287a 100644 --- a/netio.c +++ b/netio.c @@ -311,6 +311,12 @@ void set_sock_priority(int sock, enum dropbear_prio prio) { int so_prio_val = 0; #endif +#ifdef DROPBEAR_FUZZ + if (fuzz.fuzzing) { + TRACE(("fuzzing skips set_sock_prio")) + return; + } +#endif /* Don't log ENOTSOCK errors so that this can harmlessly be called * on a client '-J' proxy pipe */ @@ -482,40 +488,25 @@ void get_socket_address(int fd, char **local_host, char **local_port, { struct sockaddr_storage addr; socklen_t addrlen; + +#if DROPBEAR_FUZZ + if (fuzz.fuzzing) { + fuzz_get_socket_address(fd, local_host, local_port, remote_host, remote_port, host_lookup); + return; + } +#endif if (local_host || local_port) { addrlen = sizeof(addr); if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) { - if (errno == ENOTSOCK) { - // FUZZ - if (local_host) { - *local_host = m_strdup("notsocket"); - } - if (local_port) { - *local_port = m_strdup("999"); - } - return; - } else { - dropbear_exit("Failed socket address: %s", strerror(errno)); - } + dropbear_exit("Failed socket address: %s", strerror(errno)); } getaddrstring(&addr, local_host, local_port, host_lookup); } if (remote_host || remote_port) { addrlen = sizeof(addr); if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) { - if (errno == ENOTSOCK) { - // FUZZ - if (remote_host) { - *remote_host = m_strdup("notsocket"); - } - if (remote_port) { - *remote_port = m_strdup("999"); - } - return; - } else { - dropbear_exit("Failed socket address: %s", strerror(errno)); - } + dropbear_exit("Failed socket address: %s", strerror(errno)); } getaddrstring(&addr, remote_host, remote_port, host_lookup); } @@ -569,18 +560,6 @@ void getaddrstring(struct sockaddr_storage* addr, return; } else { /* if we can't do a numeric lookup, something's gone terribly wrong */ - if (ret == EAI_FAMILY) { - // FUZZ - // Fake it for non-socket input - if (ret_host) { - *ret_host = m_strdup("0.0.0.0"); - } - if (ret_port) - { - *ret_port = m_strdup("999"); - } - return; - } dropbear_exit("Failed lookup: %s", gai_strerror(ret)); } } From ea0e23c172ea1e97613c70550afbb5ce1e89fa10 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 30 May 2017 22:50:52 +0800 Subject: [PATCH 38/56] don't longjmp for fuzzer-preauth (temporary to debug asan) --HG-- branch : fuzz --- fuzzer-preauth.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index 7f31471..12b7fc2 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -19,35 +19,39 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; } - // get prefix. input format is - // string prefix - // uint32 wrapfd seed - // ... to be extended later - // [bytes] ssh input stream + // get prefix. 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); + // 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 = 20; wrapfd_add(fakesock, fuzz.input, PLAIN); m_malloc_set_epoch(1); + // temporarily disable setjmp to debug asan segv + svr_session(fakesock, fakesock); + #if 0 if (setjmp(fuzz.jmp) == 0) { svr_session(fakesock, fakesock); - m_malloc_free_epoch(1, 0); + m_malloc_free_epoch(1, 0); } else { m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) // dropbear_exit jumped here } + #endif return 0; } From 81b64ea0b592d0ac49807af0652d153d68665fbe Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 1 Jun 2017 21:30:26 +0800 Subject: [PATCH 39/56] Add a flag whether to longjmp, missed that last commit --HG-- branch : fuzz --- dbutil.c | 2 +- fuzz-common.c | 1 + fuzz.h | 1 + fuzzer-preauth.c | 4 +++- svr-session.c | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dbutil.c b/dbutil.c index d04afb6..aadc20e 100644 --- a/dbutil.c +++ b/dbutil.c @@ -122,7 +122,7 @@ static void generic_dropbear_exit(int exitcode, const char* format, #ifdef DROPBEAR_FUZZ // longjmp before cleaning up svr_opts - if (fuzz.fuzzing) { + if (fuzz.do_jmp) { longjmp(fuzz.jmp, 1); } #endif diff --git a/fuzz-common.c b/fuzz-common.c index 2d9044c..5c365d2 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -17,6 +17,7 @@ static void load_fixed_hostkeys(void); void common_setup_fuzzer(void) { fuzz.fuzzing = 1; fuzz.wrapfds = 1; + fuzz.do_jmp = 1; fuzz.input = m_malloc(sizeof(buffer)); _dropbear_log = fuzz_dropbear_log; crypto_init(); diff --git a/fuzz.h b/fuzz.h index 9234440..e3bcb7f 100644 --- a/fuzz.h +++ b/fuzz.h @@ -49,6 +49,7 @@ struct dropbear_fuzz_options { int wrapfds; // dropbear_exit() jumps back + int do_jmp; sigjmp_buf jmp; uid_t pw_uid; diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index 12b7fc2..e65a3bc 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -12,6 +12,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { static int once = 0; if (!once) { setup_fuzzer(); + // XXX temporarily disable setjmp to debug asan segv + fuzz.do_jmp = 0; once = 1; } @@ -40,7 +42,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { wrapfd_add(fakesock, fuzz.input, PLAIN); m_malloc_set_epoch(1); - // temporarily disable setjmp to debug asan segv + // XXX temporarily disable setjmp to debug asan segv svr_session(fakesock, fakesock); #if 0 if (setjmp(fuzz.jmp) == 0) { diff --git a/svr-session.c b/svr-session.c index a1466fe..543a830 100644 --- a/svr-session.c +++ b/svr-session.c @@ -185,7 +185,7 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { #ifdef DROPBEAR_FUZZ // longjmp before cleaning up svr_opts - if (fuzz.fuzzing) { + if (fuzz.do_jmp) { longjmp(fuzz.jmp, 1); } #endif From 937e6cb91e3b0400fc516893862de74a2440501c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 2 Jun 2017 00:03:51 +0800 Subject: [PATCH 40/56] fuzzer-preauth don't call getpwnam(), bring back longjmp --HG-- branch : fuzz --- fuzz-common.c | 8 +++----- fuzzer-preauth.c | 6 ------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/fuzz-common.c b/fuzz-common.c index 5c365d2..ac42def 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -66,11 +66,9 @@ void svr_setup_fuzzer(void) { svr_getopts(argc, argv); /* user lookups might be slow, cache it */ - pw = getpwuid(getuid()); - dropbear_assert(pw); - fuzz.pw_name = m_strdup(pw->pw_name); - fuzz.pw_dir = m_strdup(pw->pw_dir); - fuzz.pw_shell = m_strdup(pw->pw_shell); + 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(); diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index e65a3bc..f7ced9d 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -12,8 +12,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { static int once = 0; if (!once) { setup_fuzzer(); - // XXX temporarily disable setjmp to debug asan segv - fuzz.do_jmp = 0; once = 1; } @@ -42,9 +40,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { wrapfd_add(fakesock, fuzz.input, PLAIN); m_malloc_set_epoch(1); - // XXX temporarily disable setjmp to debug asan segv - svr_session(fakesock, fakesock); - #if 0 if (setjmp(fuzz.jmp) == 0) { svr_session(fakesock, fakesock); m_malloc_free_epoch(1, 0); @@ -53,7 +48,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { TRACE(("dropbear_exit longjmped")) // dropbear_exit jumped here } - #endif return 0; } From 723ec19eedd2618cd850e703519fd8432c67e44c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 11 Jun 2017 21:39:40 +0800 Subject: [PATCH 41/56] fix checkmac always failing pre-kex --HG-- branch : fuzz --- packet.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packet.c b/packet.c index a02cb1b..63c813b 100644 --- a/packet.c +++ b/packet.c @@ -372,9 +372,10 @@ static int checkmac() { #ifdef DROPBEAR_FUZZ if (fuzz.fuzzing) { - // fail 1 in 1000 times to test error path + // fail 1 in 2000 times to test error path. + // note that mac_bytes is all zero prior to kex, so don't test ==0 ! unsigned int value = *((unsigned int*)&mac_bytes); - if (value % 1000 == 0) { + if (value % 2000 == 99) { return DROPBEAR_FAILURE; } return DROPBEAR_SUCCESS; From e3246ceb7e5e1d49b0a751012c064957518e483c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 16 Jun 2017 22:35:18 +0800 Subject: [PATCH 42/56] check p and q lengths --HG-- branch : fuzz --- dss.c | 13 ++++++++++--- dss.h | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/dss.c b/dss.c index 8f4f195..a3b4dce 100644 --- a/dss.c +++ b/dss.c @@ -61,8 +61,15 @@ int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key) { goto out; } - if (mp_count_bits(key->p) < MIN_DSS_KEYLEN) { - dropbear_log(LOG_WARNING, "DSS key too short"); + if (mp_count_bits(key->p) < DSS_P_BITS) { + dropbear_log(LOG_WARNING, "Bad DSS p"); + TRACE(("leave buf_get_dss_pub_key: short key")) + ret = DROPBEAR_FAILURE; + goto out; + } + + if (mp_count_bits(key->q) < DSS_Q_BITS) { + dropbear_log(LOG_WARNING, "Bad DSS q"); TRACE(("leave buf_get_dss_pub_key: short key")) ret = DROPBEAR_FAILURE; goto out; @@ -94,7 +101,7 @@ int buf_get_dss_priv_key(buffer* buf, dropbear_dss_key *key) { m_mp_alloc_init_multi(&key->x, NULL); ret = buf_getmpint(buf, key->x); if (ret == DROPBEAR_FAILURE) { - m_mp_free_multi(&key->x); + m_mp_free_multi(&key->x, NULL); } return ret; diff --git a/dss.h b/dss.h index adf2d55..4d11c0a 100644 --- a/dss.h +++ b/dss.h @@ -41,6 +41,9 @@ typedef struct { } dropbear_dss_key; +#define DSS_P_BITS 1024 +#define DSS_Q_BITS 160 + void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, buffer *data_buf); #if DROPBEAR_SIGNKEY_VERIFY int buf_dss_verify(buffer* buf, dropbear_dss_key *key, buffer *data_buf); From 8e1ea0f27b596882356dcbb4beb647ce69c1fe9d Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 16 Jun 2017 22:35:32 +0800 Subject: [PATCH 43/56] increase min DSS and RSA lengths --HG-- branch : fuzz --- sysoptions.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysoptions.h b/sysoptions.h index 441e3a1..63e5515 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -40,10 +40,10 @@ /* Minimum key sizes for DSS and RSA */ #ifndef MIN_DSS_KEYLEN -#define MIN_DSS_KEYLEN 512 +#define MIN_DSS_KEYLEN 1024 #endif #ifndef MIN_RSA_KEYLEN -#define MIN_RSA_KEYLEN 512 +#define MIN_RSA_KEYLEN 1024 #endif #define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */ From 2cbe70ba344b2af273aab8d1a4e228faa0934751 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 20 Jun 2017 20:07:25 +0800 Subject: [PATCH 44/56] verify debug printing --HG-- branch : fuzz --- dss.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dss.c b/dss.c index a3b4dce..91412ae 100644 --- a/dss.c +++ b/dss.c @@ -174,6 +174,13 @@ int buf_dss_verify(buffer* buf, dropbear_dss_key *key, buffer *data_buf) { goto out; } +#if DEBUG_DSS_VERIFY + printmpint("dss verify p", key->p); + printmpint("dss verify q", key->q); + printmpint("dss verify g", key->g); + printmpint("dss verify x", key->x); +#endif + /* hash the data */ sha1_init(&hs); sha1_process(&hs, data_buf->data, data_buf->len); @@ -183,6 +190,9 @@ int buf_dss_verify(buffer* buf, dropbear_dss_key *key, buffer *data_buf) { /* w = (s')-1 mod q */ /* let val1 = s' */ bytes_to_mp(&val1, (const unsigned char*) &string[SHA1_HASH_SIZE], SHA1_HASH_SIZE); +#if DEBUG_DSS_VERIFY + printmpint("dss verify s'", &val1); +#endif if (mp_cmp(&val1, key->q) != MP_LT) { TRACE(("verify failed, s' >= q")) @@ -200,6 +210,9 @@ int buf_dss_verify(buffer* buf, dropbear_dss_key *key, buffer *data_buf) { /* u1 = ((SHA(M')w) mod q */ /* let val1 = SHA(M') = msghash */ bytes_to_mp(&val1, msghash, SHA1_HASH_SIZE); +#if DEBUG_DSS_VERIFY + printmpint("dss verify r'", &val1); +#endif /* let val3 = u1 = ((SHA(M')w) mod q */ if (mp_mulmod(&val1, &val2, key->q, &val3) != MP_OKAY) { From 17a9b8802f0306010f5d1f071dce2b3ca80ae415 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 24 Jun 2017 10:34:58 +0800 Subject: [PATCH 45/56] fix dss debug printing --HG-- branch : fuzz --- dbutil.c | 1 + dss.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dbutil.c b/dbutil.c index aadc20e..5ebad15 100644 --- a/dbutil.c +++ b/dbutil.c @@ -399,6 +399,7 @@ void printhex(const char * label, const unsigned char * buf, int len) { void printmpint(const char *label, mp_int *mp) { buffer *buf = buf_new(1000); buf_putmpint(buf, mp); + fprintf(stderr, "%d bits ", mp_count_bits(mp)); printhex(label, buf->data, buf->len); buf_free(buf); diff --git a/dss.c b/dss.c index 00899cf..8f80421 100644 --- a/dss.c +++ b/dss.c @@ -176,7 +176,7 @@ int buf_dss_verify(buffer* buf, dropbear_dss_key *key, buffer *data_buf) { printmpint("dss verify p", key->p); printmpint("dss verify q", key->q); printmpint("dss verify g", key->g); - printmpint("dss verify x", key->x); + printmpint("dss verify y", key->y); #endif /* hash the data */ From 4b7105dfea64f4bcea529f552579be62db7683b6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 30 Jun 2017 21:10:57 +0800 Subject: [PATCH 46/56] fix DROBPEAR_FUZZ auth delay --HG-- branch : fuzz --- svr-auth.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/svr-auth.c b/svr-auth.c index ac1086d..236eb39 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -358,10 +358,12 @@ void send_msg_userauth_failure(int partial, int incrfail) { genrandom((unsigned char*)&delay, sizeof(delay)); /* We delay for 300ms +- 50ms */ delay = 250000 + (delay % 100000); -#ifndef DROPBEAR_FUZZ +#ifdef DROPBEAR_FUZZ if (!fuzz.fuzzing) { usleep(delay); } +#else + usleep(delay); #endif ses.authstate.failcount++; } From 5cd003d9e6d36fcca0ef40f12b8c8e4e540c58c7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 14 Aug 2017 00:00:10 +0800 Subject: [PATCH 47/56] check dss g range --HG-- branch : fuzz --- dss.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dss.c b/dss.c index 8f80421..fc91ff2 100644 --- a/dss.c +++ b/dss.c @@ -73,6 +73,18 @@ int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key) { goto out; } + /* test 1 < g < p */ + if (mp_cmp_d(key->g, 1) != MP_GT) { + dropbear_log(LOG_WARNING, "Bad DSS g"); + ret = DROPBEAR_FAILURE; + goto out; + } + if (mp_cmp(key->g, key->p) != MP_LT) { + dropbear_log(LOG_WARNING, "Bad DSS g"); + ret = DROPBEAR_FAILURE; + goto out; + } + ret = DROPBEAR_SUCCESS; TRACE(("leave buf_get_dss_pub_key: success")) out: From 89bdf3b0b9376204b039f38f8bebffa92cb30d4e Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 23 Jan 2018 23:05:47 +0800 Subject: [PATCH 48/56] add fuzzer-preauth_nomaths --HG-- branch : fuzz --- Makefile.in | 6 +++- common-kex.c | 4 +-- debug.h | 2 ++ fuzz-common.c | 62 +++++++++++++++++++++++++++++++++++++--- fuzz.h | 12 ++++++-- fuzzer-preauth.c | 51 ++------------------------------- fuzzer-preauth_nomaths.c | 6 ++++ fuzzer-pubkey.c | 4 +-- fuzzer-verify.c | 4 +-- kex.h | 1 + svr-kex.c | 7 +++++ 11 files changed, 95 insertions(+), 64 deletions(-) create mode 100644 fuzzer-preauth_nomaths.c diff --git a/Makefile.in b/Makefile.in index 1d0a858..2a32549 100644 --- a/Makefile.in +++ b/Makefile.in @@ -247,7 +247,7 @@ default_options.h: default_options.h.in ## Fuzzing targets # list of fuzz targets -FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify +FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS)) @@ -269,6 +269,10 @@ fuzz-targets: $(FUZZ_TARGETS) $(FUZZER_OPTIONS) fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ +fuzzer-preauth_nomaths: fuzzer-preauth_nomaths.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) + $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ + + fuzzer-pubkey: fuzzer-pubkey.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ diff --git a/common-kex.c b/common-kex.c index fc2daad..195ffc1 100644 --- a/common-kex.c +++ b/common-kex.c @@ -48,7 +48,6 @@ static void read_kex_algos(void); /* helper function for gen_new_keys */ static void hashkeys(unsigned char *out, unsigned int outlen, const hash_state * hs, const unsigned char X); -static void finish_kexhashbuf(void); /* Send our list of algorithms we can use */ @@ -769,8 +768,7 @@ void kexcurve25519_comb_key(struct kex_curve25519_param *param, buffer *buf_pub_ #endif /* DROPBEAR_CURVE25519 */ - -static void finish_kexhashbuf(void) { +void finish_kexhashbuf(void) { hash_state hs; const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc; diff --git a/debug.h b/debug.h index 0cd611d..ce7af53 100644 --- a/debug.h +++ b/debug.h @@ -49,7 +49,9 @@ /*#define CHECKCLEARTOWRITE() assert(ses.writepayload->len == 0 && \ ses.writepayload->pos == 0)*/ +#ifndef CHECKCLEARTOWRITE #define CHECKCLEARTOWRITE() +#endif /* Define this, compile with -pg and set GMON_OUT_PREFIX=gmon to get gmon * output when Dropbear forks. This will allow it gprof to be used. diff --git a/fuzz-common.c b/fuzz-common.c index ac42def..d98043e 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -14,7 +14,7 @@ struct dropbear_fuzz_options fuzz; static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param); static void load_fixed_hostkeys(void); -void common_setup_fuzzer(void) { +void fuzz_common_setup(void) { fuzz.fuzzing = 1; fuzz.wrapfds = 1; fuzz.do_jmp = 1; @@ -23,7 +23,7 @@ void common_setup_fuzzer(void) { crypto_init(); } -int fuzzer_set_input(const uint8_t *Data, size_t Size) { +int fuzz_set_input(const uint8_t *Data, size_t Size) { fuzz.input->data = (unsigned char*)Data; fuzz.input->size = Size; @@ -51,10 +51,10 @@ static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list #endif } -void svr_setup_fuzzer(void) { +void fuzz_svr_setup(void) { struct passwd *pw; - common_setup_fuzzer(); + fuzz_common_setup(); _dropbear_exit = svr_dropbear_exit; @@ -130,3 +130,57 @@ void fuzz_get_socket_address(int UNUSED(fd), char **local_host, char **local_por *remote_port = m_strdup("9876"); } } + +/* cut down version of svr_send_msg_kexdh_reply() that skips slow maths. Still populates structures */ +void fuzz_fake_send_kexdh_reply(void) { + assert(!ses.dh_K); + m_mp_alloc_init_multi(&ses.dh_K, NULL); + mp_set_int(ses.dh_K, 12345678); + finish_kexhashbuf(); + assert(!ses.dh_K); +} + +int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) { + static int once = 0; + if (!once) { + fuzz_svr_setup(); + fuzz.skip_kexmaths = skip_kexmaths; + once = 1; + } + + if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) { + return 0; + } + + // get prefix. 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 = 20; + wrapfd_add(fakesock, fuzz.input, PLAIN); + + m_malloc_set_epoch(1); + if (setjmp(fuzz.jmp) == 0) { + svr_session(fakesock, fakesock); + m_malloc_free_epoch(1, 0); + } else { + m_malloc_free_epoch(1, 1); + TRACE(("dropbear_exit longjmped")) + // dropbear_exit jumped here + } + + return 0; +} diff --git a/fuzz.h b/fuzz.h index e3bcb7f..7569130 100644 --- a/fuzz.h +++ b/fuzz.h @@ -10,12 +10,14 @@ #include "fuzz-wrapfd.h" // once per process -void common_setup_fuzzer(void); -void svr_setup_fuzzer(void); +void fuzz_common_setup(void); +void fuzz_svr_setup(void); // must be called once per fuzz iteration. // returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE -int fuzzer_set_input(const uint8_t *Data, size_t Size); +int fuzz_set_input(const uint8_t *Data, size_t Size); + +int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths); // fuzzer functions that intrude into general code void fuzz_kex_fakealgos(void); @@ -26,6 +28,7 @@ extern const char * const * fuzz_signkey_names; 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); // fake IO wrappers #ifndef FUZZ_SKIP_WRAP @@ -48,6 +51,9 @@ struct dropbear_fuzz_options { struct dropbear_hash recv_mac; int wrapfds; + // whether to skip slow bignum maths + int skip_kexmaths; + // dropbear_exit() jumps back int do_jmp; sigjmp_buf jmp; diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c index f7ced9d..3ac49f4 100644 --- a/fuzzer-preauth.c +++ b/fuzzer-preauth.c @@ -1,53 +1,6 @@ #include "fuzz.h" -#include "session.h" -#include "fuzz-wrapfd.h" -#include "debug.h" - -static void setup_fuzzer(void) { - svr_setup_fuzzer(); - //debug_trace = 1; -} int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - static int once = 0; - if (!once) { - setup_fuzzer(); - once = 1; - } - - if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) { - return 0; - } - - // get prefix. 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 = 20; - wrapfd_add(fakesock, fuzz.input, PLAIN); - - m_malloc_set_epoch(1); - if (setjmp(fuzz.jmp) == 0) { - svr_session(fakesock, fakesock); - m_malloc_free_epoch(1, 0); - } else { - m_malloc_free_epoch(1, 1); - TRACE(("dropbear_exit longjmped")) - // dropbear_exit jumped here - } - - return 0; + return fuzz_run_preauth(Data, Size, 0); } + diff --git a/fuzzer-preauth_nomaths.c b/fuzzer-preauth_nomaths.c new file mode 100644 index 0000000..efdc2c3 --- /dev/null +++ b/fuzzer-preauth_nomaths.c @@ -0,0 +1,6 @@ +#include "fuzz.h" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + return fuzz_run_preauth(Data, Size, 1); +} + diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c index 0b7c0f3..511357b 100644 --- a/fuzzer-pubkey.c +++ b/fuzzer-pubkey.c @@ -4,7 +4,7 @@ #include "debug.h" static void setup_fuzzer(void) { - common_setup_fuzzer(); + fuzz_common_setup(); } int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { @@ -14,7 +14,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { once = 1; } - if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) { + if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) { return 0; } diff --git a/fuzzer-verify.c b/fuzzer-verify.c index 7e23bc9..ed4a9f7 100644 --- a/fuzzer-verify.c +++ b/fuzzer-verify.c @@ -4,7 +4,7 @@ #include "debug.h" static void setup_fuzzer(void) { - common_setup_fuzzer(); + fuzz_common_setup(); } static buffer *verifydata; @@ -19,7 +19,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { once = 1; } - if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) { + if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) { return 0; } diff --git a/kex.h b/kex.h index 7b98cd5..3d66ab6 100644 --- a/kex.h +++ b/kex.h @@ -34,6 +34,7 @@ void recv_msg_kexinit(void); void send_msg_newkeys(void); void recv_msg_newkeys(void); void kexfirstinitialise(void); +void finish_kexhashbuf(void); struct kex_dh_param *gen_kexdh_param(void); void free_kexdh_param(struct kex_dh_param *param); diff --git a/svr-kex.c b/svr-kex.c index 7108f64..daed9d4 100644 --- a/svr-kex.c +++ b/svr-kex.c @@ -179,6 +179,13 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { } #endif +#ifdef DROPBEAR_FUZZ + if (fuzz.fuzzing && fuzz.skip_kexmaths) { + fuzz_fake_send_kexdh_reply(); + return; + } +#endif + buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); buf_put_pub_key(ses.writepayload, svr_opts.hostkey, ses.newkeys->algo_hostkey); From f7a664f127d3dfde0e7c7a9ca74b1d14f9a2f983 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 23 Jan 2018 23:27:40 +0800 Subject: [PATCH 49/56] fix bad assertion --HG-- branch : fuzz --- fuzz-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz-common.c b/fuzz-common.c index d98043e..b596821 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -7,6 +7,7 @@ #include "crypto_desc.h" #include "session.h" #include "dbrandom.h" +#include "bignum.h" #include "fuzz-wrapfd.h" struct dropbear_fuzz_options fuzz; @@ -137,7 +138,6 @@ void fuzz_fake_send_kexdh_reply(void) { m_mp_alloc_init_multi(&ses.dh_K, NULL); mp_set_int(ses.dh_K, 12345678); finish_kexhashbuf(); - assert(!ses.dh_K); } int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) { From dcb41e91eb7ec97d8af0e9ad748345cdd1c1319e Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 17 Feb 2018 19:41:44 +0800 Subject: [PATCH 50/56] limit rsa->e size to 64 bits --HG-- branch : fuzz --- rsa.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rsa.c b/rsa.c index 7222b8d..67b90f7 100644 --- a/rsa.c +++ b/rsa.c @@ -68,6 +68,12 @@ int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key) { goto out; } + /* 64 bit is limit used by openssl, so we won't block any keys in the wild */ + if (mp_count_bits(key->e) > 64) { + dropbear_log(LOG_WARNING, "RSA key bad e"); + goto out; + } + TRACE(("leave buf_get_rsa_pub_key: success")) ret = DROPBEAR_SUCCESS; out: From dbc052099291a313295170c67bb122d85f889be7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 21 Feb 2018 21:03:42 +0800 Subject: [PATCH 51/56] compile fixes --HG-- branch : fuzz --- fuzz-wrapfd.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c index dde8dde..b4bcb72 100644 --- a/fuzz-wrapfd.c +++ b/fuzz-wrapfd.c @@ -4,7 +4,7 @@ #include "fuzz.h" -static const int IOWRAP_MAXFD = FD_SETSIZE-1; +#define 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; @@ -37,7 +37,7 @@ void wrapfd_setup() { } void wrapfd_setseed(uint32_t seed) { - *((uint32_t*)rand_state) = seed; + memcpy(rand_state, &seed, sizeof(seed)); nrand48(rand_state); } @@ -77,12 +77,10 @@ void wrapfd_remove(int fd) { } int wrapfd_close(int fd) { - if (fd >= 0 && fd <= IOWRAP_MAXFD && wrap_fds[fd].mode != UNUSED) - { + if (fd >= 0 && fd <= IOWRAP_MAXFD && wrap_fds[fd].mode != UNUSED) { wrapfd_remove(fd); return 0; - } - else { + } else { return close(fd); } } @@ -173,7 +171,9 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { int i, nset, sel; int ret = 0; - int fdlist[IOWRAP_MAXFD+1] = {0}; + int fdlist[IOWRAP_MAXFD+1]; + + memset(fdlist, 0x0, sizeof(fdlist)); if (!fuzz.wrapfds) { return select(nfds, readfds, writefds, exceptfds, timeout); From 05f4e29a52ff806620b1f80ab59797ac1bc8415d Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 21 Feb 2018 21:49:24 +0800 Subject: [PATCH 52/56] The fuzzer has managed to generated DSS key/signature pairs that verify. Avoid false positives from bogus keys that wouldn't be used --HG-- branch : fuzz --- fuzz-common.c | 2 ++ fuzzer-verify.c | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/fuzz-common.c b/fuzz-common.c index b596821..8403a7b 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -22,6 +22,8 @@ void fuzz_common_setup(void) { fuzz.input = m_malloc(sizeof(buffer)); _dropbear_log = fuzz_dropbear_log; crypto_init(); + /* let any messages get flushed */ + setlinebuf(stdout); } int fuzz_set_input(const uint8_t *Data, size_t Size) { diff --git a/fuzzer-verify.c b/fuzzer-verify.c index ed4a9f7..0aa58df 100644 --- a/fuzzer-verify.c +++ b/fuzzer-verify.c @@ -29,8 +29,28 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { sign_key *key = new_sign_key(); enum signkey_type type = DROPBEAR_SIGNKEY_ANY; if (buf_get_pub_key(fuzz.input, key, &type) == DROPBEAR_SUCCESS) { - /* Don't expect random fuzz input to verify */ - assert(buf_verify(fuzz.input, key, verifydata) == DROPBEAR_FAILURE); + if (buf_verify(fuzz.input, key, verifydata) == DROPBEAR_SUCCESS) { + /* The fuzzer is capable of generating keys with a signature to match. + We don't want false positives if the key is bogus, since a client/server + wouldn't be trusting a bogus key anyway */ + int boguskey = 0; + + if (type == DROPBEAR_SIGNKEY_DSS) { + /* So far have seen dss keys with bad p/q/g domain parameters */ + int pprime, qprime; + assert(mp_prime_is_prime(key->dsskey->p, 5, &pprime) == MP_OKAY); + assert(mp_prime_is_prime(key->dsskey->q, 18, &qprime) == MP_OKAY); + boguskey = !(pprime && qprime); + /* Could also check g**q mod p == 1 */ + } + + if (!boguskey) { + printf("Random key/signature managed to verify!\n"); + abort(); + } + + + } } sign_key_free(key); m_malloc_free_epoch(1, 0); From 573838a0278e56225bf2a4a1e386105525a6a91a Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 21 Feb 2018 21:59:52 +0800 Subject: [PATCH 53/56] print hg revid --HG-- branch : fuzz --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index 9f92328..6a61d82 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,12 @@ AC_PREREQ(2.59) AC_INIT AC_CONFIG_SRCDIR(buffer.c) +# Record which revision is being built +if which -s hg && test -d "$srcdir/.hg"; then + hgrev=`hg id -i -R "$srcdir"` + echo "Source directory Mercurial base revision $hgrev" +fi + # Checks for programs. AC_PROG_CC From c658b275fd01fa62eea135742c623dba5f39845c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 28 Feb 2018 21:40:08 +0800 Subject: [PATCH 54/56] - #if not #ifdef for DROPBEAR_FUZZ - fix some unused variables --HG-- branch : fuzz --- common-kex.c | 2 +- common-session.c | 4 ++-- configure.ac | 3 ++- dbrandom.c | 8 ++++---- dbutil.c | 6 +++--- fuzz-common.c | 14 +++++++------- fuzz.h | 2 +- netio.c | 2 +- packet.c | 6 +++--- signkey.c | 2 +- svr-auth.c | 4 ++-- svr-authpubkey.c | 2 +- svr-kex.c | 2 +- svr-session.c | 2 +- 14 files changed, 30 insertions(+), 29 deletions(-) diff --git a/common-kex.c b/common-kex.c index 90a9308..7da3fb7 100644 --- a/common-kex.c +++ b/common-kex.c @@ -949,7 +949,7 @@ static void read_kex_algos() { ses.newkeys->trans.algo_comp = s2c_comp_algo->val; } -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing) { fuzz_kex_fakealgos(); } diff --git a/common-session.c b/common-session.c index 539a53f..037a78d 100644 --- a/common-session.c +++ b/common-session.c @@ -75,7 +75,7 @@ void common_session_init(int sock_in, int sock_out) { ses.last_packet_time_any_sent = 0; ses.last_packet_time_keepalive_sent = 0; -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (!fuzz.fuzzing) #endif { @@ -158,7 +158,7 @@ void session_loop(void(*loophandler)(void)) { /* We get woken up when signal handlers write to this pipe. SIGCHLD in svr-chansession is the only one currently. */ -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (!fuzz.fuzzing) #endif { diff --git a/configure.ac b/configure.ac index 4f54cf2..58ddcfc 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ AC_CONFIG_SRCDIR(buffer.c) # Record which revision is being built if which -s hg && test -d "$srcdir/.hg"; then hgrev=`hg id -i -R "$srcdir"` - echo "Source directory Mercurial base revision $hgrev" + AC_MSG_NOTICE([Source directory Mercurial base revision $hgrev]) fi # Checks for programs. @@ -330,6 +330,7 @@ AC_ARG_ENABLE(fuzz, DROPBEAR_FUZZ=1 ], [ + AC_DEFINE(DROPBEAR_FUZZ, 0, Fuzzing) DROPBEAR_FUZZ=0 ] diff --git a/dbrandom.c b/dbrandom.c index 1907834..7d2e118 100644 --- a/dbrandom.c +++ b/dbrandom.c @@ -145,7 +145,7 @@ void addrandom(const unsigned char * buf, unsigned int len) { hash_state hs; -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing || fuzz.recordf) { return; } @@ -163,7 +163,7 @@ void addrandom(const unsigned char * buf, unsigned int len) static void write_urandom() { -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing || fuzz.recordf) { return; } @@ -181,7 +181,7 @@ static void write_urandom() #endif } -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ void fuzz_seed(void) { hash_state hs; sha1_init(&hs); @@ -203,7 +203,7 @@ void seedrandom() { struct timeval tv; clock_t clockval; -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing || fuzz.recordf) { return; } diff --git a/dbutil.c b/dbutil.c index 73c7cb2..8b9880a 100644 --- a/dbutil.c +++ b/dbutil.c @@ -120,7 +120,7 @@ static void generic_dropbear_exit(int exitcode, const char* format, _dropbear_log(LOG_INFO, fmtbuf, param); -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ // longjmp before cleaning up svr_opts if (fuzz.do_jmp) { longjmp(fuzz.jmp, 1); @@ -532,7 +532,7 @@ void setnonblocking(int fd) { TRACE(("setnonblocking: %d", fd)) -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing) { return; } @@ -629,7 +629,7 @@ static clockid_t get_linux_clock_source() { #endif time_t monotonic_now() { -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing) { /* time stands still when fuzzing */ return 5; diff --git a/fuzz-common.c b/fuzz-common.c index 8403a7b..08041e2 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -42,21 +42,21 @@ int fuzz_set_input(const uint8_t *Data, size_t Size) { return DROPBEAR_SUCCESS; } -static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param) { - - char printbuf[1024]; - #if DEBUG_TRACE +static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param) { if (debug_trace) { + char printbuf[1024]; vsnprintf(printbuf, sizeof(printbuf), format, param); fprintf(stderr, "%s\n", printbuf); } -#endif } +#else +static void fuzz_dropbear_log(int UNUSED(priority), const char* UNUSED(format), va_list UNUSED(param)) { + /* No print */ +} +#endif /* DEBUG_TRACE */ void fuzz_svr_setup(void) { - struct passwd *pw; - fuzz_common_setup(); _dropbear_exit = svr_dropbear_exit; diff --git a/fuzz.h b/fuzz.h index 7569130..dd37c15 100644 --- a/fuzz.h +++ b/fuzz.h @@ -2,7 +2,7 @@ #define DROPBEAR_FUZZ_H #include "config.h" -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ #include "includes.h" #include "buffer.h" diff --git a/netio.c b/netio.c index 6c12b08..62fdc72 100644 --- a/netio.c +++ b/netio.c @@ -361,7 +361,7 @@ void set_sock_priority(int sock, enum dropbear_prio prio) { int so_prio_val = 0; #endif -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing) { TRACE(("fuzzing skips set_sock_prio")) return; diff --git a/packet.c b/packet.c index 6349d0d..0d718c5 100644 --- a/packet.c +++ b/packet.c @@ -77,7 +77,7 @@ void write_packet() { /* This may return EAGAIN. The main loop sometimes calls write_packet() without bothering to test with select() since it's likely to be necessary */ -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing) { // pretend to write one packet at a time // TODO(fuzz): randomise amount written based on the fuzz input @@ -105,7 +105,7 @@ void write_packet() { } #else /* No writev () */ -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ _Static_assert(0, "No fuzzing code for no-writev writes"); #endif /* Get the next buffer in the queue of encrypted packets to write*/ @@ -366,7 +366,7 @@ 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 DROPBEAR_FUZZ if (fuzz.fuzzing) { // fail 1 in 2000 times to test error path. // note that mac_bytes is all zero prior to kex, so don't test ==0 ! diff --git a/signkey.c b/signkey.c index 6eda185..88f06c7 100644 --- a/signkey.c +++ b/signkey.c @@ -628,7 +628,7 @@ out: } #endif -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ const char * const * fuzz_signkey_names = signkey_names; #endif diff --git a/svr-auth.c b/svr-auth.c index 7b400d1..bc893f9 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -312,7 +312,7 @@ static int checkusername(const char *username, unsigned int userlen) { return DROPBEAR_FAILURE; } } -#endif HAVE_GETGROUPLIST +#endif TRACE(("shell is %s", ses.authstate.pw_shell)) @@ -395,7 +395,7 @@ void send_msg_userauth_failure(int partial, int incrfail) { genrandom((unsigned char*)&delay, sizeof(delay)); /* We delay for 300ms +- 50ms */ delay = 250000 + (delay % 100000); -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (!fuzz.fuzzing) { usleep(delay); } diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 8905ac9..0ca0ea4 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -473,7 +473,7 @@ static int checkfileperm(char * filename) { return DROPBEAR_SUCCESS; } -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename, const char* algo, unsigned int algolen, const unsigned char* keyblob, unsigned int keybloblen) { diff --git a/svr-kex.c b/svr-kex.c index daed9d4..406ad97 100644 --- a/svr-kex.c +++ b/svr-kex.c @@ -179,7 +179,7 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { } #endif -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ if (fuzz.fuzzing && fuzz.skip_kexmaths) { fuzz_fake_send_kexdh_reply(); return; diff --git a/svr-session.c b/svr-session.c index b097ff2..9b513ee 100644 --- a/svr-session.c +++ b/svr-session.c @@ -185,7 +185,7 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { session_cleanup(); } -#ifdef DROPBEAR_FUZZ +#if DROPBEAR_FUZZ // longjmp before cleaning up svr_opts if (fuzz.do_jmp) { longjmp(fuzz.jmp, 1); From 5f2447edbb04dff504396e426268fbaf83fa6bca Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 28 Feb 2018 22:02:12 +0800 Subject: [PATCH 55/56] Fix to be able to compile normal(ish) binaries with --enable-fuzz --HG-- branch : fuzz --- Makefile.in | 26 +++++++++++++++++--------- configure.ac | 2 +- dbutil.c | 2 +- fuzz-common.c | 16 +++++++++------- fuzz-harness.c | 2 +- fuzz-wrapfd.c | 20 ++++++++++---------- fuzz.h | 1 + fuzzer-pubkey.c | 6 +++--- fuzzer-verify.c | 2 +- packet.c | 8 ++++---- svr-session.c | 2 +- 11 files changed, 49 insertions(+), 38 deletions(-) diff --git a/Makefile.in b/Makefile.in index b7b487c..2238bf3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -61,14 +61,22 @@ CONVERTOBJS=dropbearconvert.o keyimport.o SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o ifeq (@DROPBEAR_FUZZ@, 1) - COMMONOBJS += fuzz-common.o fuzz-wrapfd.o -endif + allobjs = $(COMMONOBJS) fuzz-common.o fuzz-wrapfd.o $(CLISVROBJS) $(CLIOBJS) $(SVROBJS) + allobjs:=$(subst svr-main.o, ,$(allobjs)) + allobjs:=$(subst cli-main.o, ,$(allobjs)) + allobjs:=$(sort $(allobjs)) -dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) -dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS) -dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS) -dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS) -scpobjs=$(SCPOBJS) + dropbearobjs=$(allobjs) svr-main.o + dbclientobjs=$(allobjs) cli-main.o + dropbearkeyobjs=$(allobjs) $(KEYOBJS) + dropbearconvertobjs=$(allobjs) $(CONVERTOBJS) +else + dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) + dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS) + dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS) + dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS) + scpobjs=$(SCPOBJS) +endif VPATH=@srcdir@ srcdir=@srcdir@ @@ -185,7 +193,7 @@ dbclient: $(HEADERS) $(LIBTOM_DEPS) Makefile $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) dropbearkey dropbearconvert: $(HEADERS) $(LIBTOM_DEPS) Makefile - $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) + $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) # scp doesn't use the libs so is special. scp: $(SCPOBJS) $(HEADERS) Makefile @@ -260,7 +268,7 @@ fuzzstandalone: fuzz-harness.o fuzz-targets svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs)) # build all the fuzzers. This will require fail to link unless built with -# make fuzzers LIBS=-lFuzzer.a +# make fuzz-targetsk FUZZLIB=-lFuzzer.a # or similar - the library provides main(). fuzz-targets: $(FUZZ_TARGETS) $(FUZZER_OPTIONS) diff --git a/configure.ac b/configure.ac index 58ddcfc..c485393 100644 --- a/configure.ac +++ b/configure.ac @@ -323,7 +323,7 @@ AC_ARG_ENABLE(shadow, ) AC_ARG_ENABLE(fuzz, - [ --enable-fuzz Build fuzzing], + [ --enable-fuzz Build fuzzing. Not recommended for deployment.], [ AC_DEFINE(DROPBEAR_FUZZ, 1, Fuzzing) AC_MSG_NOTICE(Enabling fuzzing) diff --git a/dbutil.c b/dbutil.c index 8b9880a..163ffd9 100644 --- a/dbutil.c +++ b/dbutil.c @@ -121,7 +121,7 @@ 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 + /* longjmp before cleaning up svr_opts */ if (fuzz.do_jmp) { longjmp(fuzz.jmp, 1); } diff --git a/fuzz-common.c b/fuzz-common.c index 08041e2..f64504f 100644 --- a/fuzz-common.c +++ b/fuzz-common.c @@ -154,13 +154,15 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) { return 0; } - // get prefix. input format is - // string prefix - // uint32 wrapfd seed - // ... to be extended later - // [bytes] ssh input stream + /* + get prefix. input format is + string prefix + uint32 wrapfd seed + ... to be extended later + [bytes] ssh input stream + */ - // be careful to avoid triggering buffer.c assertions + /* be careful to avoid triggering buffer.c assertions */ if (fuzz.input->len < 8) { return 0; } @@ -181,7 +183,7 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) { } else { m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) - // dropbear_exit jumped here + /* dropbear_exit jumped here */ } return 0; diff --git a/fuzz-harness.c b/fuzz-harness.c index 8f370ca..00a2ba6 100644 --- a/fuzz-harness.c +++ b/fuzz-harness.c @@ -19,7 +19,7 @@ int main(int argc, char ** argv) { for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { - // ignore arguments + /* ignore arguments */ continue; } diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c index b4bcb72..6472189 100644 --- a/fuzz-wrapfd.c +++ b/fuzz-wrapfd.c @@ -21,7 +21,7 @@ struct fdwrap { }; static struct fdwrap wrap_fds[IOWRAP_MAXFD+1]; -// for quick selection of in-use descriptors +/* for quick selection of in-use descriptors */ static int wrap_used[IOWRAP_MAXFD+1]; static unsigned int nused; static unsigned short rand_state[3]; @@ -66,7 +66,7 @@ void wrapfd_remove(int fd) { wrap_fds[fd].mode = UNUSED; - // remove from used list + /* remove from used list */ for (i = 0, j = 0; i < nused; i++) { if (wrap_used[i] != fd) { wrap_used[j] = wrap_used[i]; @@ -94,7 +94,7 @@ int wrapfd_read(int fd, void *out, size_t count) { } if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode == UNUSED) { - // XXX - assertion failure? + /* XXX - assertion failure? */ TRACE(("Bad read descriptor %d\n", fd)) errno = EBADF; return -1; @@ -116,7 +116,7 @@ int wrapfd_read(int fd, void *out, size_t count) { buf = wrap_fds[fd].buf; if (buf) { 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) { maxread = nrand48(rand_state) % maxread + 1; } @@ -140,7 +140,7 @@ int wrapfd_write(int fd, const void* in, size_t count) { } if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode == UNUSED) { - // XXX - assertion failure? + /* XXX - assertion failure? */ TRACE(("Bad read descriptor %d\n", fd)) errno = EBADF; return -1; @@ -148,7 +148,7 @@ int wrapfd_write(int fd, const void* in, size_t count) { assert(count != 0); - // force read to exercise sanitisers + /* force read to exercise sanitisers */ for (i = 0; i < count; i++) { (void)volin[i]; } @@ -186,7 +186,7 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds, return -1; } - // read + /* read */ if (readfds != NULL && erand48(rand_state) < CHANCE_READ1) { for (i = 0, nset = 0; i < nfds; i++) { if (FD_ISSET(i, readfds)) { @@ -198,7 +198,7 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds, FD_ZERO(readfds); if (nset > 0) { - // set one + /* set one */ sel = fdlist[nrand48(rand_state) % nset]; FD_SET(sel, readfds); ret++; @@ -213,7 +213,7 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds, } } - // write + /* write */ if (writefds != NULL && erand48(rand_state) < CHANCE_WRITE1) { for (i = 0, nset = 0; i < nfds; i++) { if (FD_ISSET(i, writefds)) { @@ -224,7 +224,7 @@ int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds, } FD_ZERO(writefds); - // set one + /* set one */ if (nset > 0) { sel = fdlist[nrand48(rand_state) % nset]; FD_SET(sel, writefds); diff --git a/fuzz.h b/fuzz.h index dd37c15..42ce494 100644 --- a/fuzz.h +++ b/fuzz.h @@ -2,6 +2,7 @@ #define DROPBEAR_FUZZ_H #include "config.h" + #if DROPBEAR_FUZZ #include "includes.h" diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c index 511357b..3dfe7b5 100644 --- a/fuzzer-pubkey.c +++ b/fuzzer-pubkey.c @@ -20,14 +20,14 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { m_malloc_set_epoch(1); - // choose a keytype based on input + /* choose a keytype based on input */ uint8_t b = 0; size_t i; for (i = 0; i < Size; i++) { b ^= Data[i]; } const char* algoname = fuzz_signkey_names[b%DROPBEAR_SIGNKEY_NUM_NAMED]; - const char* keyblob = "blob"; // keep short + const char* keyblob = "blob"; /* keep short */ if (setjmp(fuzz.jmp) == 0) { fuzz_checkpubkey_line(fuzz.input, 5, "/home/me/authorized_keys", @@ -37,7 +37,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { } else { m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) - // dropbear_exit jumped here + /* dropbear_exit jumped here */ } return 0; diff --git a/fuzzer-verify.c b/fuzzer-verify.c index 0aa58df..bbef524 100644 --- a/fuzzer-verify.c +++ b/fuzzer-verify.c @@ -57,7 +57,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { } else { m_malloc_free_epoch(1, 1); TRACE(("dropbear_exit longjmped")) - // dropbear_exit jumped here + /* dropbear_exit jumped here */ } return 0; diff --git a/packet.c b/packet.c index 0d718c5..6ccab77 100644 --- a/packet.c +++ b/packet.c @@ -79,8 +79,8 @@ void write_packet() { it's likely to be necessary */ #if DROPBEAR_FUZZ if (fuzz.fuzzing) { - // pretend to write one packet at a time - // TODO(fuzz): randomise amount written based on the fuzz input + /* pretend to write one packet at a time */ + /* TODO(fuzz): randomise amount written based on the fuzz input */ written = iov[0].iov_len; } else @@ -368,8 +368,8 @@ static int checkmac() { #if DROPBEAR_FUZZ if (fuzz.fuzzing) { - // fail 1 in 2000 times to test error path. - // note that mac_bytes is all zero prior to kex, so don't test ==0 ! + /* fail 1 in 2000 times to test error path. + note that mac_bytes is all zero prior to kex, so don't test ==0 ! */ unsigned int value = *((unsigned int*)&mac_bytes); if (value % 2000 == 99) { return DROPBEAR_FAILURE; diff --git a/svr-session.c b/svr-session.c index 9b513ee..a816398 100644 --- a/svr-session.c +++ b/svr-session.c @@ -186,7 +186,7 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { } #if DROPBEAR_FUZZ - // longjmp before cleaning up svr_opts + /* longjmp before cleaning up svr_opts */ if (fuzz.do_jmp) { longjmp(fuzz.jmp, 1); } From 5ebc48b3f231fa7628ab7f0bea56574c8359dbd5 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 28 Feb 2018 22:10:43 +0800 Subject: [PATCH 56/56] clean some fuzzing conditionals --HG-- branch : fuzz --- dbrandom.c | 6 +++--- fuzz.h | 3 --- svr-auth.c | 7 +++---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/dbrandom.c b/dbrandom.c index 7d2e118..838f8ca 100644 --- a/dbrandom.c +++ b/dbrandom.c @@ -146,7 +146,7 @@ void addrandom(const unsigned char * buf, unsigned int len) hash_state hs; #if DROPBEAR_FUZZ - if (fuzz.fuzzing || fuzz.recordf) { + if (fuzz.fuzzing) { return; } #endif @@ -164,7 +164,7 @@ void addrandom(const unsigned char * buf, unsigned int len) static void write_urandom() { #if DROPBEAR_FUZZ - if (fuzz.fuzzing || fuzz.recordf) { + if (fuzz.fuzzing) { return; } #endif @@ -204,7 +204,7 @@ void seedrandom() { clock_t clockval; #if DROPBEAR_FUZZ - if (fuzz.fuzzing || fuzz.recordf) { + if (fuzz.fuzzing) { return; } #endif diff --git a/fuzz.h b/fuzz.h index 42ce494..9316a0a 100644 --- a/fuzz.h +++ b/fuzz.h @@ -43,9 +43,6 @@ void fuzz_fake_send_kexdh_reply(void); struct dropbear_fuzz_options { int fuzzing; - // to record an unencrypted stream - FILE* recordf; - // fuzzing input buffer *input; struct dropbear_cipher recv_cipher; diff --git a/svr-auth.c b/svr-auth.c index bc893f9..fb7b294 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -396,12 +396,11 @@ void send_msg_userauth_failure(int partial, int incrfail) { /* We delay for 300ms +- 50ms */ delay = 250000 + (delay % 100000); #if DROPBEAR_FUZZ - if (!fuzz.fuzzing) { + if (!fuzz.fuzzing) +#endif + { usleep(delay); } -#else - usleep(delay); -#endif ses.authstate.failcount++; }