add wrapfd. improve fuzzer in makefile

--HG--
branch : fuzz
This commit is contained in:
Matt Johnston 2017-05-19 00:48:46 +08:00
parent 4dae8edb76
commit e7cdb2ebe5
7 changed files with 268 additions and 12 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

193
fuzz-wrapfd.c Normal file
View File

@ -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;
}

17
fuzz-wrapfd.h Normal file
View File

@ -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

6
fuzz.h
View File

@ -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;

View File

@ -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
}