summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile.in58
-rw-r--r--buffer.c1
-rw-r--r--configure.ac1
-rw-r--r--dbrandom.c3
-rw-r--r--dbutil.c35
-rw-r--r--fuzz.h23
-rw-r--r--fuzz/fuzz-common.c (renamed from fuzz-common.c)40
-rw-r--r--fuzz/fuzz-harness.c (renamed from fuzz-harness.c)6
-rw-r--r--fuzz/fuzz-hostkeys.c (renamed from fuzz-hostkeys.c)0
-rw-r--r--fuzz/fuzz-sshpacketmutator.c240
-rw-r--r--fuzz/fuzz-wrapfd.c (renamed from fuzz-wrapfd.c)0
-rw-r--r--fuzz/fuzzer-client.c (renamed from fuzzer-client.c)0
-rw-r--r--fuzz/fuzzer-client_mutator.c6
-rw-r--r--fuzz/fuzzer-client_mutator_nomaths.c6
-rw-r--r--fuzz/fuzzer-client_nomaths.c (renamed from fuzzer-client_nomaths.c)0
-rw-r--r--fuzz/fuzzer-kexcurve25519.c (renamed from fuzzer-kexcurve25519.c)0
-rw-r--r--fuzz/fuzzer-kexdh.c (renamed from fuzzer-kexdh.c)0
-rw-r--r--fuzz/fuzzer-kexecdh.c (renamed from fuzzer-kexecdh.c)0
-rw-r--r--fuzz/fuzzer-preauth.c (renamed from fuzzer-preauth.c)0
-rw-r--r--fuzz/fuzzer-preauth_nomaths.c (renamed from fuzzer-preauth_nomaths.c)0
-rw-r--r--fuzz/fuzzer-pubkey.c (renamed from fuzzer-pubkey.c)0
-rw-r--r--fuzz/fuzzer-verify.c (renamed from fuzzer-verify.c)0
22 files changed, 369 insertions, 50 deletions
diff --git a/Makefile.in b/Makefile.in
index 182cb42..682419a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -62,7 +62,7 @@ CONVERTOBJS=dropbearconvert.o keyimport.o
SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o
ifeq (@DROPBEAR_FUZZ@, 1)
- allobjs = $(COMMONOBJS) fuzz-common.o fuzz-wrapfd.o $(CLISVROBJS) $(CLIOBJS) $(SVROBJS) @CRYPTLIB@
+ allobjs = $(COMMONOBJS) fuzz/fuzz-common.o fuzz/fuzz-wrapfd.o $(CLISVROBJS) $(CLIOBJS) $(SVROBJS) @CRYPTLIB@
allobjs:=$(subst svr-main.o, ,$(allobjs))
allobjs:=$(subst cli-main.o, ,$(allobjs))
@@ -72,6 +72,7 @@ ifeq (@DROPBEAR_FUZZ@, 1)
dropbearconvertobjs=$(allobjs) $(CONVERTOBJS)
# CXX only set when fuzzing
CXX=@CXX@
+ FUZZ_CLEAN=fuzz-clean
else
dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
@@ -246,7 +247,7 @@ ltm-clean:
sizes: dropbear
objdump -t dropbear|grep ".text"|cut -d "." -f 2|sort -rn
-clean: $(LIBTOM_CLEAN) thisclean
+clean: $(LIBTOM_CLEAN) $(FUZZ_CLEAN) thisclean
thisclean:
-rm -f dropbear$(EXEEXT) dbclient$(EXEEXT) dropbearkey$(EXEEXT) \
@@ -268,50 +269,32 @@ lint:
# list of fuzz targets
FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths \
- fuzzer-kexdh fuzzer-kexecdh fuzzer-kexcurve25519 fuzzer-client fuzzer-client_nomaths
+ fuzzer-kexdh fuzzer-kexecdh fuzzer-kexcurve25519 fuzzer-client fuzzer-client_nomaths \
+ fuzzer-client_mutator fuzzer-client_mutator_nomaths
FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS))
+FUZZ_OBJS = $(addprefix fuzz/,$(addsuffix .o,$(FUZZ_TARGETS))) \
+ fuzz/fuzz-sshpacketmutator.o
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 fuzz-targets
-
-fuzz-harness.o: $(HEADERS) $(LIBTOM_DEPS) Makefile $(allobjs) fuzz-common.o
-
-# build all the fuzzers. This will require fail to link unless built with
-# make fuzz-targets FUZZLIB=-lFuzzer.a
-# or similar - the library provides main().
+fuzzstandalone: FUZZLIB=fuzz/fuzz-harness.o
+fuzzstandalone: fuzz/fuzz-harness.o fuzz-targets
+
+# Build all the fuzzers. Usually like
+# make fuzz-targets FUZZLIB=-lFuzzer.a
+# the library provides main(). Otherwise
+# make fuzzstandalone
+# provides a main in fuzz-harness.c
fuzz-targets: $(FUZZ_TARGETS) $(FUZZER_OPTIONS)
-fuzzer-preauth: fuzzer-preauth.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
-
-fuzzer-preauth_nomaths: fuzzer-preauth_nomaths.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
-
-fuzzer-pubkey: fuzzer-pubkey.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
-
-fuzzer-verify: fuzzer-verify.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
+$(FUZZ_TARGETS): $(FUZZ_OBJS) $(allobjs) $(LIBTOM_DEPS)
+ $(CXX) $(CXXFLAGS) fuzz/$@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) -lcrypt
-fuzzer-kexdh: fuzzer-kexdh.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
-
-fuzzer-kexecdh: fuzzer-kexecdh.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
-
-fuzzer-kexcurve25519: fuzzer-kexcurve25519.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
-
-fuzzer-client: fuzzer-client.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
-
-fuzzer-client_nomaths: fuzzer-client_nomaths.o fuzz-harness.o
- $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(allobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
+# fuzzers that use the custom mutator
+fuzzer-client_mutator fuzzer-client_mutator_nomaths: allobjs += fuzz/fuzz-sshpacketmutator.o
fuzzer-%.options: Makefile
echo "[libfuzzer]" > $@
@@ -329,3 +312,6 @@ fuzz-hostkeys:
/usr/bin/xxd -i -a keye >> hostkeys.c
/usr/bin/xxd -i -a keyd >> hostkeys.c
/usr/bin/xxd -i -a keyed25519 >> hostkeys.c
+
+fuzz-clean:
+ -rm -f fuzz/*.o $(FUZZ_TARGETS) $(FUZZER_OPTIONS)
diff --git a/buffer.c b/buffer.c
index fee41d6..1397519 100644
--- a/buffer.c
+++ b/buffer.c
@@ -188,6 +188,7 @@ unsigned char* buf_getptr(const buffer* buf, unsigned int len) {
unsigned char* buf_getwriteptr(const buffer* buf, unsigned int len) {
if (len > BUF_MAX_INCR || buf->pos + len > buf->size) {
+ abort();
dropbear_exit("Bad buf_getwriteptr");
}
return &buf->data[buf->pos];
diff --git a/configure.ac b/configure.ac
index 473cea5..8f552a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -347,6 +347,7 @@ AC_ARG_ENABLE(fuzz,
DROPBEAR_FUZZ=1
# libfuzzer needs linking with c++ libraries
AC_PROG_CXX
+ mkdir -pv fuzz
else
AC_DEFINE(DROPBEAR_FUZZ, 0, Fuzzing)
AC_MSG_NOTICE(Disabling fuzzing)
diff --git a/dbrandom.c b/dbrandom.c
index d7340a3..faada2a 100644
--- a/dbrandom.c
+++ b/dbrandom.c
@@ -150,10 +150,11 @@ static void write_urandom()
}
#if DROPBEAR_FUZZ
-void fuzz_seed(void) {
+void fuzz_seed(const unsigned char* dat, unsigned int len) {
hash_state hs;
sha1_init(&hs);
sha1_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz"));
+ sha1_process(&hs, dat, len);
sha1_done(&hs, hashpool);
counter = 0;
diff --git a/dbutil.c b/dbutil.c
index 5af6330..53256a2 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -385,20 +385,37 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
#if DEBUG_TRACE
void printhex(const char * label, const unsigned char * buf, int len) {
-
- int i;
+ int i, j;
fprintf(stderr, "%s\n", label);
- for (i = 0; i < len; i++) {
- fprintf(stderr, "%02x", buf[i]);
- if (i % 16 == 15) {
- fprintf(stderr, "\n");
+ /* for each 16 byte line */
+ for (j = 0; j < len; j += 16) {
+ const int linelen = MIN(16, len - j);
+
+ /* print hex digits */
+ for (i = 0; i < 16; i++) {
+ if (i < linelen) {
+ fprintf(stderr, "%02x", buf[j+i]);
+ } else {
+ fprintf(stderr, " ");
+ }
+ // separator between pairs
+ if (i % 2 ==1) {
+ fprintf(stderr, " ");
+ }
}
- else if (i % 2 == 1) {
- fprintf(stderr, " ");
+
+ /* print characters */
+ fprintf(stderr, " ");
+ for (i = 0; i < linelen; i++) {
+ char c = buf[j+i];
+ if (!isprint(c)) {
+ c = '.';
+ }
+ fputc(c, stderr);
}
+ fprintf(stderr, "\n");
}
- fprintf(stderr, "\n");
}
void printmpint(const char *label, mp_int *mp) {
diff --git a/fuzz.h b/fuzz.h
index b5dc7e8..ae781df 100644
--- a/fuzz.h
+++ b/fuzz.h
@@ -15,6 +15,10 @@ void fuzz_common_setup(void);
void fuzz_svr_setup(void);
void fuzz_cli_setup(void);
+// constructor attribute so it runs before main(), including
+// in non-fuzzing mode.
+void fuzz_early_setup(void) __attribute__((constructor));
+
// must be called once per fuzz iteration.
// returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE
int fuzz_set_input(const uint8_t *Data, size_t Size);
@@ -29,7 +33,7 @@ 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);
+void fuzz_seed(const unsigned char* dat, unsigned int len);
// helpers
void fuzz_get_socket_address(int fd, char **local_host, char **local_port,
@@ -68,10 +72,27 @@ struct dropbear_fuzz_options {
int dumping;
// the file descriptor
int recv_dumpfd;
+
+ // avoid filling fuzzing logs, this points to /dev/null
+ FILE *fake_stderr;
};
extern struct dropbear_fuzz_options fuzz;
+/* guard for when fuzz.h is included by fuzz-common.c */
+#ifndef FUZZ_NO_REPLACE_STDERR
+
+/* This is a bodge but seems to work.
+ glibc stdio.h has the comment
+ "C89/C99 say they're macros. Make them happy." */
+/* OS X has it as a macro */
+#ifdef stderr
+#undef stderr
+#endif
+#define stderr (fuzz.fake_stderr)
+
+#endif /* FUZZ_NO_REPLACE_STDERR */
+
#endif // DROPBEAR_FUZZ
#endif /* DROPBEAR_FUZZ_H */
diff --git a/fuzz-common.c b/fuzz/fuzz-common.c
index 60dab21..a147710 100644
--- a/fuzz-common.c
+++ b/fuzz/fuzz-common.c
@@ -1,7 +1,6 @@
#include "includes.h"
#include "includes.h"
-#include "fuzz.h"
#include "dbutil.h"
#include "runopts.h"
#include "crypto_desc.h"
@@ -11,12 +10,27 @@
#include "atomicio.h"
#include "fuzz-wrapfd.h"
+#define FUZZ_NO_REPLACE_STDERR
+#include "fuzz.h"
+
+/* fuzz.h redefines stderr, we don't want that here */
+#ifdef origstderr
+#undef stderr
+#define stderr origstderr
+#endif // origstderr
+
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);
static void load_fixed_client_key(void);
+// This runs automatically before main, due to contructor attribute in fuzz.h
+void fuzz_early_setup(void) {
+ /* Set stderr to point to normal stderr by default */
+ fuzz.fake_stderr = stderr;
+}
+
void fuzz_common_setup(void) {
disallow_core();
fuzz.fuzzing = 1;
@@ -25,9 +39,25 @@ void fuzz_common_setup(void) {
fuzz.input = m_malloc(sizeof(buffer));
_dropbear_log = fuzz_dropbear_log;
crypto_init();
- fuzz_seed();
+ fuzz_seed("start", 5);
/* let any messages get flushed */
setlinebuf(stdout);
+#if DEBUG_TRACE
+ if (debug_trace)
+ {
+ fprintf(stderr, "Dropbear fuzzer: -v specified, not disabling stderr output\n");
+ }
+ else
+#endif
+ if (getenv("DROPBEAR_KEEP_STDERR")) {
+ fprintf(stderr, "Dropbear fuzzer: DROPBEAR_KEEP_STDERR, not disabling stderr output\n");
+ }
+ else
+ {
+ fprintf(stderr, "Dropbear fuzzer: Disabling stderr output\n");
+ fuzz.fake_stderr = fopen("/dev/null", "w");
+ assert(fuzz.fake_stderr);
+ }
}
int fuzz_set_input(const uint8_t *Data, size_t Size) {
@@ -42,7 +72,7 @@ int fuzz_set_input(const uint8_t *Data, size_t Size) {
memset(&cli_ses, 0x0, sizeof(cli_ses));
wrapfd_setup(fuzz.input);
- fuzz_seed();
+ fuzz_seed(fuzz.input->data, MIN(fuzz.input->len, 16));
return DROPBEAR_SUCCESS;
}
@@ -235,10 +265,12 @@ int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
int fakesock = wrapfd_new();
m_malloc_set_epoch(1);
+ fuzz.do_jmp = 1;
if (setjmp(fuzz.jmp) == 0) {
svr_session(fakesock, fakesock);
m_malloc_free_epoch(1, 0);
} else {
+ fuzz.do_jmp = 0;
m_malloc_free_epoch(1, 1);
TRACE(("dropbear_exit longjmped"))
/* dropbear_exit jumped here */
@@ -281,10 +313,12 @@ int fuzz_run_client(const uint8_t *Data, size_t Size, int skip_kexmaths) {
int fakesock = wrapfd_new();
m_malloc_set_epoch(1);
+ fuzz.do_jmp = 1;
if (setjmp(fuzz.jmp) == 0) {
cli_session(fakesock, fakesock, NULL, 0);
m_malloc_free_epoch(1, 0);
} else {
+ fuzz.do_jmp = 0;
m_malloc_free_epoch(1, 1);
TRACE(("dropbear_exit longjmped"))
/* dropbear_exit jumped here */
diff --git a/fuzz-harness.c b/fuzz/fuzz-harness.c
index ced707c..36905d6 100644
--- a/fuzz-harness.c
+++ b/fuzz/fuzz-harness.c
@@ -46,3 +46,9 @@ int main(int argc, char ** argv) {
return 0;
}
+
+size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+ printf("standalone fuzzer harness shouldn't call LLVMFuzzerMutate");
+ abort();
+ return 0;
+}
diff --git a/fuzz-hostkeys.c b/fuzz/fuzz-hostkeys.c
index 128c8d1..128c8d1 100644
--- a/fuzz-hostkeys.c
+++ b/fuzz/fuzz-hostkeys.c
diff --git a/fuzz/fuzz-sshpacketmutator.c b/fuzz/fuzz-sshpacketmutator.c
new file mode 100644
index 0000000..d26089d
--- /dev/null
+++ b/fuzz/fuzz-sshpacketmutator.c
@@ -0,0 +1,240 @@
+#include "fuzz.h"
+#include "dbutil.h"
+
+size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+
+/* out_packets an array of num_out_packets*buffer, each of size RECV_MAX_PACKET_LEN */
+static void fuzz_get_packets(buffer *inp, buffer **out_packets, unsigned int *num_out_packets) {
+ /* Skip any existing banner. Format is
+ SSH-protoversion-softwareversion SP comments CR LF
+ so we look for SSH-2. then a subsequent LF */
+ unsigned char* version = memmem(inp->data, inp->len, "SSH-2.", strlen("SSH-2."));
+ if (version) {
+ buf_incrpos(inp, version - inp->data);
+ unsigned char* newline = memchr(&inp->data[inp->pos], '\n', inp->len - inp->pos);
+ if (newline) {
+ buf_incrpos(inp, newline - &inp->data[inp->pos]+1);
+ } else {
+ /* Give up on any version string */
+ buf_setpos(inp, 0);
+ }
+ }
+
+ const unsigned int max_out_packets = *num_out_packets;
+ *num_out_packets = 0;
+ while (1) {
+ if (inp->pos + 4 > inp->len) {
+ /* End of input */
+ break;
+ }
+
+ if (*num_out_packets >= max_out_packets) {
+ /* End of output */
+ break;
+ }
+
+ /* Read packet */
+ unsigned int packet_len = buf_getint(inp);
+ if (packet_len > RECV_MAX_PACKET_LEN-4) {
+ /* Bad length, try skipping a single byte */
+ buf_decrpos(inp, 3);
+ continue;
+ }
+ packet_len = MIN(packet_len, inp->len - inp->pos);
+
+ /* Copy to output buffer. We're reusing buffers */
+ buffer* new_packet = out_packets[*num_out_packets];
+ (*num_out_packets)++;
+ buf_setlen(new_packet, 0);
+ buf_putint(new_packet, packet_len);
+ buf_putbytes(new_packet, buf_getptr(inp, packet_len), packet_len);
+ buf_incrpos(inp, packet_len);
+ }
+}
+
+/* Mutate in-place */
+void buf_llvm_mutate(buffer *buf) {
+ /* Position it after packet_length and padding_length */
+ const unsigned int offset = 5;
+ if (buf->len < offset) {
+ return;
+ }
+ buf_setpos(buf, offset);
+ size_t max_size = buf->size - buf->pos;
+ size_t new_size = LLVMFuzzerMutate(buf_getwriteptr(buf, max_size),
+ buf->len - buf->pos, max_size);
+ buf_setpos(buf, 0);
+ buf_putint(buf, new_size);
+ buf_setlen(buf, offset + new_size);
+}
+
+
+static const char* FIXED_VERSION = "SSH-2.0-dbfuzz\r\n";
+static const size_t MAX_FUZZ_PACKETS = 500;
+/* XXX This might need tuning */
+static const size_t MAX_OUT_SIZE = 50000;
+
+/* Persistent buffers to avoid constant allocations */
+static buffer *oup;
+static buffer *alloc_packetA;
+static buffer *alloc_packetB;
+buffer* packets1[MAX_FUZZ_PACKETS];
+buffer* packets2[MAX_FUZZ_PACKETS];
+
+/* Allocate buffers once at startup.
+ 'constructor' here so it runs before dbmalloc's interceptor */
+static void alloc_static_buffers() __attribute__((constructor));
+static void alloc_static_buffers() {
+
+ int i;
+ oup = buf_new(MAX_OUT_SIZE);
+ alloc_packetA = buf_new(RECV_MAX_PACKET_LEN);
+ alloc_packetB = buf_new(RECV_MAX_PACKET_LEN);
+
+ for (i = 0; i < MAX_FUZZ_PACKETS; i++) {
+ packets1[i] = buf_new(RECV_MAX_PACKET_LEN);
+ }
+ for (i = 0; i < MAX_FUZZ_PACKETS; i++) {
+ packets2[i] = buf_new(RECV_MAX_PACKET_LEN);
+ }
+}
+
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed) {
+
+ buf_setlen(alloc_packetA, 0);
+ buf_setlen(alloc_packetB, 0);
+ buf_setlen(oup, 0);
+
+ unsigned int i;
+ unsigned short randstate[3] = {0,0,0};
+ memcpy(randstate, &Seed, sizeof(Seed));
+
+ // printhex("mutator input", Data, Size);
+ #if 0
+ /* 1% chance straight llvm mutate */
+ if (nrand48(randstate) % 100 == 0) {
+ return LLVMFuzzerMutate(Data, Size, MaxSize);
+ }
+ #endif
+
+ buffer inp_buf = {.data = Data, .size = Size, .len = Size, .pos = 0};
+ buffer *inp = &inp_buf;
+
+ /* Parse packets */
+ unsigned int num_packets = MAX_FUZZ_PACKETS;
+ buffer **packets = packets1;
+ fuzz_get_packets(inp, packets, &num_packets);
+
+ if (num_packets == 0) {
+ // gotta do something
+ memcpy(Data, FIXED_VERSION, MIN(strlen(FIXED_VERSION), MaxSize));
+ return LLVMFuzzerMutate(Data, Size, MaxSize);
+ }
+
+ /* Start output */
+ /* Put a new banner to output */
+ buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION));
+
+ /* Iterate output */
+ for (i = 0; i < num_packets+1; i++) {
+ // These are pointers to output
+ buffer *out_packetA = NULL, *out_packetB = NULL;
+ buf_setlen(alloc_packetA, 0);
+ buf_setlen(alloc_packetB, 0);
+
+ /* 5% chance each */
+ const int optA = nrand48(randstate) % 20;
+ if (optA == 0) {
+ /* Copy another */
+ unsigned int other = nrand48(randstate) % num_packets;
+ out_packetA = packets[other];
+ }
+ if (optA == 1) {
+ /* Mutate another */
+ unsigned int other = nrand48(randstate) % num_packets;
+ buffer *from = packets[other];
+ buf_putbytes(alloc_packetA, from->data, from->len);
+ out_packetA = alloc_packetA;
+ buf_llvm_mutate(out_packetA);
+ }
+
+ if (i < num_packets) {
+ int optB = nrand48(randstate) % 10;
+ if (optB == 1) {
+ /* 10% chance of drop */
+ /* Drop it */
+ // printf("%d drop\n", i);
+ } else if (optB <= 6) {
+ /* Mutate it, 50% chance */
+ // printf("%d mutate\n", i);
+ buffer *from = packets[nrand48(randstate) % num_packets];
+ buf_putbytes(alloc_packetB, from->data, from->len);
+ out_packetB = alloc_packetB;
+ buf_llvm_mutate(out_packetB);
+ } else {
+ /* Copy as-is */
+ out_packetB = packets[i];
+ // printf("%d as-is\n", i);
+ }
+ }
+
+ if (out_packetA && oup->len + out_packetA->len <= oup->size) {
+ buf_putbytes(oup, out_packetA->data, out_packetA->len);
+ }
+ if (out_packetB && oup->len + out_packetB->len <= oup->size) {
+ buf_putbytes(oup, out_packetB->data, out_packetB->len);
+ }
+ }
+
+ size_t ret_len = MIN(MaxSize, oup->len);
+ memcpy(Data, oup->data, ret_len);
+ // printhex("mutator done", Data, ret_len);
+ return ret_len;
+}
+
+size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
+ const uint8_t *Data2, size_t Size2,
+ uint8_t *Out, size_t MaxOutSize,
+ unsigned int Seed) {
+ unsigned short randstate[3] = {0,0,0};
+ memcpy(randstate, &Seed, sizeof(Seed));
+
+ unsigned int i;
+ buffer inp_buf1 = {.data = (void*)Data1, .size = Size1, .len = Size1, .pos = 0};
+ buffer *inp1 = &inp_buf1;
+ buffer inp_buf2 = {.data = (void*)Data2, .size = Size2, .len = Size2, .pos = 0};
+ buffer *inp2 = &inp_buf2;
+
+ unsigned int num_packets1 = MAX_FUZZ_PACKETS;
+ fuzz_get_packets(inp1, packets1, &num_packets1);
+ unsigned int num_packets2 = MAX_FUZZ_PACKETS;
+ fuzz_get_packets(inp2, packets2, &num_packets2);
+
+ buf_setlen(oup, 0);
+ /* Put a new banner to output */
+ buf_putbytes(oup, FIXED_VERSION, strlen(FIXED_VERSION));
+
+ for (i = 0; i < num_packets1+1; i++) {
+ if (num_packets2 > 0 && nrand48(randstate) % 10 == 0) {
+ /* 10% chance of taking another packet at each position */
+ int other = nrand48(randstate) % num_packets2;
+ // printf("inserted other packet %d at %d\n", other, i);
+ buffer *otherp = packets2[other];
+ if (oup->len + otherp->len <= oup->size) {
+ buf_putbytes(oup, otherp->data, otherp->len);
+ }
+ }
+ if (i < num_packets1) {
+ buffer *thisp = packets1[i];
+ if (oup->len + thisp->len <= oup->size) {
+ buf_putbytes(oup, thisp->data, thisp->len);
+ }
+ }
+ }
+
+ size_t ret_len = MIN(MaxOutSize, oup->len);
+ memcpy(Out, oup->data, ret_len);
+ return ret_len;
+}
+
diff --git a/fuzz-wrapfd.c b/fuzz/fuzz-wrapfd.c
index c6d59fc..c6d59fc 100644
--- a/fuzz-wrapfd.c
+++ b/fuzz/fuzz-wrapfd.c
diff --git a/fuzzer-client.c b/fuzz/fuzzer-client.c
index eb59f46..eb59f46 100644
--- a/fuzzer-client.c
+++ b/fuzz/fuzzer-client.c
diff --git a/fuzz/fuzzer-client_mutator.c b/fuzz/fuzzer-client_mutator.c
new file mode 100644
index 0000000..eb59f46
--- /dev/null
+++ b/fuzz/fuzzer-client_mutator.c
@@ -0,0 +1,6 @@
+#include "fuzz.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ return fuzz_run_client(Data, Size, 0);
+}
+
diff --git a/fuzz/fuzzer-client_mutator_nomaths.c b/fuzz/fuzzer-client_mutator_nomaths.c
new file mode 100644
index 0000000..eb59f46
--- /dev/null
+++ b/fuzz/fuzzer-client_mutator_nomaths.c
@@ -0,0 +1,6 @@
+#include "fuzz.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ return fuzz_run_client(Data, Size, 0);
+}
+
diff --git a/fuzzer-client_nomaths.c b/fuzz/fuzzer-client_nomaths.c
index e0910a7..e0910a7 100644
--- a/fuzzer-client_nomaths.c
+++ b/fuzz/fuzzer-client_nomaths.c
diff --git a/fuzzer-kexcurve25519.c b/fuzz/fuzzer-kexcurve25519.c
index f2eab14..f2eab14 100644
--- a/fuzzer-kexcurve25519.c
+++ b/fuzz/fuzzer-kexcurve25519.c
diff --git a/fuzzer-kexdh.c b/fuzz/fuzzer-kexdh.c
index 224ff58..224ff58 100644
--- a/fuzzer-kexdh.c
+++ b/fuzz/fuzzer-kexdh.c
diff --git a/fuzzer-kexecdh.c b/fuzz/fuzzer-kexecdh.c
index c3a450a..c3a450a 100644
--- a/fuzzer-kexecdh.c
+++ b/fuzz/fuzzer-kexecdh.c
diff --git a/fuzzer-preauth.c b/fuzz/fuzzer-preauth.c
index 3ac49f4..3ac49f4 100644
--- a/fuzzer-preauth.c
+++ b/fuzz/fuzzer-preauth.c
diff --git a/fuzzer-preauth_nomaths.c b/fuzz/fuzzer-preauth_nomaths.c
index efdc2c3..efdc2c3 100644
--- a/fuzzer-preauth_nomaths.c
+++ b/fuzz/fuzzer-preauth_nomaths.c
diff --git a/fuzzer-pubkey.c b/fuzz/fuzzer-pubkey.c
index 7c12cdc..7c12cdc 100644
--- a/fuzzer-pubkey.c
+++ b/fuzz/fuzzer-pubkey.c
diff --git a/fuzzer-verify.c b/fuzz/fuzzer-verify.c
index a0ad086..a0ad086 100644
--- a/fuzzer-verify.c
+++ b/fuzz/fuzzer-verify.c