From 0bececbb601fea8d290ffbecf85b283886aa5a24 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 29 Feb 2012 14:26:02 +1100 Subject: [PATCH] Basic fuzzing framework --- unittests/test_helper/Makefile | 2 +- unittests/test_helper/fuzz.c | 255 ++++++++++++++++++++++++++++ unittests/test_helper/test_helper.h | 31 ++++ 3 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 unittests/test_helper/fuzz.c diff --git a/unittests/test_helper/Makefile b/unittests/test_helper/Makefile index 0f73a55..49d687f 100644 --- a/unittests/test_helper/Makefile +++ b/unittests/test_helper/Makefile @@ -1,7 +1,7 @@ # $OpenBSD$ LIB= test_helper -SRCS= test_helper.c +SRCS= test_helper.c fuzz.c DEBUGLIBS= no NOPROFILE= yes diff --git a/unittests/test_helper/fuzz.c b/unittests/test_helper/fuzz.c new file mode 100644 index 0000000..e73b6c0 --- /dev/null +++ b/unittests/test_helper/fuzz.c @@ -0,0 +1,255 @@ +/* $OpenBSD */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Utility functions/framework for fuzz tests */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_helper.h" + +struct fuzz { + /* Fuzz method in use */ + int strategy; + + /* Original seed data blob */ + void *seed; + size_t slen; + + /* Current working copy of seed with fuzz mutations applied */ + u_char *fuzzed; + size_t flen; + + /* Used by fuzz methods */ + size_t o1, o2; +}; + +void +fuzz_dump(struct fuzz *fuzz) +{ + u_char *p = fuzz_buf(fuzz); + size_t i, j, len = fuzz_len(fuzz); + + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + fprintf(stderr, "FUZZ_1_BIT_FLIP case %zu of %zu\n", + fuzz->o1, fuzz->flen * 8); + break; + case FUZZ_2_BIT_FLIP: + fprintf(stderr, "FUZZ_2_BIT_FLIP case %llu of %llu\n", + ((unsigned long long)fuzz->o1) * fuzz->o2, + ((unsigned long long)fuzz->flen * 8) * fuzz->flen * 8); + break; + case FUZZ_1_BYTE_FLIP: + fprintf(stderr, "FUZZ_1_BYTE_FLIP case %zu of %zu\n", + fuzz->o1, fuzz->flen); + break; + case FUZZ_2_BYTE_FLIP: + fprintf(stderr, "FUZZ_2_BYTE_FLIP case %llu of %llu\n", + ((unsigned long long)fuzz->o1) * fuzz->o2, + ((unsigned long long)fuzz->flen) * fuzz->flen); + break; + case FUZZ_TRUNCATE_START: + fprintf(stderr, "FUZZ_TRUNCATE_START case %zu of %zu\n", + fuzz->o1, fuzz->flen); + break; + case FUZZ_TRUNCATE_END: + fprintf(stderr, "FUZZ_TRUNCATE_END case %zu of %zu\n", + fuzz->o1, fuzz->flen); + break; + default: + abort(); + } + + fprintf(stderr, "fuzz context %p len = %zu\n", fuzz, len); + for (i = 0; i < len; i += 16) { + fprintf(stderr, "%.4zd: ", i); + for (j = i; j < i + 16; j++) { + if (j < len) + fprintf(stderr, "%02x ", p[j]); + else + fprintf(stderr, " "); + } + fprintf(stderr, " "); + for (j = i; j < i + 16; j++) { + if (j < len) { + if (isascii(p[j]) && isprint(p[j])) + fprintf(stderr, "%c", p[j]); + else + fprintf(stderr, "."); + } + } + fprintf(stderr, "\n"); + } +} + +struct fuzz * +fuzz_begin(int strategy, void *p, size_t l) +{ + struct fuzz *ret = calloc(sizeof(ret), 1); + + assert(ret != NULL); + ret->strategy = strategy; + ret->seed = p; + ret->slen = l; + + switch (ret->strategy) { + case FUZZ_1_BIT_FLIP: + case FUZZ_2_BIT_FLIP: + assert(ret->slen < SIZE_MAX / 8); + /* FALLTHROUGH */ + case FUZZ_1_BYTE_FLIP: + case FUZZ_2_BYTE_FLIP: + case FUZZ_TRUNCATE_START: + case FUZZ_TRUNCATE_END: + ret->fuzzed = calloc(ret->slen, 1); + assert(ret->fuzzed != NULL); + break; + default: + abort(); + } + fuzz_next(ret); + return ret; +} + +void +fuzz_next(struct fuzz *fuzz) +{ + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + assert(fuzz->flen == fuzz->slen); + assert(fuzz->fuzzed != NULL); + assert(fuzz->o1 / 8 < fuzz->flen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen); + fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8); + fuzz->o1++; + break; + case FUZZ_2_BIT_FLIP: + assert(fuzz->flen == fuzz->slen); + assert(fuzz->fuzzed != NULL); + assert(fuzz->o1 / 8 < fuzz->flen); + assert(fuzz->o2 / 8 < fuzz->flen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen); + fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8); + fuzz->fuzzed[fuzz->o2 / 8] ^= 1 << (fuzz->o2 % 8); + fuzz->o1++; + if (fuzz->o1 >= fuzz->flen * 8) { + fuzz->o1 = 0; + fuzz->o2++; + } + break; + case FUZZ_1_BYTE_FLIP: + assert(fuzz->flen == fuzz->slen); + assert(fuzz->fuzzed != NULL); + assert(fuzz->o1 < fuzz->flen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen); + fuzz->fuzzed[fuzz->o1] ^= 0xff; + fuzz->o1++; + break; + case FUZZ_2_BYTE_FLIP: + assert(fuzz->flen == fuzz->slen); + assert(fuzz->fuzzed != NULL); + assert(fuzz->o1 < fuzz->flen); + assert(fuzz->o2 < fuzz->flen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen); + fuzz->fuzzed[fuzz->o1] ^= 0xff; + fuzz->fuzzed[fuzz->o2] ^= 0xff; + if (fuzz->o1 >= fuzz->flen) { + fuzz->o1 = 0; + fuzz->o2++; + } + break; + case FUZZ_TRUNCATE_START: + case FUZZ_TRUNCATE_END: + assert(fuzz->flen == fuzz->slen); + assert(fuzz->fuzzed != NULL); + assert(fuzz->o1 < fuzz->flen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen); + fuzz->o1++; + break; + default: + abort(); + } +} + +int +fuzz_done(struct fuzz *fuzz) +{ + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + return fuzz->o1 >= fuzz->flen * 8; + case FUZZ_2_BIT_FLIP: + return fuzz->o2 >= fuzz->flen * 8; + case FUZZ_1_BYTE_FLIP: + return fuzz->o1 >= fuzz->flen; + case FUZZ_2_BYTE_FLIP: + return fuzz->o2 >= fuzz->flen; + case FUZZ_TRUNCATE_START: + case FUZZ_TRUNCATE_END: + return fuzz->o1 >= fuzz->flen; + default: + abort(); + } +} + +size_t +fuzz_len(struct fuzz *fuzz) +{ + assert(fuzz->fuzzed != NULL); + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + case FUZZ_2_BIT_FLIP: + case FUZZ_1_BYTE_FLIP: + case FUZZ_2_BYTE_FLIP: + return fuzz->flen; + case FUZZ_TRUNCATE_START: + case FUZZ_TRUNCATE_END: + assert(fuzz->o1 < fuzz->flen); + return fuzz->flen - fuzz->o1; + default: + abort(); + } +} + +u_char * +fuzz_buf(struct fuzz *fuzz) +{ + assert(fuzz->fuzzed != NULL); + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + case FUZZ_2_BIT_FLIP: + case FUZZ_1_BYTE_FLIP: + case FUZZ_2_BYTE_FLIP: + return fuzz->fuzzed; + case FUZZ_TRUNCATE_START: + assert(fuzz->o1 < fuzz->flen); + return fuzz->fuzzed + fuzz->o1; + case FUZZ_TRUNCATE_END: + assert(fuzz->o1 < fuzz->flen); + return fuzz->fuzzed; + default: + abort(); + } +} + diff --git a/unittests/test_helper/test_helper.h b/unittests/test_helper/test_helper.h index edef21a..e724929 100644 --- a/unittests/test_helper/test_helper.h +++ b/unittests/test_helper/test_helper.h @@ -255,4 +255,35 @@ void assert_u64(const char *file, int line, #define ASSERT_U64_GE(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +/* Fuzzing support */ + +struct fuzz; +enum fuzz_strategy { + FUZZ_1_BIT_FLIP, /* Flip one bit at a time */ + FUZZ_2_BIT_FLIP, /* Flip two bits at a time */ + FUZZ_1_BYTE_FLIP, /* Flip one byte at a time */ + FUZZ_2_BYTE_FLIP, /* Flip two bytes at a time */ + FUZZ_TRUNCATE_START, /* Truncate from beginning */ + FUZZ_TRUNCATE_END, /* Truncate from end */ + FUZZ_MAX +}; + +/* Start fuzzing a blob of data */ +struct fuzz *fuzz_begin(int strategy, void *p, size_t l); + +/* Free a fuzz context */ +void fuzz_cleanup(struct fuzz *fuzz); + +/* Prepare the next fuzz case in the series */ +void fuzz_next(struct fuzz *fuzz); + +/* Determine whether the current fuzz sequence is exhausted (nonzero = yes) */ +int fuzz_done(struct fuzz *fuzz); + +/* Return the length and a pointer to the current fuzzed case */ +size_t fuzz_len(struct fuzz *fuzz); +u_char *fuzz_buf(struct fuzz *fuzz); + +/* Dump the current fuzz case to stderr */ +void fuzz_dump(struct fuzz *fuzz); #endif /* _TEST_HELPER_H */