diff options
38 files changed, 2150 insertions, 1884 deletions
diff --git a/GNUmakefile b/GNUmakefile index e0d89274..679ccc82 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -270,7 +270,7 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as test_build all_done man: $(MANPAGES) tests: source-only - @cd test ; ./test.sh + @cd test ; ./test-all.sh @rm -f test/errors performance-tests: performance-test @@ -551,9 +551,9 @@ source-only: all -$(MAKE) -C gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap - #$(MAKE) -C examples/afl_network_proxy - #$(MAKE) -C examples/socket_fuzzing - #$(MAKE) -C examples/argv_fuzzing + @#$(MAKE) -C examples/afl_network_proxy + @#$(MAKE) -C examples/socket_fuzzing + @#$(MAKE) -C examples/argv_fuzzing %.8: % @echo .TH $* 8 $(BUILD_DATE) "afl++" > $@ diff --git a/README.md b/README.md index bd2784ae..2e24a534 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ anything below 9 is not recommended. +--------------------------------+ | if you want to instrument only | -> use afl-gcc-fast and afl-gcc-fast++ | parts of the target | see [gcc_plugin/README.md](gcc_plugin/README.md) and - +--------------------------------+ [gcc_plugin/README.instrument_file.md](gcc_plugin/README.instrument_file.md) + +--------------------------------+ [gcc_plugin/README.instrument_list.md](gcc_plugin/README.instrument_list.md) | | if not, or if you do not have a gcc with plugin support | @@ -290,12 +290,18 @@ selectively only instrument parts of the target that you are interested in: create a file with all the filenames of the source code that should be instrumented. For afl-clang-lto and afl-gcc-fast - or afl-clang-fast if either the clang - version is < 7 or the CLASSIC instrumentation is used - just put one - filename per line, no directory information necessary, and set - `export AFL_LLVM_INSTRUMENT_FILE=yourfile.txt` - see [llvm_mode/README.instrument_file.md](llvm_mode/README.instrument_file.md) + version is below 7 or the CLASSIC instrumentation is used - just put one + filename or function per line (no directory information necessary for + filenames9, and either set `export AFL_LLVM_ALLOWLIST=allowlist.txt` **or** + `export AFL_LLVM_DENYLIST=denylist.txt` - depending on if you want per + default to instrument unless noted (DENYLIST) or not perform instrumentation + unless requested (ALLOWLIST). + **NOTE:** In optimization functions might be inlined and then not match! + see [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) For afl-clang-fast > 6.0 or if PCGUARD instrumentation is used then use the llvm sancov allow-list feature: [http://clang.llvm.org/docs/SanitizerCoverage.html](http://clang.llvm.org/docs/SanitizerCoverage.html) + The llvm sancov format works with the allowlist/denylist feature of afl++ + however afl++ is more flexible in the format. There are many more options and modes available however these are most of the time less effective. See: diff --git a/TODO.md b/TODO.md index 999cb9d3..e81b82a3 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,6 @@ ## Roadmap 2.67+ - - expand on AFL_LLVM_INSTRUMENT_FILE to also support sancov allowlist format - AFL_MAP_SIZE for qemu_mode and unicorn_mode - CPU affinity for many cores? There seems to be an issue > 96 cores diff --git a/custom_mutators/honggfuzz/Makefile b/custom_mutators/honggfuzz/Makefile index 2f46d0e7..1d46f163 100644 --- a/custom_mutators/honggfuzz/Makefile +++ b/custom_mutators/honggfuzz/Makefile @@ -7,6 +7,8 @@ honggfuzz.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c $(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz.so honggfuzz.c mangle.c ../../src/afl-performance.c update: + @# seriously? --unlink is a dud option? sigh ... + rm -f mangle.c mangle.h honggfuzz.h wget --unlink https://github.com/google/honggfuzz/raw/master/mangle.c wget --unlink https://github.com/google/honggfuzz/raw/master/mangle.h wget --unlink https://github.com/google/honggfuzz/raw/master/honggfuzz.h diff --git a/custom_mutators/honggfuzz/honggfuzz.h b/custom_mutators/honggfuzz/honggfuzz.h index 4e045272..9d07fdf4 100644 --- a/custom_mutators/honggfuzz/honggfuzz.h +++ b/custom_mutators/honggfuzz/honggfuzz.h @@ -38,18 +38,17 @@ #include "libhfcommon/util.h" -#define PROG_NAME "honggfuzz" -#define PROG_VERSION "2.2" +#define PROG_NAME "honggfuzz" +#define PROG_VERSION "2.3" -/* Name of the template which will be replaced with the proper name of the file - */ +/* Name of the template which will be replaced with the proper name of the file */ #define _HF_FILE_PLACEHOLDER "___FILE___" /* Default name of the report created with some architectures */ #define _HF_REPORT_FILE "HONGGFUZZ.REPORT.TXT" /* Default stack-size of created threads. */ -#define _HF_PTHREAD_STACKSIZE (1024ULL * 1024ULL * 2ULL) /* 2MB */ +#define _HF_PTHREAD_STACKSIZE (1024ULL * 1024ULL * 2ULL) /* 2MB */ /* Name of envvar which indicates sequential number of fuzzer */ #define _HF_THREAD_NO_ENV "HFUZZ_THREAD_NO" @@ -63,12 +62,11 @@ /* Number of crash verifier iterations before tag crash as stable */ #define _HF_VERIFIER_ITER 5 -/* Size (in bytes) for report data to be stored in stack before written to file - */ +/* Size (in bytes) for report data to be stored in stack before written to file */ #define _HF_REPORT_SIZE 32768 /* Perf bitmap size */ -#define _HF_PERF_BITMAP_SIZE_16M (1024U * 1024U * 16U) +#define _HF_PERF_BITMAP_SIZE_16M (1024U * 1024U * 16U) #define _HF_PERF_BITMAP_BITSZ_MASK 0x7FFFFFFULL /* Maximum number of PC guards (=trace-pc-guard) we support */ #define _HF_PC_GUARD_MAX (1024ULL * 1024ULL * 64ULL) @@ -89,7 +87,7 @@ #define _HF_INPUT_FD 1021 /* FD used to pass coverage feedback from the fuzzed process */ #define _HF_COV_BITMAP_FD 1022 -#define _HF_BITMAP_FD _HF_COV_BITMAP_FD /* Old name for _HF_COV_BITMAP_FD */ +#define _HF_BITMAP_FD _HF_COV_BITMAP_FD /* Old name for _HF_COV_BITMAP_FD */ /* FD used to pass data to a persistent process */ #define _HF_PERSISTENT_FD 1023 @@ -105,356 +103,283 @@ static const uint8_t HFReadyTag = 'R'; /* Maximum number of active fuzzing threads */ #define _HF_THREAD_MAX 1024U -/* Persistent-binary signature - if found within file, it means it's a - * persistent mode binary */ +/* Persistent-binary signature - if found within file, it means it's a persistent mode binary */ #define _HF_PERSISTENT_SIG "\x01_LIBHFUZZ_PERSISTENT_BINARY_SIGNATURE_\x02\xFF" -/* HF NetDriver signature - if found within file, it means it's a - * NetDriver-based binary */ +/* HF NetDriver signature - if found within file, it means it's a NetDriver-based binary */ #define _HF_NETDRIVER_SIG "\x01_LIBHFUZZ_NETDRIVER_BINARY_SIGNATURE_\x02\xFF" -/* printf() nonmonetary separator. According to MacOSX's man it's supported - * there as well */ +/* printf() nonmonetary separator. According to MacOSX's man it's supported there as well */ #define _HF_NONMON_SEP "'" typedef enum { - - _HF_DYNFILE_NONE = 0x0, - _HF_DYNFILE_INSTR_COUNT = 0x1, - _HF_DYNFILE_BRANCH_COUNT = 0x2, - _HF_DYNFILE_BTS_EDGE = 0x10, - _HF_DYNFILE_IPT_BLOCK = 0x20, - _HF_DYNFILE_SOFT = 0x40, - + _HF_DYNFILE_NONE = 0x0, + _HF_DYNFILE_INSTR_COUNT = 0x1, + _HF_DYNFILE_BRANCH_COUNT = 0x2, + _HF_DYNFILE_BTS_EDGE = 0x10, + _HF_DYNFILE_IPT_BLOCK = 0x20, + _HF_DYNFILE_SOFT = 0x40, } dynFileMethod_t; typedef struct { - - uint64_t cpuInstrCnt; - uint64_t cpuBranchCnt; - uint64_t bbCnt; - uint64_t newBBCnt; - uint64_t softCntPc; - uint64_t softCntEdge; - uint64_t softCntCmp; - + uint64_t cpuInstrCnt; + uint64_t cpuBranchCnt; + uint64_t bbCnt; + uint64_t newBBCnt; + uint64_t softCntPc; + uint64_t softCntEdge; + uint64_t softCntCmp; } hwcnt_t; typedef enum { - - _HF_STATE_UNSET = 0, - _HF_STATE_STATIC, - _HF_STATE_DYNAMIC_DRY_RUN, - _HF_STATE_DYNAMIC_MAIN, - _HF_STATE_DYNAMIC_MINIMIZE, - + _HF_STATE_UNSET = 0, + _HF_STATE_STATIC, + _HF_STATE_DYNAMIC_DRY_RUN, + _HF_STATE_DYNAMIC_MAIN, + _HF_STATE_DYNAMIC_MINIMIZE, } fuzzState_t; typedef enum { - - HF_MAYBE = -1, - HF_NO = 0, - HF_YES = 1, - + HF_MAYBE = -1, + HF_NO = 0, + HF_YES = 1, } tristate_t; struct _dynfile_t { - - size_t size; - uint64_t cov[4]; - size_t idx; - int fd; - uint64_t timeExecUSecs; - char path[PATH_MAX]; - struct _dynfile_t *src; - uint32_t refs; - uint8_t * data; - TAILQ_ENTRY(_dynfile_t) pointers; - + size_t size; + uint64_t cov[4]; + size_t idx; + int fd; + uint64_t timeExecUSecs; + char path[PATH_MAX]; + struct _dynfile_t* src; + uint32_t refs; + uint8_t* data; + TAILQ_ENTRY(_dynfile_t) pointers; }; typedef struct _dynfile_t dynfile_t; struct strings_t { - - size_t len; - TAILQ_ENTRY(strings_t) pointers; - char s[]; - + size_t len; + TAILQ_ENTRY(strings_t) pointers; + char s[]; }; typedef struct { - - uint8_t pcGuardMap[_HF_PC_GUARD_MAX]; - uint8_t bbMapPc[_HF_PERF_BITMAP_SIZE_16M]; - uint32_t bbMapCmp[_HF_PERF_BITMAP_SIZE_16M]; - uint64_t pidNewPC[_HF_THREAD_MAX]; - uint64_t pidNewEdge[_HF_THREAD_MAX]; - uint64_t pidNewCmp[_HF_THREAD_MAX]; - uint64_t guardNb; - uint64_t pidTotalPC[_HF_THREAD_MAX]; - uint64_t pidTotalEdge[_HF_THREAD_MAX]; - uint64_t pidTotalCmp[_HF_THREAD_MAX]; - + uint8_t pcGuardMap[_HF_PC_GUARD_MAX]; + uint8_t bbMapPc[_HF_PERF_BITMAP_SIZE_16M]; + uint32_t bbMapCmp[_HF_PERF_BITMAP_SIZE_16M]; + uint64_t pidNewPC[_HF_THREAD_MAX]; + uint64_t pidNewEdge[_HF_THREAD_MAX]; + uint64_t pidNewCmp[_HF_THREAD_MAX]; + uint64_t guardNb; + uint64_t pidTotalPC[_HF_THREAD_MAX]; + uint64_t pidTotalEdge[_HF_THREAD_MAX]; + uint64_t pidTotalCmp[_HF_THREAD_MAX]; } feedback_t; typedef struct { - - uint32_t cnt; - struct { - - uint8_t val[32]; - uint32_t len; - - } valArr[1024 * 16]; - + uint32_t cnt; + struct { + uint8_t val[32]; + uint32_t len; + } valArr[1024 * 16]; } cmpfeedback_t; typedef struct { - - struct { - - size_t threadsMax; - size_t threadsFinished; - uint32_t threadsActiveCnt; - pthread_t mainThread; - pid_t mainPid; - pthread_t threads[_HF_THREAD_MAX]; - - } threads; - - struct { - - const char *inputDir; - const char *outputDir; - DIR * inputDirPtr; - size_t fileCnt; - size_t testedFileCnt; - const char *fileExtn; - size_t maxFileSz; - size_t newUnitsAdded; - char workDir[PATH_MAX]; - const char *crashDir; - const char *covDirNew; - bool saveUnique; - size_t dynfileqMaxSz; - size_t dynfileqCnt; - dynfile_t * dynfileqCurrent; - dynfile_t * dynfileq2Current; - TAILQ_HEAD(dyns_t, _dynfile_t) dynfileq; - bool exportFeedback; - - } io; - - struct { - - int argc; - const char *const *cmdline; - bool nullifyStdio; - bool fuzzStdin; - const char * externalCommand; - const char * postExternalCommand; - const char * feedbackMutateCommand; - bool netDriver; - bool persistent; - uint64_t asLimit; - uint64_t rssLimit; - uint64_t dataLimit; - uint64_t coreLimit; - uint64_t stackLimit; - bool clearEnv; - char * env_ptrs[128]; - char env_vals[128][4096]; - sigset_t waitSigSet; - - } exe; - - struct { - - time_t timeStart; - time_t runEndTime; - time_t tmOut; - time_t lastCovUpdate; - int64_t timeOfLongestUnitUSecs; - bool tmoutVTALRM; - - } timing; - - struct { - struct { - - uint8_t val[256]; - size_t len; - - } dictionary[1024]; - - size_t dictionaryCnt; - const char *dictionaryFile; - size_t mutationsMax; - unsigned mutationsPerRun; - size_t maxInputSz; - - } mutate; - - struct { - - bool useScreen; - char cmdline_txt[65]; - int64_t lastDisplayUSecs; - - } display; - - struct { - - bool useVerifier; - bool exitUponCrash; - const char *reportFile; - size_t dynFileIterExpire; - bool only_printable; - bool minimize; - bool switchingToFDM; - - } cfg; - - struct { - - bool enable; - bool del_report; - - } sanitizer; - - struct { - - fuzzState_t state; - feedback_t * covFeedbackMap; - int covFeedbackFd; - cmpfeedback_t * cmpFeedbackMap; - int cmpFeedbackFd; - bool cmpFeedback; - const char * blacklistFile; - uint64_t * blacklist; - size_t blacklistCnt; - bool skipFeedbackOnTimeout; - uint64_t maxCov[4]; - dynFileMethod_t dynFileMethod; - hwcnt_t hwCnts; - - } feedback; - - struct { - - size_t mutationsCnt; - size_t crashesCnt; - size_t uniqueCrashesCnt; - size_t verifiedCrashesCnt; - size_t blCrashesCnt; - size_t timeoutedCnt; - - } cnts; - - struct { - - bool enabled; - int serverSocket; - int clientSocket; - - } socketFuzzer; - - struct { - - pthread_rwlock_t dynfileq; - pthread_mutex_t feedback; - pthread_mutex_t report; - pthread_mutex_t state; - pthread_mutex_t input; - pthread_mutex_t timing; - - } mutex; - - /* For the Linux code */ - struct { - - int exeFd; - uint64_t dynamicCutOffAddr; - bool disableRandomization; - void * ignoreAddr; - const char *symsBlFile; - char ** symsBl; - size_t symsBlCnt; - const char *symsWlFile; - char ** symsWl; - size_t symsWlCnt; - uintptr_t cloneFlags; - tristate_t useNetNs; - bool kernelOnly; - bool useClone; - - } arch_linux; - - /* For the NetBSD code */ - struct { - - void * ignoreAddr; - const char *symsBlFile; - char ** symsBl; - size_t symsBlCnt; - const char *symsWlFile; - char ** symsWl; - size_t symsWlCnt; - - } arch_netbsd; - + size_t threadsMax; + size_t threadsFinished; + uint32_t threadsActiveCnt; + pthread_t mainThread; + pid_t mainPid; + pthread_t threads[_HF_THREAD_MAX]; + } threads; + struct { + const char* inputDir; + const char* outputDir; + DIR* inputDirPtr; + size_t fileCnt; + size_t testedFileCnt; + const char* fileExtn; + size_t maxFileSz; + size_t newUnitsAdded; + char workDir[PATH_MAX]; + const char* crashDir; + const char* covDirNew; + bool saveUnique; + size_t dynfileqMaxSz; + size_t dynfileqCnt; + dynfile_t* dynfileqCurrent; + dynfile_t* dynfileq2Current; + TAILQ_HEAD(dyns_t, _dynfile_t) dynfileq; + bool exportFeedback; + } io; + struct { + int argc; + const char* const* cmdline; + bool nullifyStdio; + bool fuzzStdin; + const char* externalCommand; + const char* postExternalCommand; + const char* feedbackMutateCommand; + bool netDriver; + bool persistent; + uint64_t asLimit; + uint64_t rssLimit; + uint64_t dataLimit; + uint64_t coreLimit; + uint64_t stackLimit; + bool clearEnv; + char* env_ptrs[128]; + char env_vals[128][4096]; + sigset_t waitSigSet; + } exe; + struct { + time_t timeStart; + time_t runEndTime; + time_t tmOut; + time_t lastCovUpdate; + int64_t timeOfLongestUnitUSecs; + bool tmoutVTALRM; + } timing; + struct { + struct { + uint8_t val[256]; + size_t len; + } dictionary[1024]; + size_t dictionaryCnt; + const char* dictionaryFile; + size_t mutationsMax; + unsigned mutationsPerRun; + size_t maxInputSz; + } mutate; + struct { + bool useScreen; + char cmdline_txt[65]; + int64_t lastDisplayUSecs; + } display; + struct { + bool useVerifier; + bool exitUponCrash; + const char* reportFile; + size_t dynFileIterExpire; + bool only_printable; + bool minimize; + bool switchingToFDM; + } cfg; + struct { + bool enable; + bool del_report; + } sanitizer; + struct { + fuzzState_t state; + feedback_t* covFeedbackMap; + int covFeedbackFd; + cmpfeedback_t* cmpFeedbackMap; + int cmpFeedbackFd; + bool cmpFeedback; + const char* blacklistFile; + uint64_t* blacklist; + size_t blacklistCnt; + bool skipFeedbackOnTimeout; + uint64_t maxCov[4]; + dynFileMethod_t dynFileMethod; + hwcnt_t hwCnts; + } feedback; + struct { + size_t mutationsCnt; + size_t crashesCnt; + size_t uniqueCrashesCnt; + size_t verifiedCrashesCnt; + size_t blCrashesCnt; + size_t timeoutedCnt; + } cnts; + struct { + bool enabled; + int serverSocket; + int clientSocket; + } socketFuzzer; + struct { + pthread_rwlock_t dynfileq; + pthread_mutex_t feedback; + pthread_mutex_t report; + pthread_mutex_t state; + pthread_mutex_t input; + pthread_mutex_t timing; + } mutex; + + /* For the Linux code */ + struct { + int exeFd; + uint64_t dynamicCutOffAddr; + bool disableRandomization; + void* ignoreAddr; + const char* symsBlFile; + char** symsBl; + size_t symsBlCnt; + const char* symsWlFile; + char** symsWl; + size_t symsWlCnt; + uintptr_t cloneFlags; + tristate_t useNetNs; + bool kernelOnly; + bool useClone; + } arch_linux; + /* For the NetBSD code */ + struct { + void* ignoreAddr; + const char* symsBlFile; + char** symsBl; + size_t symsBlCnt; + const char* symsWlFile; + char** symsWl; + size_t symsWlCnt; + } arch_netbsd; } honggfuzz_t; typedef enum { - - _HF_RS_UNKNOWN = 0, - _HF_RS_WAITING_FOR_INITIAL_READY = 1, - _HF_RS_WAITING_FOR_READY = 2, - _HF_RS_SEND_DATA = 3, - + _HF_RS_UNKNOWN = 0, + _HF_RS_WAITING_FOR_INITIAL_READY = 1, + _HF_RS_WAITING_FOR_READY = 2, + _HF_RS_SEND_DATA = 3, } runState_t; typedef struct { - - honggfuzz_t *global; - pid_t pid; - int64_t timeStartedUSecs; - char crashFileName[PATH_MAX]; - uint64_t pc; - uint64_t backtrace; - uint64_t access; - int exception; - char report[_HF_REPORT_SIZE]; - bool mainWorker; - unsigned mutationsPerRun; - dynfile_t * dynfile; - bool staticFileTryMore; - uint32_t fuzzNo; - int persistentSock; - runState_t runState; - bool tmOutSignaled; - char * args[_HF_ARGS_MAX + 1]; - int perThreadCovFeedbackFd; - unsigned triesLeft; - dynfile_t * current; + honggfuzz_t* global; + pid_t pid; + int64_t timeStartedUSecs; + char crashFileName[PATH_MAX]; + uint64_t pc; + uint64_t backtrace; + uint64_t access; + int exception; + char report[_HF_REPORT_SIZE]; + bool mainWorker; + unsigned mutationsPerRun; + dynfile_t* dynfile; + bool staticFileTryMore; + uint32_t fuzzNo; + int persistentSock; + runState_t runState; + bool tmOutSignaled; + char* args[_HF_ARGS_MAX + 1]; + int perThreadCovFeedbackFd; + unsigned triesLeft; + dynfile_t* current; #if !defined(_HF_ARCH_DARWIN) - timer_t timerId; -#endif // !defined(_HF_ARCH_DARWIN) - hwcnt_t hwCnts; - - struct { - - /* For Linux code */ - uint8_t *perfMmapBuf; - uint8_t *perfMmapAux; - int cpuInstrFd; - int cpuBranchFd; - int cpuIptBtsFd; - - } arch_linux; + timer_t timerId; +#endif // !defined(_HF_ARCH_DARWIN) + hwcnt_t hwCnts; + struct { + /* For Linux code */ + uint8_t* perfMmapBuf; + uint8_t* perfMmapAux; + int cpuInstrFd; + int cpuBranchFd; + int cpuIptBtsFd; + } arch_linux; } run_t; #endif - diff --git a/custom_mutators/honggfuzz/mangle.c b/custom_mutators/honggfuzz/mangle.c index 05e0dcfa..c2988319 100644 --- a/custom_mutators/honggfuzz/mangle.c +++ b/custom_mutators/honggfuzz/mangle.c @@ -51,7 +51,7 @@ static inline size_t mangle_LenLeft(run_t *run, size_t off) { } -/* Get a random value between <1:max> with x^2 distribution */ +/* Get a random value <1:max>, but prefer smaller ones - up to 4KiB */ static inline size_t mangle_getLen(size_t max) { if (max > _HF_INPUT_MAX_SIZE) { @@ -64,27 +64,25 @@ static inline size_t mangle_getLen(size_t max) { if (max == 0) { LOG_F("max == 0"); } if (max == 1) { return 1; } - const uint64_t max2 = (uint64_t)max * max; - const uint64_t max3 = (uint64_t)max * max * max; - const uint64_t rnd = util_rndGet(1, max2 - 1); + /* Give 50% chance the the uniform distribution */ + switch (util_rndGet(0, 9)) { - uint64_t ret = rnd * rnd; - ret /= max3; - ret += 1; - - if (ret < 1) { - - LOG_F("ret (%" PRIu64 ") < 1, max:%zu, rnd:%" PRIu64, ret, max, rnd); - - } - - if (ret > max) { - - LOG_F("ret (%" PRIu64 ") > max (%zu), rnd:%" PRIu64, ret, max, rnd); + case 0: + return (size_t)util_rndGet(1, HF_MIN(16, max)); + case 1: + return (size_t)util_rndGet(1, HF_MIN(64, max)); + case 2: + return (size_t)util_rndGet(1, HF_MIN(256, max)); + case 3: + return (size_t)util_rndGet(1, HF_MIN(1024, max)); + case 4: + return (size_t)util_rndGet(1, HF_MIN(4096, max)); + default: + break; } - return (size_t)ret; + return (size_t)util_rndGet(1, max); } diff --git a/custom_mutators/honggfuzz/mangle.h b/custom_mutators/honggfuzz/mangle.h index 1b6a4943..f8f3988c 100644 --- a/custom_mutators/honggfuzz/mangle.h +++ b/custom_mutators/honggfuzz/mangle.h @@ -26,7 +26,6 @@ #include "honggfuzz.h" -extern void mangle_mangleContent(run_t *run, int speed_factor); +extern void mangle_mangleContent(run_t* run, int speed_factor); #endif - diff --git a/docs/Changelog.md b/docs/Changelog.md index ae7377f2..f98f8b9b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -22,6 +22,10 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. - fixed a bug in redqueen for strings - llvm_mode: - now supports llvm 12! + - support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous + AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and + are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm + sancov, and also supports function matching! - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) - LTO: autodictionary mode is a default diff --git a/docs/FAQ.md b/docs/FAQ.md index c15cd484..e690635a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -112,12 +112,13 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! on start, check to which memory address the edge ID value is written and set a write breakpoint to that address (`watch 0x.....`). - 3. Third step: create a text file with the filenames + 3. Third step: create a text file with the filenames/functions Identify which source code files contain the functions that you need to - remove from instrumentation. + remove from instrumentation, or just specify the functions you want to + skip instrumenting. Note that optimization might inline functions! - Simply follow this document on how to do this: [llvm_mode/README.instrument_file.md](llvm_mode/README.instrument_file.md) + Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) diff --git a/docs/env_variables.md b/docs/env_variables.md index 811c5658..f0ae0b6c 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -202,14 +202,15 @@ Then there are a few specific features that are only available in llvm_mode: See llvm_mode/README.laf-intel.md for more information. -### INSTRUMENT_FILE +### INSTRUMENT LIST (selectively instrument files and functions) This feature allows selectively instrumentation of the source - - Setting AFL_LLVM_INSTRUMENT_FILE with a filename will only instrument those - files that match the names listed in this file. + - Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or + function will only instrument (or skip) those files that match the names + listed in the specified file. - See llvm_mode/README.instrument_file.md for more information. + See llvm_mode/README.instrument_list.md for more information. ### NOT_ZERO @@ -241,7 +242,7 @@ Then there are a few specific features that are only available in the gcc_plugin - Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those files that match the names listed in this file (one filename per line). - See gcc_plugin/README.instrument_file.md for more information. + See gcc_plugin/README.instrument_list.md for more information. ## 3) Settings for afl-fuzz diff --git a/docs/perf_tips.md b/docs/perf_tips.md index 7a690b77..731dc238 100644 --- a/docs/perf_tips.md +++ b/docs/perf_tips.md @@ -67,7 +67,7 @@ to get to the important parts in the code. If you are only interested in specific parts of the code being fuzzed, you can instrument_files the files that are actually relevant. This improves the speed and -accuracy of afl. See llvm_mode/README.instrument_file.md +accuracy of afl. See llvm_mode/README.instrument_list.md Also use the InsTrim mode on larger binaries, this improves performance and coverage a lot. diff --git a/gcc_plugin/GNUmakefile b/gcc_plugin/GNUmakefile index 4a4f0dcd..f10a6c1d 100644 --- a/gcc_plugin/GNUmakefile +++ b/gcc_plugin/GNUmakefile @@ -163,7 +163,7 @@ install: all install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH) install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH) install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md - install -m 644 -T README.instrument_file.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md + install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md clean: rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 diff --git a/gcc_plugin/Makefile b/gcc_plugin/Makefile index f720112f..c088b61c 100644 --- a/gcc_plugin/Makefile +++ b/gcc_plugin/Makefile @@ -152,7 +152,7 @@ install: all install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH) install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH) install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md - install -m 644 -T README.instrument_file.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md + install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md clean: rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 diff --git a/gcc_plugin/README.instrument_file.md b/gcc_plugin/README.instrument_list.md index d0eaf6ff..d0eaf6ff 100644 --- a/gcc_plugin/README.instrument_file.md +++ b/gcc_plugin/README.instrument_list.md diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index b82ddb4a..2324efa5 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -986,6 +986,8 @@ uint64_t rand_next(afl_state_t *afl); static inline u32 rand_below(afl_state_t *afl, u32 limit) { + if (limit <= 1) return 0; + /* The boundary not being necessarily a power of 2, we need to ensure the result uniformity. */ if (unlikely(!afl->rand_cnt--) && likely(!afl->fixed_seed)) { @@ -1001,6 +1003,32 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) { } +/* we prefer lower range values here */ +/* this is only called with normal havoc, not MOpt, to have an equalizer for + expand havoc mode */ +static inline u32 rand_below_datalen(afl_state_t *afl, u32 limit) { + + if (limit <= 1) return 0; + + switch (rand_below(afl, 3)) { + + case 2: + return (rand_below(afl, limit) % (1 + rand_below(afl, limit - 1))) % + (1 + rand_below(afl, limit - 1)); + break; + case 1: + return rand_below(afl, limit) % (1 + rand_below(afl, limit - 1)); + break; + case 0: + return rand_below(afl, limit); + break; + + } + + return 1; // cannot be reached + +} + static inline s64 rand_get_seed(afl_state_t *afl) { if (unlikely(afl->fixed_seed)) { return afl->init_seed; } diff --git a/include/envs.h b/include/envs.h index 7153ed47..96ae91ba 100644 --- a/include/envs.h +++ b/include/envs.h @@ -62,6 +62,9 @@ static char *afl_environment_variables[] = { "AFL_REAL_LD", "AFL_LD_PRELOAD", "AFL_LD_VERBOSE", + "AFL_LLVM_ALLOWLIST", + "AFL_LLVM_DENYLIST", + "AFL_LLVM_BLOCKLIST", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CTX", diff --git a/llvm_mode/README.instrument_file.md b/llvm_mode/README.instrument_file.md deleted file mode 100644 index 46e45ba2..00000000 --- a/llvm_mode/README.instrument_file.md +++ /dev/null @@ -1,81 +0,0 @@ -# Using afl++ with partial instrumentation - - This file describes how you can selectively instrument only the source files - that are interesting to you using the LLVM instrumentation provided by - afl++ - - Originally developed by Christian Holler (:decoder) <choller@mozilla.com>. - -## 1) Description and purpose - -When building and testing complex programs where only a part of the program is -the fuzzing target, it often helps to only instrument the necessary parts of -the program, leaving the rest uninstrumented. This helps to focus the fuzzer -on the important parts of the program, avoiding undesired noise and -disturbance by uninteresting code being exercised. - -For this purpose, I have added a "partial instrumentation" support to the LLVM -mode of AFLFuzz that allows you to specify on a source file level which files -should be compiled with or without instrumentation. - -Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: -https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation - -## 2) Building the LLVM module - -The new code is part of the existing afl++ LLVM module in the llvm_mode/ -subdirectory. There is nothing specifically to do :) - - -## 3) How to use the partial instrumentation mode - -In order to build with partial instrumentation, you need to build with -afl-clang-fast and afl-clang-fast++ respectively. The only required change is -that you need to set the environment variable AFL_LLVM_INSTRUMENT_FILE when calling -the compiler. - -The environment variable must point to a file containing all the filenames -that should be instrumented. For matching, the filename that is being compiled -must end in the filename entry contained in this the instrument file list (to avoid breaking -the matching when absolute paths are used during compilation). - -For example if your source tree looks like this: - -``` -project/ -project/feature_a/a1.cpp -project/feature_a/a2.cpp -project/feature_b/b1.cpp -project/feature_b/b2.cpp -``` - -and you only want to test feature_a, then create a the instrument file list file containing: - -``` -feature_a/a1.cpp -feature_a/a2.cpp -``` - -However if the instrument file list file contains only this, it works as well: - -``` -a1.cpp -a2.cpp -``` - -but it might lead to files being unwantedly instrumented if the same filename -exists somewhere else in the project directories. - -The created the instrument file list file is then set to AFL_LLVM_INSTRUMENT_FILE when you compile -your program. For each file that didn't match the the instrument file list, the compiler will -issue a warning at the end stating that no blocks were instrumented. If you -didn't intend to instrument that file, then you can safely ignore that warning. - -For old LLVM versions this feature might require to be compiled with debug -information (-g), however at least from llvm version 6.0 onwards this is not -required anymore (and might hurt performance and crash detection, so better not -use -g). - -## 4) UNIX-style filename pattern matching -You can add UNIX-style pattern matching in the the instrument file list entries. See `man -fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.instrument_list.md b/llvm_mode/README.instrument_list.md new file mode 100644 index 00000000..b0e0cc1e --- /dev/null +++ b/llvm_mode/README.instrument_list.md @@ -0,0 +1,86 @@ +# Using afl++ with partial instrumentation + + This file describes how you can selectively instrument only the source files + or functions that are interesting to you using the LLVM instrumentation + provided by afl++ + +## 1) Description and purpose + +When building and testing complex programs where only a part of the program is +the fuzzing target, it often helps to only instrument the necessary parts of +the program, leaving the rest uninstrumented. This helps to focus the fuzzer +on the important parts of the program, avoiding undesired noise and +disturbance by uninteresting code being exercised. + +For this purpose, a "partial instrumentation" support en par with llvm sancov +is provided by afl++ that allows you to specify on a source file and function +level which should be compiled with or without instrumentation. + +Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: +https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation + +the llvm sancov list format is fully supported by afl++, however afl++ has +more flexbility. + +## 2) Building the LLVM module + +The new code is part of the existing afl++ LLVM module in the llvm_mode/ +subdirectory. There is nothing specifically to do :) + +## 3) How to use the partial instrumentation mode + +In order to build with partial instrumentation, you need to build with +afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. +The only required change is that you need to set either the environment variable +AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename. + +That file then contains the filenames or functions that should be instrumented +(AFL_LLVM_ALLOWLIST) or should specifically NOT instrumentd (AFL_LLVM_DENYLIST). + +For matching, the function/filename that is being compiled must end in the +function/filename entry contained in this the instrument file list (to avoid +breaking the matching when absolute paths are used during compilation). + +**NOTE:** In optimization functions might be inlined and then not match! + +For example if your source tree looks like this: +``` +project/ +project/feature_a/a1.cpp +project/feature_a/a2.cpp +project/feature_b/b1.cpp +project/feature_b/b2.cpp +``` + +and you only want to test feature_a, then create a the instrument file list file containing: +``` +feature_a/a1.cpp +feature_a/a2.cpp +``` + +However if the instrument file list file contains only this, it works as well: +``` +a1.cpp +a2.cpp +``` +but it might lead to files being unwantedly instrumented if the same filename +exists somewhere else in the project directories. + +You can also specify function names. Note that for C++ the function names +must be mangled to match! + +afl++ is intelligent to identify if an entry is a filename or a function. +However if you want to be sure (and compliant to the sancov allow/blocklist +format), you can file entries like this: +``` +src: *malloc.c +``` +and function entries like this: +``` +fun: MallocFoo +``` +Note that whitespace is ignored and comments (`# foo`) supported. + +## 4) UNIX-style pattern matching +You can add UNIX-style pattern matching in the the instrument file list entries. +See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index e521ac82..4d643324 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -108,15 +108,12 @@ make install Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc. -Also the instrument file listing (AFL_LLVM_INSTRUMENT_FILE -> [README.instrument_file.md](README.instrument_file.md)) and +Also the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> [README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. -InsTrim (control flow graph instrumentation) is supported and recommended! - (set `AFL_LLVM_INSTRUMENT=CFG`) Example: ``` CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure -export AFL_LLVM_INSTRUMENT=CFG make ``` diff --git a/llvm_mode/README.md b/llvm_mode/README.md index 22088dfd..f23d7150 100644 --- a/llvm_mode/README.md +++ b/llvm_mode/README.md @@ -109,7 +109,7 @@ Several options are present to make llvm_mode faster or help it rearrange the code to make afl-fuzz path discovery easier. If you need just to instrument specific parts of the code, you can the instrument file list -which C/C++ files to actually instrument. See [README.instrument_file](README.instrument_file.md) +which C/C++ files to actually instrument. See [README.instrument_list](README.instrument_list.md) For splitting memcmp, strncmp, etc. please see [README.laf-intel](README.laf-intel.md) diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index ef99e3f3..16f2c9c0 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -161,7 +161,8 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { - u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, + preprocessor_only = 0; u8 have_pic = 0; u8 *name; @@ -229,7 +230,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode) { if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST")) { + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; @@ -398,6 +400,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; + + if (!strcmp(cur, "-E")) preprocessor_only = 1; cc_params[cc_par_cnt++] = cur; @@ -562,6 +566,16 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "none"; } + + if (preprocessor_only) { + /* In the preprocessor_only case (-E), we are not actually compiling at + all but requesting the compiler to output preprocessed sources only. + We must not add the runtime in this case because the compiler will + simply output its binary content back on stdout, breaking any build + systems that rely on a separate source preprocessing step. */ + cc_params[cc_par_cnt] = NULL; + return; + } #ifndef __ANDROID__ switch (bit_mode) { @@ -637,9 +651,13 @@ int main(int argc, char **argv, char **envp) { } - if ((getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) && + if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && getenv("AFL_DONT_OPTIMIZE")) - FATAL("AFL_LLVM_INSTRUMENT_FILE and AFL_DONT_OPTIMIZE cannot be combined"); + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || getenv("INSTRIM_LIB")) { @@ -787,15 +805,17 @@ int main(int argc, char **argv, char **envp) { #if LLVM_VERSION_MAJOR <= 6 instrument_mode = INSTRUMENT_AFL; #else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) { + if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { instrument_mode = INSTRUMENT_AFL; WARNF( "switching to classic instrumentation because " - "AFL_LLVM_INSTRUMENT_FILE does not work with PCGUARD. Use " - "-fsanitize-coverage-allowlist=allowlist.txt if you want to use " - "PCGUARD. Requires llvm 12+. See " - "https://clang.llvm.org/docs/" + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt if you want to use " + "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " "SanitizerCoverage.html#partially-disabling-instrumentation"); } else @@ -846,11 +866,14 @@ int main(int argc, char **argv, char **envp) { "together"); if (instrument_mode == INSTRUMENT_PCGUARD && - (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST"))) + (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) FATAL( "Instrumentation type PCGUARD does not support " - "AFL_LLVM_INSTRUMENT_FILE! Use " - "-fsanitize-coverage-allowlist=allowlist.txt instead (requires llvm " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " "12+), see " "https://clang.llvm.org/docs/" "SanitizerCoverage.html#partially-disabling-instrumentation"); diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 9a884ded..0b50c547 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -20,7 +20,10 @@ using namespace llvm; -static std::list<std::string> myInstrumentList; +static std::list<std::string> allowListFiles; +static std::list<std::string> allowListFunctions; +static std::list<std::string> denyListFiles; +static std::list<std::string> denyListFunctions; char *getBBName(const llvm::BasicBlock *BB) { @@ -57,7 +60,7 @@ bool isIgnoreFunction(const llvm::Function *F) { "asan.", "llvm.", "sancov.", - "__ubsan_handle_", + "__ubsan_", "ign.", "__afl_", "_fini", @@ -87,30 +90,166 @@ bool isIgnoreFunction(const llvm::Function *F) { void initInstrumentList() { - char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!instrumentListFilename) - instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); + char *allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); - if (instrumentListFilename) { + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST " + "but not both!"); + + if (allowlist) { std::string line; std::ifstream fileStream; - fileStream.open(instrumentListFilename); - if (!fileStream) - report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE"); + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_ALLOWLIST"); getline(fileStream, line); + while (fileStream) { - myInstrumentList.push_back(line); - getline(fileStream, line); + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_ALLOWLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); + + } } + if (debug) + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); + } - if (debug) - SAYF(cMGN "[D] " cRST "loaded instrument list with %zu entries\n", - myInstrumentList.size()); + if (denylist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_DENYLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_DENYLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); + + } + + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } } @@ -121,42 +260,173 @@ bool isInInstrumentList(llvm::Function *F) { if (!F->size() || isIgnoreFunction(F)) return false; // if we do not have a the instrument file list return true - if (myInstrumentList.empty()) return true; + if (!allowListFiles.empty() || !allowListFunctions.empty()) { + + if (!allowListFunctions.empty()) { + + std::string instFunction = F->getName().str(); + + for (std::list<std::string>::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - // let's try to get the filename for the function - auto bb = &F->getEntryBlock(); - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - DebugLoc Loc = IP->getDebugLoc(); + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; + + } + + } + + } + + } + + if (!allowListFiles.empty()) { + + // let's try to get the filename for the function + auto bb = &F->getEntryBlock(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + DebugLoc Loc = IP->getDebugLoc(); #if LLVM_VERSION_MAJOR >= 4 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) - if (Loc) { + if (Loc) { + + DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); + + unsigned int instLine = cDILoc->getLine(); + StringRef instFilename = cDILoc->getFilename(); - DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); + if (instFilename.str().empty()) { - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { + + instFilename = oDILoc->getFilename(); + instLine = oDILoc->getLine(); + + } + + } - if (instFilename.str().empty()) { + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { + for (std::list<std::string>::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + F->getName().str().c_str(), instFilename.str().c_str()); + return true; + + } + + } + + } + + } + + } + + } + +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + unsigned int instLine = cDILoc.getLineNumber(); + StringRef instFilename = cDILoc.getFilename(); + + (void)instLine; + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list<std::string>::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + return true; + + } + + } + + } + + } } } - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { +#endif + else { + + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return false; + + } + + return false; + + } + + if (!denyListFiles.empty() || !denyListFunctions.empty()) { + + if (!denyListFunctions.empty()) { - for (std::list<std::string>::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + std::string instFunction = F->getName().str(); + + for (std::list<std::string>::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -164,16 +434,16 @@ bool isInInstrumentList(llvm::Function *F) { * specified in the list. We also allow UNIX-style pattern * matching */ - if (instFilename.str().length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the list (%s), instrumenting ... \n", - F->getName().str().c_str(), instFilename.str().c_str()); - return true; + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; } @@ -183,35 +453,64 @@ bool isInInstrumentList(llvm::Function *F) { } - } + if (!denyListFiles.empty()) { -#else - if (!Loc.isUnknown()) { + // let's try to get the filename for the function + auto bb = &F->getEntryBlock(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + DebugLoc Loc = IP->getDebugLoc(); - DILocation cDILoc(Loc.getAsMDNode(F->getContext())); +#if LLVM_VERSION_MAJOR >= 4 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) + if (Loc) { - unsigned int instLine = cDILoc.getLineNumber(); - StringRef instFilename = cDILoc.getFilename(); + DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); - (void)instLine; - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { + unsigned int instLine = cDILoc->getLine(); + StringRef instFilename = cDILoc->getFilename(); - for (std::list<std::string>::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + if (instFilename.str().empty()) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { - if (instFilename.str().length() >= it->length()) { + instFilename = oDILoc->getFilename(); + instLine = oDILoc->getLine(); - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + } - return true; + } + + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list<std::string>::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the denylist (%s), not " + "instrumenting ... \n", + F->getName().str().c_str(), instFilename.str().c_str()); + return false; + + } + + } } @@ -221,23 +520,65 @@ bool isInInstrumentList(llvm::Function *F) { } - } +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + unsigned int instLine = cDILoc.getLineNumber(); + StringRef instFilename = cDILoc.getFilename(); + + (void)instLine; + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list<std::string>::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + return false; + + } + + } + + } + + } + + } + + } #endif - else { - - // we could not find out the location. in this case we say it is not - // in the the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return false; + else { + + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return true; + + } + + return true; } - return false; + return true; // not reached } diff --git a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc index ab7c0c58..a7331444 100644 --- a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc @@ -59,39 +59,9 @@ class AFLcheckIfInstrument : public ModulePass { static char ID; AFLcheckIfInstrument() : ModulePass(ID) { - int entries = 0; - if (getenv("AFL_DEBUG")) debug = 1; - char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!instrumentListFilename) - instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); - if (instrumentListFilename) { - - std::string line; - std::ifstream fileStream; - fileStream.open(instrumentListFilename); - if (!fileStream) - report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE"); - getline(fileStream, line); - while (fileStream) { - - myInstrumentList.push_back(line); - getline(fileStream, line); - entries++; - - } - - } else - - PFATAL( - "afl-llvm-lto-instrumentlist.so loaded without " - "AFL_LLVM_INSTRUMENT_FILE?!"); - - if (debug) - SAYF(cMGN "[D] " cRST - "loaded the instrument file list %s with %d entries\n", - instrumentListFilename, entries); + initInstrumentList(); } @@ -129,120 +99,28 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { for (auto &F : M) { if (F.size() < 1) continue; - // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - if (isIgnoreFunction(&F)) continue; - - BasicBlock::iterator IP = F.getEntryBlock().getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - if (!myInstrumentList.empty()) { - - bool instrumentFunction = false; - - /* Get the current location using debug information. - * For now, just instrument the block if we are not able - * to determine our location. */ - DebugLoc Loc = IP->getDebugLoc(); - if (Loc) { - - DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); - - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); - - if (instFilename.str().empty()) { - - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { - - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); - - } - - if (instFilename.str().empty()) { - if (!be_quiet) - WARNF( - "Function %s has no source file name information and will " - "not be instrumented.", - F.getName().str().c_str()); - continue; - - } - - } - - //(void)instLine; - - fprintf(stderr, "xxx %s %s\n", F.getName().str().c_str(), - instFilename.str().c_str()); - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in file %s\n", - F.getName().str().c_str(), instFilename.str().c_str()); - - for (std::list<std::string>::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. */ - if (instFilename.str().length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { - - instrumentFunction = true; - break; - - } - - } - - } - - } else { - - if (!be_quiet) - WARNF( - "No debug information found for function %s, recompile with -g " - "-O[1-3]", - F.getName().str().c_str()); - continue; - - } - - /* Either we couldn't figure out our location or the location is - * not the instrument file listed, so we skip instrumentation. - * We do this by renaming the function. */ - if (instrumentFunction == true) { - - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", - F.getName().str().c_str()); - - } else { - - if (debug) - SAYF(cMGN "[D] " cRST - "function %s is NOT in the instrument file list\n", - F.getName().str().c_str()); + // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - auto & Ctx = F.getContext(); - AttributeList Attrs = F.getAttributes(); - AttrBuilder NewAttrs; - NewAttrs.addAttribute("skipinstrument"); - F.setAttributes( - Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); + if (isInInstrumentList(&F)) { - } + if (debug) + SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", + F.getName().str().c_str()); } else { - PFATAL("InstrumentList is empty"); + if (debug) + SAYF(cMGN "[D] " cRST + "function %s is NOT in the instrument file list\n", + F.getName().str().c_str()); + + auto & Ctx = F.getContext(); + AttributeList Attrs = F.getAttributes(); + AttrBuilder NewAttrs; + NewAttrs.addAttribute("skipinstrument"); + F.setAttributes( + Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 1f0bf30e..77bce7d0 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1921,14 +1921,14 @@ havoc_stage: /* Flip a single bit somewhere. Spooky! */ - FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); + FLIP_BIT(out_buf, rand_below_datalen(afl, temp_len << 3)); break; case 1: /* Set byte to interesting value. */ - out_buf[rand_below(afl, temp_len)] = + out_buf[rand_below_datalen(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; break; @@ -1940,12 +1940,12 @@ havoc_stage: if (rand_below(afl, 2)) { - *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = + *(u16 *)(out_buf + rand_below_datalen(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { - *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( + *(u16 *)(out_buf + rand_below_datalen(afl, temp_len - 1)) = SWAP16( interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); } @@ -1960,12 +1960,12 @@ havoc_stage: if (rand_below(afl, 2)) { - *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = + *(u32 *)(out_buf + rand_below_datalen(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { - *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( + *(u32 *)(out_buf + rand_below_datalen(afl, temp_len - 3)) = SWAP32( interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); } @@ -1976,14 +1976,16 @@ havoc_stage: /* Randomly subtract from byte. */ - out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); + out_buf[rand_below_datalen(afl, temp_len)] -= + 1 + rand_below(afl, ARITH_MAX); break; case 5: /* Randomly add to byte. */ - out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); + out_buf[rand_below_datalen(afl, temp_len)] += + 1 + rand_below(afl, ARITH_MAX); break; case 6: @@ -1994,13 +1996,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below(afl, temp_len - 1); + u32 pos = rand_below_datalen(afl, temp_len - 1); *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below(afl, temp_len - 1); + u32 pos = rand_below_datalen(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); *(u16 *)(out_buf + pos) = @@ -2018,13 +2020,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below(afl, temp_len - 1); + u32 pos = rand_below_datalen(afl, temp_len - 1); *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below(afl, temp_len - 1); + u32 pos = rand_below_datalen(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); *(u16 *)(out_buf + pos) = @@ -2042,13 +2044,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below(afl, temp_len - 3); + u32 pos = rand_below_datalen(afl, temp_len - 3); *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below(afl, temp_len - 3); + u32 pos = rand_below_datalen(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); *(u32 *)(out_buf + pos) = @@ -2066,13 +2068,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below(afl, temp_len - 3); + u32 pos = rand_below_datalen(afl, temp_len - 3); *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below(afl, temp_len - 3); + u32 pos = rand_below_datalen(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); *(u32 *)(out_buf + pos) = @@ -2088,7 +2090,8 @@ havoc_stage: why not. We use XOR with 1-255 to eliminate the possibility of a no-op. */ - out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); + out_buf[rand_below_datalen(afl, temp_len)] ^= + 1 + rand_below(afl, 255); break; case 11 ... 12: { @@ -2105,7 +2108,7 @@ havoc_stage: del_len = choose_block_len(afl, temp_len - 1); - del_from = rand_below(afl, temp_len - del_len + 1); + del_from = rand_below_datalen(afl, temp_len - del_len + 1); memmove(out_buf + del_from, out_buf + del_from + del_len, temp_len - del_from - del_len); @@ -2129,7 +2132,7 @@ havoc_stage: if (actually_clone) { clone_len = choose_block_len(afl, temp_len); - clone_from = rand_below(afl, temp_len - clone_len + 1); + clone_from = rand_below_datalen(afl, temp_len - clone_len + 1); } else { @@ -2138,7 +2141,7 @@ havoc_stage: } - clone_to = rand_below(afl, temp_len); + clone_to = rand_below_datalen(afl, temp_len); new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); @@ -2156,8 +2159,9 @@ havoc_stage: } else { memset(new_buf + clone_to, - rand_below(afl, 2) ? rand_below(afl, 256) - : out_buf[rand_below(afl, temp_len)], + rand_below(afl, 2) + ? rand_below(afl, 256) + : out_buf[rand_below_datalen(afl, temp_len)], clone_len); } @@ -2186,8 +2190,8 @@ havoc_stage: copy_len = choose_block_len(afl, temp_len - 1); - copy_from = rand_below(afl, temp_len - copy_len + 1); - copy_to = rand_below(afl, temp_len - copy_len + 1); + copy_from = rand_below_datalen(afl, temp_len - copy_len + 1); + copy_to = rand_below_datalen(afl, temp_len - copy_len + 1); if (rand_below(afl, 4)) { @@ -2200,8 +2204,9 @@ havoc_stage: } else { memset(out_buf + copy_to, - rand_below(afl, 2) ? rand_below(afl, 256) - : out_buf[rand_below(afl, temp_len)], + rand_below(afl, 2) + ? rand_below(afl, 256) + : out_buf[rand_below_datalen(afl, temp_len)], copy_len); } @@ -2233,7 +2238,7 @@ havoc_stage: if (extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + insert_at = rand_below_datalen(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -2247,7 +2252,7 @@ havoc_stage: if (extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + insert_at = rand_below_datalen(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -2258,7 +2263,7 @@ havoc_stage: } else { // case 16 u32 use_extra, extra_len, - insert_at = rand_below(afl, temp_len + 1); + insert_at = rand_below_datalen(afl, temp_len + 1); u8 *ptr; /* Insert an extra. Do the same dice-rolling stuff as for the @@ -2362,8 +2367,8 @@ havoc_stage: copy_len = choose_block_len(afl, new_len - 1); if (copy_len > temp_len) copy_len = temp_len; - copy_from = rand_below(afl, new_len - copy_len + 1); - copy_to = rand_below(afl, temp_len - copy_len + 1); + copy_from = rand_below_datalen(afl, new_len - copy_len + 1); + copy_to = rand_below_datalen(afl, temp_len - copy_len + 1); memmove(out_buf + copy_to, new_buf + copy_from, copy_len); @@ -2372,9 +2377,9 @@ havoc_stage: u32 clone_from, clone_to, clone_len; clone_len = choose_block_len(afl, new_len); - clone_from = rand_below(afl, new_len - clone_len + 1); + clone_from = rand_below_datalen(afl, new_len - clone_len + 1); - clone_to = rand_below(afl, temp_len); + clone_to = rand_below_datalen(afl, temp_len); u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); @@ -2523,7 +2528,7 @@ retry_splicing: /* Split somewhere between the first and last differing byte. */ - split_at = f_diff + rand_below(afl, l_diff - f_diff); + split_at = f_diff + rand_below_datalen(afl, l_diff - f_diff); /* Do the thing. */ diff --git a/test/test-all.sh b/test/test-all.sh new file mode 100755 index 00000000..7175493b --- /dev/null +++ b/test/test-all.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +. ./test-pre.sh + +. ./test-basic.sh + +. ./test-llvm.sh + +. ./test-llvm-lto.sh + +. ./test-gcc-plugin.sh + +. ./test-compcov.sh + +. ./test-qemu-mode.sh + +. ./test-unicorn-mode.sh + +. ./test-custom-mutators.sh + +. ./test-unittests.sh + +. ./test-post.sh diff --git a/test/test-basic.sh b/test/test-basic.sh new file mode 100755 index 00000000..59269ffe --- /dev/null +++ b/test/test-basic.sh @@ -0,0 +1,125 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" +test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "i386" && { + test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && { + ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 + test -e test-instr.plain && { + $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] ${AFL_GCC} instrumentation present and working correctly" + } + } || { + $ECHO "$RED[!] ${AFL_GCC} instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 11 && { + $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] ${AFL_GCC} instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + } || { + $ECHO "$RED[!] ${AFL_GCC} failed" + echo CUT------------------------------------------------------------------CUT + uname -a + ../${AFL_GCC} -o test-instr.plain ../test-instr.c + echo CUT------------------------------------------------------------------CUT + CODE=1 + } + test -e test-compcov.harden && { + grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && { + $ECHO "$GREEN[+] ${AFL_GCC} hardened mode succeeded and is working" + } || { + $ECHO "$RED[!] ${AFL_GCC} hardened mode is not hardened" + CODE=1 + } + rm -f test-compcov.harden + } || { + $ECHO "$RED[!] ${AFL_GCC} hardened mode compilation failed" + CODE=1 + } + # now we want to be sure that afl-fuzz is working + # make sure core_pattern is set to core on linux + (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { + $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" + true + }) || + # make sure crash reporter is disabled on Mac OS X + (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { + $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" + true + }) || { + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for ${AFL_GCC}, this will take approx 10 seconds" + { + ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with ${AFL_GCC}" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with ${AFL_GCC}" + CODE=1 + } + echo 000000000000000000000000 > in/in2 + echo 111 > in/in3 + mkdir -p in2 + ../afl-cmin -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null 2>&1 # why is afl-forkserver writing to stderr? + CNT=`ls in2/* 2>/dev/null | wc -l` + case "$CNT" in + *2) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;; + *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" + CODE=1 + ;; + esac + rm -f in2/in* + export AFL_QUIET=1 + if command -v bash >/dev/null ; then { + ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null + CNT=`ls in2/* 2>/dev/null | wc -l` + case "$CNT" in + *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; + *) $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" + CODE=1 + ;; + esac + } else { + $ECHO "$YELLOW[-] no bash available, cannot test afl-cmin.bash" + INCOMPLETE=1 + } + fi + ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 + SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` + test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" + test "$SIZE" = 1 || { + $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" + CODE=1 + } + rm -rf in out errors in2 + unset AFL_QUIET + } + rm -f test-instr.plain + } || { + $ECHO "$YELLOW[-] afl is not compiled, cannot test" + INCOMPLETE=1 + } +} || { + $ECHO "$YELLOW[-] not an intel platform, cannot test afl-gcc" +} + +. ./test-post.sh diff --git a/test/test-compcov.sh b/test/test-compcov.sh new file mode 100755 index 00000000..905a4cbc --- /dev/null +++ b/test/test-compcov.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +. ./test-pre.sh + +test -z "$AFL_CC" && unset AFL_CC + +$ECHO "$BLUE[*] Testing: shared library extensions" +cc $CFLAGS -o test-compcov test-compcov.c > /dev/null 2>&1 +test -e ../libtokencap.so && { + AFL_TOKEN_FILE=token.out LD_PRELOAD=../libtokencap.so DYLD_INSERT_LIBRARIES=../libtokencap.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov foobar > /dev/null 2>&1 + grep -q BUGMENOT token.out > /dev/null 2>&1 && { + $ECHO "$GREEN[+] libtokencap did successfully capture tokens" + } || { + $ECHO "$RED[!] libtokencap did not capture tokens" + CODE=1 + } + rm -f token.out +} || { + $ECHO "$YELLOW[-] libtokencap is not compiled, cannot test" + INCOMPLETE=1 +} +test -e ../libdislocator.so && { + { + ulimit -c 1 + # DYLD_INSERT_LIBRARIES and DYLD_FORCE_FLAT_NAMESPACE is used on Darwin/MacOSX + LD_PRELOAD=../libdislocator.so DYLD_INSERT_LIBRARIES=../libdislocator.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov BUFFEROVERFLOW > test.out 2>/dev/null + } > /dev/null 2>&1 + grep -q BUFFEROVERFLOW test.out > /dev/null 2>&1 && { + $ECHO "$RED[!] libdislocator did not detect the memory corruption" + CODE=1 + } || { + $ECHO "$GREEN[+] libdislocator did successfully detect the memory corruption" + } + rm -f test.out core test-compcov.core core.test-compcov +} || { + $ECHO "$YELLOW[-] libdislocator is not compiled, cannot test" + INCOMPLETE=1 +} +rm -f test-compcov + +test -z "$AFL_CC" && { + if type gcc >/dev/null; then + export AFL_CC=gcc + else + if type clang >/dev/null; then + export AFL_CC=clang + fi + fi +} + +. ./test-post.sh diff --git a/test/test-custom-mutators.sh b/test/test-custom-mutators.sh new file mode 100755 index 00000000..b0a05e15 --- /dev/null +++ b/test/test-custom-mutators.sh @@ -0,0 +1,125 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: custom mutator" +test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { + # normalize path + CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../examples/custom_mutators;pwd) + test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && { + unset AFL_CC + # Compile the vulnerable program for single mutator + test -e ../afl-clang-fast && { + ../afl-clang-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 + } || { + test -e ../afl-gcc-fast && { + ../afl-gcc-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 + } || { + ../afl-gcc -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 + } + } + # Compile the vulnerable program for multiple mutators + test -e ../afl-clang-fast && { + ../afl-clang-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 + } || { + test -e ../afl-gcc-fast && { + ../afl-gcc-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 + } || { + ../afl-gcc -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 + } + } + # Compile the custom mutator + cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 + cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 + test -e test-custom-mutator -a -e ./libexamplemutator.so && { + # Create input directory + mkdir -p in + echo "00000" > in/in + + # Run afl-fuzz w/ the C mutator + $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 5 seconds" + { + AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 + } >>errors 2>&1 + + # Check results + test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here + $ECHO "$GREEN[+] afl-fuzz is working correctly with the C mutator" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with the C mutator" + CODE=1 + } + + # Clean + rm -rf out errors + + # Run afl-fuzz w/ multiple C mutators + $ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 5 seconds" + { + AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1 + } >>errors 2>&1 + + test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here + $ECHO "$GREEN[+] afl-fuzz is working correctly with multiple C mutators" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with multiple C mutators" + CODE=1 + } + + # Clean + rm -rf out errors + + # Run afl-fuzz w/ the Python mutator + $ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 5 seconds" + { + export PYTHONPATH=${CUSTOM_MUTATOR_PATH} + export AFL_PYTHON_MODULE=example + AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V5 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 + unset PYTHONPATH + unset AFL_PYTHON_MODULE + } >>errors 2>&1 + + # Check results + test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here + $ECHO "$GREEN[+] afl-fuzz is working correctly with the Python mutator" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with the Python mutator" + CODE=1 + } + + # Clean + rm -rf in out errors + rm -rf ${CUSTOM_MUTATOR_PATH}/__pycache__/ + rm -f test-multiple-mutators test-custom-mutator libexamplemutator.so libexamplemutator2.so + } || { + ls . + ls ${CUSTOM_MUTATOR_PATH} + $ECHO "$RED[!] cannot compile the test program or the custom mutator" + CODE=1 + } + + #test "$CODE" = 1 && { $ECHO "$YELLOW[!] custom mutator tests currently will not fail travis" ; CODE=0 ; } + + make -C ../examples/custom_mutators clean > /dev/null 2>&1 + rm -f test-custom-mutator + rm -f test-custom-mutators + } || { + $ECHO "$YELLOW[-] no custom mutators in $CUSTOM_MUTATOR_PATH, cannot test" + INCOMPLETE=1 + } + unset CUSTOM_MUTATOR_PATH +} || { + $ECHO "$YELLOW[-] no python support in afl-fuzz, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh new file mode 100755 index 00000000..2ed10a72 --- /dev/null +++ b/test/test-gcc-plugin.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: gcc_plugin" +test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && { + SAVE_AFL_CC=${AFL_CC} + export AFL_CC=`command -v gcc` + ../afl-gcc-fast -o test-instr.plain.gccpi ../test-instr.c > /dev/null 2>&1 + AFL_HARDEN=1 ../afl-gcc-fast -o test-compcov.harden.gccpi test-compcov.c > /dev/null 2>&1 + test -e test-instr.plain.gccpi && { + $ECHO "$GREEN[+] gcc_plugin compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain.gccpi > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain.gccpi < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] gcc_plugin instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] gcc_plugin instrumentation present and working correctly" + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { + $ECHO "$GREEN[+] gcc_plugin run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] gcc_plugin instrumentation produces a weird numbers: $TUPLES" + $ECHO "$YELLOW[-] this is a known issue in gcc, not afl++. It is not flagged as an error because travis builds would all fail otherwise :-(" + #CODE=1 + } + } + } || { + $ECHO "$RED[!] gcc_plugin instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + } || { + $ECHO "$RED[!] gcc_plugin failed" + CODE=1 + } + + test -e test-compcov.harden.gccpi && test_compcov_binary_functionality ./test-compcov.harden.gccpi && { + grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden.gccpi > /dev/null 2>&1 && { + $ECHO "$GREEN[+] gcc_plugin hardened mode succeeded and is working" + } || { + $ECHO "$RED[!] gcc_plugin hardened mode is not hardened" + CODE=1 + } + rm -f test-compcov.harden.gccpi + } || { + $ECHO "$RED[!] gcc_plugin hardened mode compilation failed" + CODE=1 + } + # now we want to be sure that afl-fuzz is working + (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { + $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" + true + }) || + # make sure crash reporter is disabled on Mac OS X + (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { + $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" + CODE=1 + true + }) || { + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for gcc_plugin, this will take approx 10 seconds" + { + ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain.gccpi >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with gcc_plugin" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with gcc_plugin" + CODE=1 + } + rm -rf in out errors + } + rm -f test-instr.plain.gccpi + + # now for the special gcc_plugin things + echo foobar.c > instrumentlist.txt + AFL_GCC_INSTRUMENT_FILE=instrumentlist.txt ../afl-gcc-fast -o test-compcov test-compcov.c > /dev/null 2>&1 + test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { + echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 1 tuples" && { + $ECHO "$GREEN[+] gcc_plugin instrumentlist feature works correctly" + } || { + $ECHO "$RED[!] gcc_plugin instrumentlist feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] gcc_plugin instrumentlist feature compilation failed" + CODE=1 + } + rm -f test-compcov test.out instrumentlist.txt + ../afl-gcc-fast -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 + test -e test-persistent && { + echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { + $ECHO "$GREEN[+] gcc_plugin persistent mode feature works correctly" + } || { + $ECHO "$RED[!] gcc_plugin persistent mode feature failed to work" + CODE=1 + } + } || { + $ECHO "$RED[!] gcc_plugin persistent mode feature compilation failed" + CODE=1 + } + rm -f test-persistent + export AFL_CC=${SAVE_AFL_CC} +} || { + $ECHO "$YELLOW[-] gcc_plugin not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-llvm-lto.sh b/test/test-llvm-lto.sh new file mode 100755 index 00000000..6b327633 --- /dev/null +++ b/test/test-llvm-lto.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: LTO llvm_mode" +test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { + # on FreeBSD need to set AFL_CC + test `uname -s` = 'FreeBSD' && { + if type clang >/dev/null; then + export AFL_CC=`command -v clang` + else + export AFL_CC=`$LLVM_CONFIG --bindir`/clang + fi + } + + ../afl-clang-lto -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + test -e test-instr.plain && { + $ECHO "$GREEN[+] llvm_mode LTO compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] llvm_mode LTO instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] llvm_mode LTO instrumentation present and working correctly" + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { + $ECHO "$GREEN[+] llvm_mode LTO run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + } + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + } || { + $ECHO "$RED[!] LTO llvm_mode failed" + CODE=1 + } + rm -f test-instr.plain + + echo foobar.c > instrumentlist.txt + AFL_DEBUG=1 AFL_LLVM_INSTRUMENT_FILE=instrumentlist.txt ../afl-clang-lto -o test-compcov test-compcov.c > test.out 2>&1 + test -e test-compcov && { + grep -q "No instrumentation targets found" test.out && { + $ECHO "$GREEN[+] llvm_mode LTO instrumentlist feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentlist feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentlist feature compilation failed" + CODE=1 + } + rm -f test-compcov test.out instrumentlist.txt + ../afl-clang-lto -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 + test -e test-persistent && { + echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { + $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode LTO persistent mode feature failed to work" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode LTO persistent mode feature compilation failed" + CODE=1 + } + rm -f test-persistent +} || { + $ECHO "$YELLOW[-] LTO llvm_mode not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-llvm.sh b/test/test-llvm.sh new file mode 100755 index 00000000..02d23f9c --- /dev/null +++ b/test/test-llvm.sh @@ -0,0 +1,230 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: llvm_mode, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" +test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { + # on FreeBSD need to set AFL_CC + test `uname -s` = 'FreeBSD' && { + if type clang >/dev/null; then + export AFL_CC=`command -v clang` + else + export AFL_CC=`$LLVM_CONFIG --bindir`/clang + fi + } + ../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1 + test -e test-instr.plain && { + $ECHO "$GREEN[+] llvm_mode compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] llvm_mode instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly" + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 8 && { + $ECHO "$GREEN[+] llvm_mode run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] llvm_mode instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + } + } || { + $ECHO "$RED[!] llvm_mode instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + } || { + $ECHO "$RED[!] llvm_mode failed" + CODE=1 + } + test -e test-compcov.harden && test_compcov_binary_functionality ./test-compcov.harden && { + grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && { + $ECHO "$GREEN[+] llvm_mode hardened mode succeeded and is working" + } || { + $ECHO "$RED[!] llvm_mode hardened mode is not hardened" + CODE=1 + } + rm -f test-compcov.harden + } || { + $ECHO "$RED[!] llvm_mode hardened mode compilation failed" + CODE=1 + } + # now we want to be sure that afl-fuzz is working + (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { + $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" + true + }) || + # make sure crash reporter is disabled on Mac OS X + (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { + $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" + CODE=1 + true + }) || { + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for llvm_mode, this will take approx 10 seconds" + { + ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode" + CODE=1 + } + test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" || { + echo 000000000000000000000000 > in/in2 + echo 111 > in/in3 + mkdir -p in2 + ../afl-cmin -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null 2>&1 # why is afl-forkserver writing to stderr? + CNT=`ls in2/* 2>/dev/null | wc -l` + case "$CNT" in + *2) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;; + *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" + CODE=1 + ;; + esac + rm -f in2/in* + export AFL_QUIET=1 + if type bash >/dev/null ; then { + ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null + CNT=`ls in2/* 2>/dev/null | wc -l` + case "$CNT" in + *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; + *) $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" + CODE=1 + ;; + esac + } else { + $ECHO "$YELLOW[-] no bash available, cannot test afl-cmin.bash" + INCOMPLETE=1 + } + fi + ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 + SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` + test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" + test "$SIZE" = 1 || { + $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" + CODE=1 + } + rm -rf in2 + } + rm -rf in out errors + } + rm -f test-instr.plain + + # now for the special llvm_mode things + test -e ../libLLVMInsTrim.so && { + AFL_LLVM_INSTRUMENT=CFG AFL_LLVM_INSTRIM_LOOPHEAD=1 ../afl-clang-fast -o test-instr.instrim ../test-instr.c > /dev/null 2>test.out + test -e test-instr.instrim && { + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.instrim 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 2 -a "$TUPLES" -lt 5 && { + $ECHO "$GREEN[+] llvm_mode InsTrim reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] llvm_mode InsTrim instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + rm -f test-instr.instrim test.out + } || { + $ECHO "$RED[!] llvm_mode InsTrim compilation failed" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] llvm_mode InsTrim not compiled, cannot test" + INCOMPLETE=1 + } + AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast -o test-compcov.compcov test-compcov.c > test.out 2>&1 + test -e test-compcov.compcov && test_compcov_binary_functionality ./test-compcov.compcov && { + grep --binary-files=text -Eq " [ 123][0-9][0-9] location| [3-9][0-9] location" test.out && { + $ECHO "$GREEN[+] llvm_mode laf-intel/compcov feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode laf-intel/compcov feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode laf-intel/compcov feature compilation failed" + CODE=1 + } + rm -f test-compcov.compcov test.out + AFL_LLVM_INSTRUMENT=AFL AFL_LLVM_LAF_SPLIT_FLOATS=1 ../afl-clang-fast -o test-floatingpoint test-floatingpoint.c >errors 2>&1 + test -e test-floatingpoint && { + mkdir -p in + echo ZZZZ > in/in + $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 30 seconds" + { + AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -s 1 -V30 -m ${MEM_LIMIT} -i in -o out -- ./test-floatingpoint >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/crashes/id:* 2>/dev/null )" && { + $ECHO "$GREEN[+] llvm_mode laf-intel floatingpoint splitting feature works correctly" + } || { + cat errors + $ECHO "$RED[!] llvm_mode laf-intel floatingpoint splitting feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode laf-intel floatingpoint splitting feature compilation failed" + CODE=1 + } + rm -f test-floatingpoint test.out in/in + echo foobar.c > instrumentlist.txt + AFL_DEBUG=1 AFL_LLVM_INSTRUMENT_FILE=instrumentlist.txt ../afl-clang-fast -o test-compcov test-compcov.c > test.out 2>&1 + test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { + grep -q "No instrumentation targets found" test.out && { + $ECHO "$GREEN[+] llvm_mode instrumentlist feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode instrumentlist feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode instrumentlist feature compilation failed" + CODE=1 + } + rm -f test-compcov test.out instrumentlist.txt + AFL_LLVM_CMPLOG=1 ../afl-clang-fast -o test-cmplog test-cmplog.c > /dev/null 2>&1 + test -e test-cmplog && { + $ECHO "$GREY[*] running afl-fuzz for llvm_mode cmplog, this will take approx 10 seconds" + { + mkdir -p in + echo 0000000000000000000000000 > in/in + ../afl-fuzz -m none -V10 -i in -o out -c./test-cmplog -- ./test-cmplog >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode cmplog" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode cmplog" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] we cannot test llvm_mode cmplog because it is not present" + INCOMPLETE=1 + } + rm -rf errors test-cmplog in + ../afl-clang-fast -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 + test -e test-persistent && { + echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { + $ECHO "$GREEN[+] llvm_mode persistent mode feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode persistent mode feature failed to work" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode persistent mode feature compilation failed" + CODE=1 + } + rm -f test-persistent +} || { + $ECHO "$YELLOW[-] llvm_mode not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh \ No newline at end of file diff --git a/test/test-post.sh b/test/test-post.sh new file mode 100755 index 00000000..c1e22498 --- /dev/null +++ b/test/test-post.sh @@ -0,0 +1,14 @@ +#!/bin/sh +AFL_TEST_DEPTH=$((AFL_TEST_DEPTH-1)) + +if [ $AFL_TEST_DEPTH = 0 ]; then +# All runs done :) + +$ECHO "$GREY[*] all test cases completed.$RESET" +test "$INCOMPLETE" = "0" && $ECHO "$GREEN[+] all test cases executed" +test "$INCOMPLETE" = "1" && $ECHO "$YELLOW[-] not all test cases were executed" +test "$CODE" = "0" && $ECHO "$GREEN[+] all tests were successful :-)$RESET" +test "$CODE" = "0" || $ECHO "$RED[!] failure in tests :-($RESET" +exit $CODE + +fi diff --git a/test/test-pre.sh b/test/test-pre.sh new file mode 100755 index 00000000..3e2d475d --- /dev/null +++ b/test/test-pre.sh @@ -0,0 +1,131 @@ +#!/bin/sh + +# All tests should start with sourcing test-pre.sh and finish with sourcing test-post.sh +# They may set an error code with $CODE=1 +# If tests are incomplete, they may set $INCOMPLETE=1 + +AFL_TEST_DEPTH=$((AFL_TEST_DEPTH+1)) + +if [ $AFL_TEST_DEPTH = 1 ]; then +# First run :) + +# +# Ensure we have: test, type, diff, grep -qE +# +test -z "" 2>/dev/null || { echo Error: test command not found ; exit 1 ; } +GREP=`type grep > /dev/null 2>&1 && echo OK` +test "$GREP" = OK || { echo Error: grep command not found ; exit 1 ; } +echo foobar | grep -qE 'asd|oob' 2>/dev/null || { echo Error: grep command does not support -q and/or -E option ; exit 1 ; } +test -e ./test-all.sh || cd $(dirname $0) || exit 1 +test -e ./test-all.sh || { echo Error: you must be in the test/ directory ; exit 1 ; } +export AFL_PATH=`pwd`/.. +export AFL_NO_AFFINITY=1 # workaround for travis that fails for no avail cores + +echo 1 > test.1 +echo 1 > test.2 +OK=OK +diff test.1 test.2 >/dev/null 2>&1 || OK= +rm -f test.1 test.2 +test -z "$OK" && { echo Error: diff is not working ; exit 1 ; } +test -z "$LLVM_CONFIG" && LLVM_CONFIG=llvm-config + +# check for '-a' option of grep +if grep -a test test-all.sh >/dev/null 2>&1; then + GREPAOPTION=' -a' +else + GREPAOPTION= +fi + +test_compcov_binary_functionality() { + RUN="../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- $1" + $RUN 'LIBTOKENCAP' | grep 'your string was LIBTOKENCAP' \ + && $RUN 'BUGMENOT' | grep 'your string was BUGMENOT' \ + && $RUN 'BANANA' | grep 'your string started with BAN' \ + && $RUN 'APRI' | grep 'your string was APRI' \ + && $RUN 'kiWI' | grep 'your string was Kiwi' \ + && $RUN 'Avocado' | grep 'your string was avocado' \ + && $RUN 'GRAX' 3 | grep 'your string was a prefix of Grapes' \ + && $RUN 'LOCALVARIABLE' | grep 'local var memcmp works!' \ + && $RUN 'abc' | grep 'short local var memcmp works!' \ + && $RUN 'GLOBALVARIABLE' | grep 'global var memcmp works!' +} > /dev/null + +ECHO="printf %b\\n" +$ECHO \\101 2>&1 | grep -qE '^A' || { + ECHO= + test -e /bin/printf && { + ECHO="/bin/printf %b\\n" + $ECHO "\\101" 2>&1 | grep -qE '^A' || ECHO= + } +} +test -z "$ECHO" && { printf Error: printf command does not support octal character codes ; exit 1 ; } + +export AFL_EXIT_WHEN_DONE=1 +export AFL_SKIP_CPUFREQ=1 +export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 +unset AFL_NO_X86 +unset AFL_QUIET +unset AFL_DEBUG +unset AFL_HARDEN +unset AFL_USE_ASAN +unset AFL_USE_MSAN +unset AFL_USE_UBSAN +unset AFL_TMPDIR +unset AFL_CC +unset AFL_PRELOAD +unset AFL_GCC_INSTRUMENT_FILE +unset AFL_LLVM_INSTRUMENT_FILE +unset AFL_LLVM_INSTRIM +unset AFL_LLVM_LAF_SPLIT_SWITCHES +unset AFL_LLVM_LAF_TRANSFORM_COMPARES +unset AFL_LLVM_LAF_SPLIT_COMPARES +unset AFL_QEMU_PERSISTENT_ADDR +unset AFL_QEMU_PERSISTENT_RETADDR_OFFSET +unset AFL_QEMU_PERSISTENT_GPR +unset AFL_QEMU_PERSISTENT_RET +unset AFL_QEMU_PERSISTENT_HOOK +unset AFL_QEMU_PERSISTENT_CNT +unset AFL_CUSTOM_MUTATOR_LIBRARY +unset AFL_PYTHON_MODULE +unset AFL_PRELOAD +unset LD_PRELOAD + +rm -rf in in2 out + +export ASAN_OPTIONS=detect_leaks=0:allocator_may_return_null=1:abort_on_error=1:symbolize=0 +export AFL_LLVM_INSTRUMENT=AFL + +# on OpenBSD we need to work with llvm from /usr/local/bin +test -e /usr/local/bin/opt && { + export PATH="/usr/local/bin:${PATH}" +} +# on MacOS X we prefer afl-clang over afl-gcc, because +# afl-gcc does not work there +test `uname -s` = 'Darwin' -o `uname -s` = 'FreeBSD' && { + AFL_GCC=afl-clang +} || { + AFL_GCC=afl-gcc +} +command -v gcc >/dev/null 2>&1 || AFL_GCC=afl-clang + +SYS=`uname -m` + +GREY="\\033[1;90m" +BLUE="\\033[1;94m" +GREEN="\\033[0;32m" +RED="\\033[0;31m" +YELLOW="\\033[1;93m" +RESET="\\033[0m" + +MEM_LIMIT=none + +export PATH="${PATH}:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" + +$ECHO "${RESET}${GREY}[*] starting afl++ test framework ..." + +test -z "$SYS" && $ECHO "$YELLOW[-] uname -m did not succeed" + +CODE=0 +INCOMPLETE=0 + +fi \ No newline at end of file diff --git a/test/test-qemu-mode.sh b/test/test-qemu-mode.sh new file mode 100755 index 00000000..85a0b8b5 --- /dev/null +++ b/test/test-qemu-mode.sh @@ -0,0 +1,217 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: qemu_mode" +test -e ../afl-qemu-trace && { + cc -pie -fPIE -o test-instr ../test-instr.c + cc -o test-compcov test-compcov.c + test -e test-instr -a -e test-compcov && { + { + mkdir -p in + echo 00000 > in/in + $ECHO "$GREY[*] running afl-fuzz for qemu_mode, this will take approx 10 seconds" + { + ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode" + RUNTIME=`grep execs_done out/fuzzer_stats | awk '{print$3}'` + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode" + CODE=1 + } + rm -f errors + + $ECHO "$GREY[*] running afl-fuzz for qemu_mode AFL_ENTRYPOINT, this will take approx 6 seconds" + { + { + if file test-instr | grep -q "32-bit"; then + # for 32-bit reduce 8 nibbles to the lower 7 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` + else + # for 64-bit reduce 16 nibbles to the lower 9 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` + fi + export AFL_ENTRYPOINT=`expr 0x4${ADDR_LOWER_PART}` + $ECHO AFL_ENTRYPOINT=$AFL_ENTRYPOINT - $(nm test-instr | grep "T main") - $(file ./test-instr) + ../afl-fuzz -m ${MEM_LIMIT} -V2 -Q -i in -o out -- ./test-instr + unset AFL_ENTRYPOINT + } >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode AFL_ENTRYPOINT" + RUNTIME=`grep execs_done out/fuzzer_stats | awk '{print$3}'` + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode AFL_ENTRYPOINT" + CODE=1 + } + rm -f errors + + test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { + test -e ../libcompcov.so && { + $ECHO "$GREY[*] running afl-fuzz for qemu_mode compcov, this will take approx 10 seconds" + { + export AFL_PRELOAD=../libcompcov.so + export AFL_COMPCOV_LEVEL=2 + ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-compcov >>errors 2>&1 + unset AFL_PRELOAD + unset AFL_COMPCOV_LEVEL + } >>errors 2>&1 + test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode compcov" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode compcov" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] we cannot test qemu_mode compcov because it is not present" + INCOMPLETE=1 + } + rm -f errors + } || { + $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode compcov" + } + + test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { + $ECHO "$GREY[*] running afl-fuzz for qemu_mode cmplog, this will take approx 10 seconds" + { + ../afl-fuzz -m none -V10 -Q -c 0 -i in -o out -- ./test-compcov >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode cmplog" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode cmplog" + CODE=1 + } + rm -f errors + } || { + $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode cmplog" + } + + test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { + $ECHO "$GREY[*] running afl-fuzz for persistent qemu_mode, this will take approx 10 seconds" + { + if file test-instr | grep -q "32-bit"; then + # for 32-bit reduce 8 nibbles to the lower 7 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` + else + # for 64-bit reduce 16 nibbles to the lower 9 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` + fi + export AFL_QEMU_PERSISTENT_ADDR=`expr 0x4${ADDR_LOWER_PART}` + export AFL_QEMU_PERSISTENT_GPR=1 + $ECHO "Info: AFL_QEMU_PERSISTENT_ADDR=$AFL_QEMU_PERSISTENT_ADDR <= $(nm test-instr | grep "T main" | awk '{print $1}')" + env|grep AFL_|sort + file test-instr + ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr + unset AFL_QEMU_PERSISTENT_ADDR + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with persistent qemu_mode" + RUNTIMEP=`grep execs_done out/fuzzer_stats | awk '{print$3}'` + test -n "$RUNTIME" -a -n "$RUNTIMEP" && { + DIFF=`expr $RUNTIMEP / $RUNTIME` + test "$DIFF" -gt 1 && { # must be at least twice as fast + $ECHO "$GREEN[+] persistent qemu_mode was noticeable faster than standard qemu_mode" + } || { + $ECHO "$YELLOW[-] persistent qemu_mode was not noticeable faster than standard qemu_mode" + } + } || { + $ECHO "$YELLOW[-] we got no data on executions performed? weird!" + } + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with persistent qemu_mode" + CODE=1 + } + rm -rf in out errors + } || { + $ECHO "$YELLOW[-] not an intel or arm platform, cannot test persistent qemu_mode" + } + + test -e ../qemu_mode/unsigaction/unsigaction32.so && { + ${AFL_CC} -o test-unsigaction32 -m32 test-unsigaction.c >> errors 2>&1 && { + ./test-unsigaction32 + RETVAL_NORMAL32=$? + LD_PRELOAD=../qemu_mode/unsigaction/unsigaction32.so ./test-unsigaction32 + RETVAL_LIBUNSIGACTION32=$? + test $RETVAL_NORMAL32 = "2" -a $RETVAL_LIBUNSIGACTION32 = "0" && { + $ECHO "$GREEN[+] qemu_mode unsigaction library (32 bit) ignores signals" + } || { + test $RETVAL_NORMAL32 != "2" && { + $ECHO "$RED[!] cannot trigger signal in test program (32 bit)" + } + test $RETVAL_LIBUNSIGACTION32 != "0" && { + $ECHO "$RED[!] signal in test program (32 bit) is not ignored with unsigaction" + } + CODE=1 + } + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] cannot compile test program (32 bit) for unsigaction library" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (32 bit) because it is not present" + INCOMPLETE=1 + } + test -e ../qemu_mode/unsigaction/unsigaction64.so && { + ${AFL_CC} -o test-unsigaction64 -m64 test-unsigaction.c >> errors 2>&1 && { + ./test-unsigaction64 + RETVAL_NORMAL64=$? + LD_PRELOAD=../qemu_mode/unsigaction/unsigaction64.so ./test-unsigaction64 + RETVAL_LIBUNSIGACTION64=$? + test $RETVAL_NORMAL64 = "2" -a $RETVAL_LIBUNSIGACTION64 = "0" && { + $ECHO "$GREEN[+] qemu_mode unsigaction library (64 bit) ignores signals" + } || { + test $RETVAL_NORMAL64 != "2" && { + $ECHO "$RED[!] cannot trigger signal in test program (64 bit)" + } + test $RETVAL_LIBUNSIGACTION64 != "0" && { + $ECHO "$RED[!] signal in test program (64 bit) is not ignored with unsigaction" + } + CODE=1 + } + unset LD_PRELOAD + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] cannot compile test program (64 bit) for unsigaction library" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (64 bit) because it is not present" + INCOMPLETE=1 + } + rm -rf errors test-unsigaction32 test-unsigaction64 + } + } || { + $ECHO "$RED[!] gcc compilation of test targets failed - what is going on??" + CODE=1 + } + + rm -f test-instr test-compcov +} || { + $ECHO "$YELLOW[-] qemu_mode is not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-unicorn-mode.sh b/test/test-unicorn-mode.sh new file mode 100755 index 00000000..eb2ad294 --- /dev/null +++ b/test/test-unicorn-mode.sh @@ -0,0 +1,112 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: unicorn_mode" +test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shellcode && { + test -e ../unicorn_mode/samples/simple/simple_target.bin -a -e ../unicorn_mode/samples/compcov_x64/compcov_target.bin && { + { + # We want to see python errors etc. in logs, in case something doesn't work + export AFL_DEBUG_CHILD_OUTPUT=1 + + # some python version should be available now + PYTHONS="`command -v python3` `command -v python` `command -v python2`" + EASY_INSTALL_FOUND=0 + for PYTHON in $PYTHONS ; do + + if $PYTHON -c "help('easy_install');" </dev/null | grep -q module ; then + + EASY_INSTALL_FOUND=1 + PY=$PYTHON + break + + fi + + done + if [ "0" = $EASY_INSTALL_FOUND ]; then + + echo "[-] Error: Python setup-tools not found. Run 'sudo apt-get install python-setuptools'." + PREREQ_NOTFOUND=1 + + fi + + + cd ../unicorn_mode/samples/persistent + make >>errors 2>&1 + $ECHO "$GREY[*] running afl-fuzz for unicorn_mode (persistent), this will take approx 25 seconds" + AFL_DEBUG_CHILD_OUTPUT=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode (persistent)" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode (persistent)" + CODE=1 + } + + rm -rf out errors >/dev/null + make clean >/dev/null + cd ../../../test + + # travis workaround + test "$PY" = "/opt/pyenv/shims/python" -a -x /usr/bin/python && PY=/usr/bin/python + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] Using python binary $PY" + if ! $PY -c 'import unicornafl' 2>/dev/null ; then + $ECHO "$YELLOW[-] we cannot test unicorn_mode for python because it is not present" + INCOMPLETE=1 + else + { + $ECHO "$GREY[*] running afl-fuzz for unicorn_mode in python, this will take approx 25 seconds" + { + ../afl-fuzz -m ${MEM_LIMIT} -V25 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/simple/simple_test_harness.py @@ >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode" + CODE=1 + } + rm -f errors + + printf '\x01\x01' > in/in + # This seed is close to the first byte of the comparison. + # If CompCov works, a new tuple will appear in the map => new input in queue + $ECHO "$GREY[*] running afl-fuzz for unicorn_mode compcov, this will take approx 35 seconds" + { + export AFL_COMPCOV_LEVEL=2 + ../afl-fuzz -m ${MEM_LIMIT} -V35 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ >>errors 2>&1 + unset AFL_COMPCOV_LEVEL + } >>errors 2>&1 + test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode compcov" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode compcov" + CODE=1 + } + rm -rf in out errors + } + fi + + unset AFL_DEBUG_CHILD_OUTPUT + + } + } || { + $ECHO "$RED[!] missing sample binaries in unicorn_mode/samples/ - what is going on??" + CODE=1 + } + +} || { + $ECHO "$YELLOW[-] unicorn_mode is not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-unittests.sh b/test/test-unittests.sh new file mode 100755 index 00000000..f540b5f8 --- /dev/null +++ b/test/test-unittests.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Execution cmocka Unit-Tests $GREY" +unset AFL_CC +make -C .. unit || CODE=1 INCOMPLETE=1 : + +. ./test-post.sh diff --git a/test/test.sh b/test/test.sh deleted file mode 100755 index 46843d4a..00000000 --- a/test/test.sh +++ /dev/null @@ -1,1181 +0,0 @@ -#!/bin/sh - -# -# Ensure we have: test, type, diff, grep -qE -# -test -z "" 2>/dev/null || { echo Error: test command not found ; exit 1 ; } -GREP=`type grep > /dev/null 2>&1 && echo OK` -test "$GREP" = OK || { echo Error: grep command not found ; exit 1 ; } -echo foobar | grep -qE 'asd|oob' 2>/dev/null || { echo Error: grep command does not support -q and/or -E option ; exit 1 ; } -test -e ./test.sh || cd $(dirname $0) || exit 1 -test -e ./test.sh || { echo Error: you must be in the test/ directory ; exit 1 ; } -export AFL_PATH=`pwd`/.. - -echo 1 > test.1 -echo 1 > test.2 -OK=OK -diff test.1 test.2 >/dev/null 2>&1 || OK= -rm -f test.1 test.2 -test -z "$OK" && { echo Error: diff is not working ; exit 1 ; } -test -z "$LLVM_CONFIG" && LLVM_CONFIG=llvm-config - -# check for '-a' option of grep -if grep -a test test.sh >/dev/null 2>&1; then - GREPAOPTION=' -a' -else - GREPAOPTION= -fi - -test_compcov_binary_functionality() { - RUN="../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- $1" - $RUN 'LIBTOKENCAP' | grep 'your string was LIBTOKENCAP' \ - && $RUN 'BUGMENOT' | grep 'your string was BUGMENOT' \ - && $RUN 'BANANA' | grep 'your string started with BAN' \ - && $RUN 'APRI' | grep 'your string was APRI' \ - && $RUN 'kiWI' | grep 'your string was Kiwi' \ - && $RUN 'Avocado' | grep 'your string was avocado' \ - && $RUN 'GRAX' 3 | grep 'your string was a prefix of Grapes' \ - && $RUN 'LOCALVARIABLE' | grep 'local var memcmp works!' \ - && $RUN 'abc' | grep 'short local var memcmp works!' \ - && $RUN 'GLOBALVARIABLE' | grep 'global var memcmp works!' -} > /dev/null - -ECHO="printf %b\\n" -$ECHO \\101 2>&1 | grep -qE '^A' || { - ECHO= - test -e /bin/printf && { - ECHO="/bin/printf %b\\n" - $ECHO "\\101" 2>&1 | grep -qE '^A' || ECHO= - } -} -test -z "$ECHO" && { printf Error: printf command does not support octal character codes ; exit 1 ; } - -CODE=0 -INCOMPLETE=0 - -export AFL_EXIT_WHEN_DONE=1 -export AFL_SKIP_CPUFREQ=1 -export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 -unset AFL_NO_X86 -unset AFL_QUIET -unset AFL_DEBUG -unset AFL_HARDEN -unset AFL_USE_ASAN -unset AFL_USE_MSAN -unset AFL_USE_UBSAN -unset AFL_TMPDIR -unset AFL_CC -unset AFL_PRELOAD -unset AFL_GCC_INSTRUMENT_FILE -unset AFL_LLVM_INSTRUMENT_FILE -unset AFL_LLVM_INSTRIM -unset AFL_LLVM_LAF_SPLIT_SWITCHES -unset AFL_LLVM_LAF_TRANSFORM_COMPARES -unset AFL_LLVM_LAF_SPLIT_COMPARES -unset AFL_QEMU_PERSISTENT_ADDR -unset AFL_QEMU_PERSISTENT_RETADDR_OFFSET -unset AFL_QEMU_PERSISTENT_GPR -unset AFL_QEMU_PERSISTENT_RET -unset AFL_QEMU_PERSISTENT_HOOK -unset AFL_QEMU_PERSISTENT_CNT -unset AFL_CUSTOM_MUTATOR_LIBRARY -unset AFL_PYTHON_MODULE -unset AFL_PRELOAD -unset LD_PRELOAD - -rm -rf in in2 out - -export ASAN_OPTIONS=detect_leaks=0:allocator_may_return_null=1:abort_on_error=1:symbolize=0 -export AFL_LLVM_INSTRUMENT=AFL - -# on OpenBSD we need to work with llvm from /usr/local/bin -test -e /usr/local/bin/opt && { - export PATH="/usr/local/bin:${PATH}" -} -# on MacOS X we prefer afl-clang over afl-gcc, because -# afl-gcc does not work there -test `uname -s` = 'Darwin' -o `uname -s` = 'FreeBSD' && { - AFL_GCC=afl-clang -} || { - AFL_GCC=afl-gcc -} -command -v gcc >/dev/null 2>&1 || AFL_GCC=afl-clang - -SYS=`uname -m` - -GREY="\\033[1;90m" -BLUE="\\033[1;94m" -GREEN="\\033[0;32m" -RED="\\033[0;31m" -YELLOW="\\033[1;93m" -RESET="\\033[0m" - -MEM_LIMIT=none - -export PATH="${PATH}:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" - -$ECHO "${RESET}${GREY}[*] starting afl++ test framework ..." - -test -z "$SYS" && $ECHO "$YELLOW[-] uname -m did not succeed" - -$ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" -test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "i386" && { - test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && { - ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1 - AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 - test -e test-instr.plain && { - $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 - test -e test-instr.plain.0 -a -e test-instr.plain.1 && { - diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" - CODE=1 - } || { - $ECHO "$GREEN[+] ${AFL_GCC} instrumentation present and working correctly" - } - } || { - $ECHO "$RED[!] ${AFL_GCC} instrumentation failed" - CODE=1 - } - rm -f test-instr.plain.0 test-instr.plain.1 - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 11 && { - $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] ${AFL_GCC} instrumentation produces weird numbers: $TUPLES" - CODE=1 - } - } || { - $ECHO "$RED[!] ${AFL_GCC} failed" - echo CUT------------------------------------------------------------------CUT - uname -a - ../${AFL_GCC} -o test-instr.plain ../test-instr.c - echo CUT------------------------------------------------------------------CUT - CODE=1 - } - test -e test-compcov.harden && { - grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && { - $ECHO "$GREEN[+] ${AFL_GCC} hardened mode succeeded and is working" - } || { - $ECHO "$RED[!] ${AFL_GCC} hardened mode is not hardened" - CODE=1 - } - rm -f test-compcov.harden - } || { - $ECHO "$RED[!] ${AFL_GCC} hardened mode compilation failed" - CODE=1 - } - # now we want to be sure that afl-fuzz is working - # make sure core_pattern is set to core on linux - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || - # make sure crash reporter is disabled on Mac OS X - (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { - $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" - true - }) || { - mkdir -p in - echo 0 > in/in - $ECHO "$GREY[*] running afl-fuzz for ${AFL_GCC}, this will take approx 10 seconds" - { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with ${AFL_GCC}" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with ${AFL_GCC}" - CODE=1 - } - echo 000000000000000000000000 > in/in2 - echo 111 > in/in3 - mkdir -p in2 - ../afl-cmin -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null 2>&1 # why is afl-forkserver writing to stderr? - CNT=`ls in2/* 2>/dev/null | wc -l` - case "$CNT" in - *2) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;; - *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" - CODE=1 - ;; - esac - rm -f in2/in* - export AFL_QUIET=1 - if command -v bash >/dev/null ; then { - ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null - CNT=`ls in2/* 2>/dev/null | wc -l` - case "$CNT" in - *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; - *) $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" - CODE=1 - ;; - esac - } else { - $ECHO "$YELLOW[-] no bash available, cannot test afl-cmin.bash" - INCOMPLETE=1 - } - fi - ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 - SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` - test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" - test "$SIZE" = 1 || { - $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" - CODE=1 - } - rm -rf in out errors in2 - unset AFL_QUIET - } - rm -f test-instr.plain - } || { - $ECHO "$YELLOW[-] afl is not compiled, cannot test" - INCOMPLETE=1 - } -} || { - $ECHO "$YELLOW[-] not an intel platform, cannot test afl-gcc" -} - -$ECHO "$BLUE[*] Testing: llvm_mode, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" -test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { - # on FreeBSD need to set AFL_CC - test `uname -s` = 'FreeBSD' && { - if type clang >/dev/null; then - export AFL_CC=`command -v clang` - else - export AFL_CC=`$LLVM_CONFIG --bindir`/clang - fi - } - ../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 - AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1 - test -e test-instr.plain && { - $ECHO "$GREEN[+] llvm_mode compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 - test -e test-instr.plain.0 -a -e test-instr.plain.1 && { - diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] llvm_mode instrumentation should be different on different input but is not" - CODE=1 - } || { - $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { - $ECHO "$GREEN[+] llvm_mode run reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] llvm_mode instrumentation produces weird numbers: $TUPLES" - CODE=1 - } - } - } || { - $ECHO "$RED[!] llvm_mode instrumentation failed" - CODE=1 - } - rm -f test-instr.plain.0 test-instr.plain.1 - } || { - $ECHO "$RED[!] llvm_mode failed" - CODE=1 - } - test -e test-compcov.harden && test_compcov_binary_functionality ./test-compcov.harden && { - grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && { - $ECHO "$GREEN[+] llvm_mode hardened mode succeeded and is working" - } || { - $ECHO "$RED[!] llvm_mode hardened mode is not hardened" - CODE=1 - } - rm -f test-compcov.harden - } || { - $ECHO "$RED[!] llvm_mode hardened mode compilation failed" - CODE=1 - } - # now we want to be sure that afl-fuzz is working - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || - # make sure crash reporter is disabled on Mac OS X - (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { - $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" - CODE=1 - true - }) || { - mkdir -p in - echo 0 > in/in - $ECHO "$GREY[*] running afl-fuzz for llvm_mode, this will take approx 10 seconds" - { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode" - CODE=1 - } - test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" || { - echo 000000000000000000000000 > in/in2 - echo 111 > in/in3 - mkdir -p in2 - ../afl-cmin -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null 2>&1 # why is afl-forkserver writing to stderr? - CNT=`ls in2/* 2>/dev/null | wc -l` - case "$CNT" in - *2) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;; - *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" - CODE=1 - ;; - esac - rm -f in2/in* - export AFL_QUIET=1 - if type bash >/dev/null ; then { - ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null - CNT=`ls in2/* 2>/dev/null | wc -l` - case "$CNT" in - *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; - *) $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" - CODE=1 - ;; - esac - } else { - $ECHO "$YELLOW[-] no bash available, cannot test afl-cmin.bash" - INCOMPLETE=1 - } - fi - ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 - SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` - test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" - test "$SIZE" = 1 || { - $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" - CODE=1 - } - rm -rf in2 - } - rm -rf in out errors - } - rm -f test-instr.plain - - # now for the special llvm_mode things - test -e ../libLLVMInsTrim.so && { - AFL_LLVM_INSTRUMENT=CFG AFL_LLVM_INSTRIM_LOOPHEAD=1 ../afl-clang-fast -o test-instr.instrim ../test-instr.c > /dev/null 2>test.out - test -e test-instr.instrim && { - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.instrim 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 2 -a "$TUPLES" -lt 5 && { - $ECHO "$GREEN[+] llvm_mode InsTrim reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] llvm_mode InsTrim instrumentation produces weird numbers: $TUPLES" - CODE=1 - } - rm -f test-instr.instrim test.out - } || { - $ECHO "$RED[!] llvm_mode InsTrim compilation failed" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] llvm_mode InsTrim not compiled, cannot test" - INCOMPLETE=1 - } - AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast -o test-compcov.compcov test-compcov.c > test.out 2>&1 - test -e test-compcov.compcov && test_compcov_binary_functionality ./test-compcov.compcov && { - grep --binary-files=text -Eq " [ 123][0-9][0-9] location| [3-9][0-9] location" test.out && { - $ECHO "$GREEN[+] llvm_mode laf-intel/compcov feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode laf-intel/compcov feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode laf-intel/compcov feature compilation failed" - CODE=1 - } - rm -f test-compcov.compcov test.out - AFL_LLVM_INSTRUMENT=AFL AFL_LLVM_LAF_SPLIT_FLOATS=1 ../afl-clang-fast -o test-floatingpoint test-floatingpoint.c >errors 2>&1 - test -e test-floatingpoint && { - mkdir -p in - echo ZZZZ > in/in - $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 30 seconds" - { - AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -s 1 -V30 -m ${MEM_LIMIT} -i in -o out -- ./test-floatingpoint >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/crashes/id:* 2>/dev/null )" && { - $ECHO "$GREEN[+] llvm_mode laf-intel floatingpoint splitting feature works correctly" - } || { - cat errors - $ECHO "$RED[!] llvm_mode laf-intel floatingpoint splitting feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode laf-intel floatingpoint splitting feature compilation failed" - CODE=1 - } - rm -f test-floatingpoint test.out in/in - echo foobar.c > instrumentlist.txt - AFL_DEBUG=1 AFL_LLVM_INSTRUMENT_FILE=instrumentlist.txt ../afl-clang-fast -o test-compcov test-compcov.c > test.out 2>&1 - test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { - grep -q "No instrumentation targets found" test.out && { - $ECHO "$GREEN[+] llvm_mode instrumentlist feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode instrumentlist feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode instrumentlist feature compilation failed" - CODE=1 - } - rm -f test-compcov test.out instrumentlist.txt - AFL_LLVM_CMPLOG=1 ../afl-clang-fast -o test-cmplog test-cmplog.c > /dev/null 2>&1 - test -e test-cmplog && { - $ECHO "$GREY[*] running afl-fuzz for llvm_mode cmplog, this will take approx 10 seconds" - { - mkdir -p in - echo 0000000000000000000000000 > in/in - ../afl-fuzz -m none -V10 -i in -o out -c./test-cmplog -- ./test-cmplog >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode cmplog" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode cmplog" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] we cannot test llvm_mode cmplog because it is not present" - INCOMPLETE=1 - } - rm -rf errors test-cmplog in - ../afl-clang-fast -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 - test -e test-persistent && { - echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { - $ECHO "$GREEN[+] llvm_mode persistent mode feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode persistent mode feature failed to work" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode persistent mode feature compilation failed" - CODE=1 - } - rm -f test-persistent -} || { - $ECHO "$YELLOW[-] llvm_mode not compiled, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Testing: LTO llvm_mode" -test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { - # on FreeBSD need to set AFL_CC - test `uname -s` = 'FreeBSD' && { - if type clang >/dev/null; then - export AFL_CC=`command -v clang` - else - export AFL_CC=`$LLVM_CONFIG --bindir`/clang - fi - } - - ../afl-clang-lto -o test-instr.plain ../test-instr.c > /dev/null 2>&1 - test -e test-instr.plain && { - $ECHO "$GREEN[+] llvm_mode LTO compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 - test -e test-instr.plain.0 -a -e test-instr.plain.1 && { - diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] llvm_mode LTO instrumentation should be different on different input but is not" - CODE=1 - } || { - $ECHO "$GREEN[+] llvm_mode LTO instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { - $ECHO "$GREEN[+] llvm_mode LTO run reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] llvm_mode LTO instrumentation produces weird numbers: $TUPLES" - CODE=1 - } - } - } || { - $ECHO "$RED[!] llvm_mode LTO instrumentation failed" - CODE=1 - } - rm -f test-instr.plain.0 test-instr.plain.1 - } || { - $ECHO "$RED[!] LTO llvm_mode failed" - CODE=1 - } - rm -f test-instr.plain - - echo foobar.c > instrumentlist.txt - AFL_DEBUG=1 AFL_LLVM_INSTRUMENT_FILE=instrumentlist.txt ../afl-clang-lto -o test-compcov test-compcov.c > test.out 2>&1 - test -e test-compcov && { - grep -q "No instrumentation targets found" test.out && { - $ECHO "$GREEN[+] llvm_mode LTO instrumentlist feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode LTO instrumentlist feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode LTO instrumentlist feature compilation failed" - CODE=1 - } - rm -f test-compcov test.out instrumentlist.txt - ../afl-clang-lto -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 - test -e test-persistent && { - echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { - $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode LTO persistent mode feature failed to work" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode LTO persistent mode feature compilation failed" - CODE=1 - } - rm -f test-persistent -} || { - $ECHO "$YELLOW[-] LTO llvm_mode not compiled, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Testing: gcc_plugin" -test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && { - SAVE_AFL_CC=${AFL_CC} - export AFL_CC=`command -v gcc` - ../afl-gcc-fast -o test-instr.plain.gccpi ../test-instr.c > /dev/null 2>&1 - AFL_HARDEN=1 ../afl-gcc-fast -o test-compcov.harden.gccpi test-compcov.c > /dev/null 2>&1 - test -e test-instr.plain.gccpi && { - $ECHO "$GREEN[+] gcc_plugin compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain.gccpi > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain.gccpi < /dev/null > /dev/null 2>&1 - test -e test-instr.plain.0 -a -e test-instr.plain.1 && { - diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] gcc_plugin instrumentation should be different on different input but is not" - CODE=1 - } || { - $ECHO "$GREEN[+] gcc_plugin instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { - $ECHO "$GREEN[+] gcc_plugin run reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] gcc_plugin instrumentation produces a weird numbers: $TUPLES" - $ECHO "$YELLOW[-] this is a known issue in gcc, not afl++. It is not flagged as an error because travis builds would all fail otherwise :-(" - #CODE=1 - } - } - } || { - $ECHO "$RED[!] gcc_plugin instrumentation failed" - CODE=1 - } - rm -f test-instr.plain.0 test-instr.plain.1 - } || { - $ECHO "$RED[!] gcc_plugin failed" - CODE=1 - } - - test -e test-compcov.harden.gccpi && test_compcov_binary_functionality ./test-compcov.harden.gccpi && { - grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden.gccpi > /dev/null 2>&1 && { - $ECHO "$GREEN[+] gcc_plugin hardened mode succeeded and is working" - } || { - $ECHO "$RED[!] gcc_plugin hardened mode is not hardened" - CODE=1 - } - rm -f test-compcov.harden.gccpi - } || { - $ECHO "$RED[!] gcc_plugin hardened mode compilation failed" - CODE=1 - } - # now we want to be sure that afl-fuzz is working - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || - # make sure crash reporter is disabled on Mac OS X - (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { - $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" - CODE=1 - true - }) || { - mkdir -p in - echo 0 > in/in - $ECHO "$GREY[*] running afl-fuzz for gcc_plugin, this will take approx 10 seconds" - { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain.gccpi >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with gcc_plugin" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with gcc_plugin" - CODE=1 - } - rm -rf in out errors - } - rm -f test-instr.plain.gccpi - - # now for the special gcc_plugin things - echo foobar.c > instrumentlist.txt - AFL_GCC_INSTRUMENT_FILE=instrumentlist.txt ../afl-gcc-fast -o test-compcov test-compcov.c > /dev/null 2>&1 - test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { - echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 1 tuples" && { - $ECHO "$GREEN[+] gcc_plugin instrumentlist feature works correctly" - } || { - $ECHO "$RED[!] gcc_plugin instrumentlist feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] gcc_plugin instrumentlist feature compilation failed" - CODE=1 - } - rm -f test-compcov test.out instrumentlist.txt - ../afl-gcc-fast -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 - test -e test-persistent && { - echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { - $ECHO "$GREEN[+] gcc_plugin persistent mode feature works correctly" - } || { - $ECHO "$RED[!] gcc_plugin persistent mode feature failed to work" - CODE=1 - } - } || { - $ECHO "$RED[!] gcc_plugin persistent mode feature compilation failed" - CODE=1 - } - rm -f test-persistent - export AFL_CC=${SAVE_AFL_CC} -} || { - $ECHO "$YELLOW[-] gcc_plugin not compiled, cannot test" - INCOMPLETE=1 -} - -test -z "$AFL_CC" && unset AFL_CC - -$ECHO "$BLUE[*] Testing: shared library extensions" -cc $CFLAGS -o test-compcov test-compcov.c > /dev/null 2>&1 -test -e ../libtokencap.so && { - AFL_TOKEN_FILE=token.out LD_PRELOAD=../libtokencap.so DYLD_INSERT_LIBRARIES=../libtokencap.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov foobar > /dev/null 2>&1 - grep -q BUGMENOT token.out > /dev/null 2>&1 && { - $ECHO "$GREEN[+] libtokencap did successfully capture tokens" - } || { - $ECHO "$RED[!] libtokencap did not capture tokens" - CODE=1 - } - rm -f token.out -} || { - $ECHO "$YELLOW[-] libtokencap is not compiled, cannot test" - INCOMPLETE=1 -} -test -e ../libdislocator.so && { - { - ulimit -c 1 - # DYLD_INSERT_LIBRARIES and DYLD_FORCE_FLAT_NAMESPACE is used on Darwin/MacOSX - LD_PRELOAD=../libdislocator.so DYLD_INSERT_LIBRARIES=../libdislocator.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov BUFFEROVERFLOW > test.out 2>/dev/null - } > /dev/null 2>&1 - grep -q BUFFEROVERFLOW test.out > /dev/null 2>&1 && { - $ECHO "$RED[!] libdislocator did not detect the memory corruption" - CODE=1 - } || { - $ECHO "$GREEN[+] libdislocator did successfully detect the memory corruption" - } - rm -f test.out core test-compcov.core core.test-compcov -} || { - $ECHO "$YELLOW[-] libdislocator is not compiled, cannot test" - INCOMPLETE=1 -} -rm -f test-compcov -#test -e ../libradamsa.so && { -# # on FreeBSD need to set AFL_CC -# test `uname -s` = 'FreeBSD' && { -# if type clang >/dev/null; then -# export AFL_CC=`command -v clang` -# else -# export AFL_CC=`$LLVM_CONFIG --bindir`/clang -# fi -# } -# test -e test-instr.plain || ../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 -# test -e test-instr.plain || ../afl-gcc-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 -# test -e test-instr.plain || ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1 -# test -e test-instr.plain && { -# mkdir -p in -# printf 1 > in/in -# $ECHO "$GREY[*] running afl-fuzz with radamsa, this will take approx 10 seconds" -# { -# ../afl-fuzz -RR -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain -# } >>errors 2>&1 -# test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { -# $ECHO "$GREEN[+] libradamsa performs good - and very slow - mutations" -# } || { -# echo CUT------------------------------------------------------------------CUT -# cat errors -# echo CUT------------------------------------------------------------------CUT -# $ECHO "$RED[!] libradamsa failed" -# CODE=1 -# } -# rm -rf in out errors test-instr.plain -# } || { -# $ECHO "$YELLOW[-] compilation of test target failed, cannot test libradamsa" -# INCOMPLETE=1 -# } -#} || { -# $ECHO "$YELLOW[-] libradamsa is not compiled, cannot test" -# INCOMPLETE=1 -#} - -test -z "$AFL_CC" && { - if type gcc >/dev/null; then - export AFL_CC=gcc - else - if type clang >/dev/null; then - export AFL_CC=clang - fi - fi -} - -$ECHO "$BLUE[*] Testing: qemu_mode" -test -e ../afl-qemu-trace && { - cc -pie -fPIE -o test-instr ../test-instr.c - cc -o test-compcov test-compcov.c - test -e test-instr -a -e test-compcov && { - { - mkdir -p in - echo 00000 > in/in - $ECHO "$GREY[*] running afl-fuzz for qemu_mode, this will take approx 10 seconds" - { - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode" - RUNTIME=`grep execs_done out/fuzzer_stats | awk '{print$3}'` - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode" - CODE=1 - } - rm -f errors - - $ECHO "$GREY[*] running afl-fuzz for qemu_mode AFL_ENTRYPOINT, this will take approx 6 seconds" - { - { - if file test-instr | grep -q "32-bit"; then - # for 32-bit reduce 8 nibbles to the lower 7 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` - else - # for 64-bit reduce 16 nibbles to the lower 9 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` - fi - export AFL_ENTRYPOINT=`expr 0x4${ADDR_LOWER_PART}` - $ECHO AFL_ENTRYPOINT=$AFL_ENTRYPOINT - $(nm test-instr | grep "T main") - $(file ./test-instr) - ../afl-fuzz -m ${MEM_LIMIT} -V2 -Q -i in -o out -- ./test-instr - unset AFL_ENTRYPOINT - } >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode AFL_ENTRYPOINT" - RUNTIME=`grep execs_done out/fuzzer_stats | awk '{print$3}'` - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode AFL_ENTRYPOINT" - CODE=1 - } - rm -f errors - - test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { - test -e ../libcompcov.so && { - $ECHO "$GREY[*] running afl-fuzz for qemu_mode compcov, this will take approx 10 seconds" - { - export AFL_PRELOAD=../libcompcov.so - export AFL_COMPCOV_LEVEL=2 - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-compcov >>errors 2>&1 - unset AFL_PRELOAD - unset AFL_COMPCOV_LEVEL - } >>errors 2>&1 - test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode compcov" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode compcov" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] we cannot test qemu_mode compcov because it is not present" - INCOMPLETE=1 - } - rm -f errors - } || { - $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode compcov" - } - - test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { - $ECHO "$GREY[*] running afl-fuzz for qemu_mode cmplog, this will take approx 10 seconds" - { - ../afl-fuzz -m none -V10 -Q -c 0 -i in -o out -- ./test-compcov >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode cmplog" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode cmplog" - CODE=1 - } - rm -f errors - } || { - $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode cmplog" - } - - test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { - $ECHO "$GREY[*] running afl-fuzz for persistent qemu_mode, this will take approx 10 seconds" - { - if file test-instr | grep -q "32-bit"; then - # for 32-bit reduce 8 nibbles to the lower 7 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` - else - # for 64-bit reduce 16 nibbles to the lower 9 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` - fi - export AFL_QEMU_PERSISTENT_ADDR=`expr 0x4${ADDR_LOWER_PART}` - export AFL_QEMU_PERSISTENT_GPR=1 - $ECHO "Info: AFL_QEMU_PERSISTENT_ADDR=$AFL_QEMU_PERSISTENT_ADDR <= $(nm test-instr | grep "T main" | awk '{print $1}')" - env|grep AFL_|sort - file test-instr - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr - unset AFL_QEMU_PERSISTENT_ADDR - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with persistent qemu_mode" - RUNTIMEP=`grep execs_done out/fuzzer_stats | awk '{print$3}'` - test -n "$RUNTIME" -a -n "$RUNTIMEP" && { - DIFF=`expr $RUNTIMEP / $RUNTIME` - test "$DIFF" -gt 1 && { # must be at least twice as fast - $ECHO "$GREEN[+] persistent qemu_mode was noticeable faster than standard qemu_mode" - } || { - $ECHO "$YELLOW[-] persistent qemu_mode was not noticeable faster than standard qemu_mode" - } - } || { - $ECHO "$YELLOW[-] we got no data on executions performed? weird!" - } - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with persistent qemu_mode" - CODE=1 - } - rm -rf in out errors - } || { - $ECHO "$YELLOW[-] not an intel or arm platform, cannot test persistent qemu_mode" - } - - test -e ../qemu_mode/unsigaction/unsigaction32.so && { - ${AFL_CC} -o test-unsigaction32 -m32 test-unsigaction.c >> errors 2>&1 && { - ./test-unsigaction32 - RETVAL_NORMAL32=$? - LD_PRELOAD=../qemu_mode/unsigaction/unsigaction32.so ./test-unsigaction32 - RETVAL_LIBUNSIGACTION32=$? - test $RETVAL_NORMAL32 = "2" -a $RETVAL_LIBUNSIGACTION32 = "0" && { - $ECHO "$GREEN[+] qemu_mode unsigaction library (32 bit) ignores signals" - } || { - test $RETVAL_NORMAL32 != "2" && { - $ECHO "$RED[!] cannot trigger signal in test program (32 bit)" - } - test $RETVAL_LIBUNSIGACTION32 != "0" && { - $ECHO "$RED[!] signal in test program (32 bit) is not ignored with unsigaction" - } - CODE=1 - } - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] cannot compile test program (32 bit) for unsigaction library" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (32 bit) because it is not present" - INCOMPLETE=1 - } - test -e ../qemu_mode/unsigaction/unsigaction64.so && { - ${AFL_CC} -o test-unsigaction64 -m64 test-unsigaction.c >> errors 2>&1 && { - ./test-unsigaction64 - RETVAL_NORMAL64=$? - LD_PRELOAD=../qemu_mode/unsigaction/unsigaction64.so ./test-unsigaction64 - RETVAL_LIBUNSIGACTION64=$? - test $RETVAL_NORMAL64 = "2" -a $RETVAL_LIBUNSIGACTION64 = "0" && { - $ECHO "$GREEN[+] qemu_mode unsigaction library (64 bit) ignores signals" - } || { - test $RETVAL_NORMAL64 != "2" && { - $ECHO "$RED[!] cannot trigger signal in test program (64 bit)" - } - test $RETVAL_LIBUNSIGACTION64 != "0" && { - $ECHO "$RED[!] signal in test program (64 bit) is not ignored with unsigaction" - } - CODE=1 - } - unset LD_PRELOAD - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] cannot compile test program (64 bit) for unsigaction library" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (64 bit) because it is not present" - INCOMPLETE=1 - } - rm -rf errors test-unsigaction32 test-unsigaction64 - } - } || { - $ECHO "$RED[!] gcc compilation of test targets failed - what is going on??" - CODE=1 - } - - rm -f test-instr test-compcov -} || { - $ECHO "$YELLOW[-] qemu_mode is not compiled, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Testing: unicorn_mode" -test -d ../unicorn_mode/unicornafl && { - test -e ../unicorn_mode/samples/simple/simple_target.bin -a -e ../unicorn_mode/samples/compcov_x64/compcov_target.bin && { - { - # We want to see python errors etc. in logs, in case something doesn't work - export AFL_DEBUG_CHILD_OUTPUT=1 - - # some python version should be available now - PYTHONS="`command -v python3` `command -v python` `command -v python2`" - EASY_INSTALL_FOUND=0 - for PYTHON in $PYTHONS ; do - - if $PYTHON -c "help('easy_install');" </dev/null | grep -q module ; then - - EASY_INSTALL_FOUND=1 - PY=$PYTHON - break - - fi - - done - if [ "0" = $EASY_INSTALL_FOUND ]; then - - echo "[-] Error: Python setup-tools not found. Run 'sudo apt-get install python-setuptools'." - PREREQ_NOTFOUND=1 - - fi - - - cd ../unicorn_mode/samples/persistent - make >>errors 2>&1 - $ECHO "$GREY[*] running afl-fuzz for unicorn_mode (persistent), this will take approx 25 seconds" - AFL_DEBUG_CHILD_OUTPUT=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode (persistent)" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode (persistent)" - CODE=1 - } - - rm -rf out errors >/dev/null - make clean >/dev/null - cd ../../../test - - # travis workaround - test "$PY" = "/opt/pyenv/shims/python" -a -x /usr/bin/python && PY=/usr/bin/python - mkdir -p in - echo 0 > in/in - $ECHO "$GREY[*] Using python binary $PY" - if ! $PY -c 'import unicornafl' 2>/dev/null ; then - $ECHO "$YELLOW[-] we cannot test unicorn_mode for python because it is not present" - INCOMPLETE=1 - else - { - $ECHO "$GREY[*] running afl-fuzz for unicorn_mode in python, this will take approx 25 seconds" - { - ../afl-fuzz -m ${MEM_LIMIT} -V25 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/simple/simple_test_harness.py @@ >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode" - CODE=1 - } - rm -f errors - - printf '\x01\x01' > in/in - # This seed is close to the first byte of the comparison. - # If CompCov works, a new tuple will appear in the map => new input in queue - $ECHO "$GREY[*] running afl-fuzz for unicorn_mode compcov, this will take approx 35 seconds" - { - export AFL_COMPCOV_LEVEL=2 - ../afl-fuzz -m ${MEM_LIMIT} -V35 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ >>errors 2>&1 - unset AFL_COMPCOV_LEVEL - } >>errors 2>&1 - test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode compcov" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode compcov" - CODE=1 - } - rm -rf in out errors - } - fi - - unset AFL_DEBUG_CHILD_OUTPUT - - } - } || { - $ECHO "$RED[!] missing sample binaries in unicorn_mode/samples/ - what is going on??" - CODE=1 - } - -} || { - $ECHO "$YELLOW[-] unicorn_mode is not compiled, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Testing: custom mutator" -test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { - # normalize path - CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../examples/custom_mutators;pwd) - test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && { - unset AFL_CC - # Compile the vulnerable program for single mutator - test -e ../afl-clang-fast && { - ../afl-clang-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 - } || { - test -e ../afl-gcc-fast && { - ../afl-gcc-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 - } || { - ../afl-gcc -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 - } - } - # Compile the vulnerable program for multiple mutators - test -e ../afl-clang-fast && { - ../afl-clang-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 - } || { - test -e ../afl-gcc-fast && { - ../afl-gcc-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 - } || { - ../afl-gcc -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 - } - } - # Compile the custom mutator - cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 - cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 - test -e test-custom-mutator -a -e ./libexamplemutator.so && { - # Create input directory - mkdir -p in - echo "00000" > in/in - - # Run afl-fuzz w/ the C mutator - $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 5 seconds" - { - AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 - } >>errors 2>&1 - - # Check results - test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here - $ECHO "$GREEN[+] afl-fuzz is working correctly with the C mutator" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with the C mutator" - CODE=1 - } - - # Clean - rm -rf out errors - - # Run afl-fuzz w/ multiple C mutators - $ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 5 seconds" - { - AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1 - } >>errors 2>&1 - - test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here - $ECHO "$GREEN[+] afl-fuzz is working correctly with multiple C mutators" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with multiple C mutators" - CODE=1 - } - - # Clean - rm -rf out errors - - # Run afl-fuzz w/ the Python mutator - $ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 5 seconds" - { - export PYTHONPATH=${CUSTOM_MUTATOR_PATH} - export AFL_PYTHON_MODULE=example - AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V5 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 - unset PYTHONPATH - unset AFL_PYTHON_MODULE - } >>errors 2>&1 - - # Check results - test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here - $ECHO "$GREEN[+] afl-fuzz is working correctly with the Python mutator" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with the Python mutator" - CODE=1 - } - - # Clean - rm -rf in out errors - rm -rf ${CUSTOM_MUTATOR_PATH}/__pycache__/ - rm -f test-multiple-mutators test-custom-mutator libexamplemutator.so libexamplemutator2.so - } || { - ls . - ls ${CUSTOM_MUTATOR_PATH} - $ECHO "$RED[!] cannot compile the test program or the custom mutator" - CODE=1 - } - - #test "$CODE" = 1 && { $ECHO "$YELLOW[!] custom mutator tests currently will not fail travis" ; CODE=0 ; } - - make -C ../examples/custom_mutators clean > /dev/null 2>&1 - rm -f test-custom-mutator - rm -f test-custom-mutators - } || { - $ECHO "$YELLOW[-] no custom mutators in $CUSTOM_MUTATOR_PATH, cannot test" - INCOMPLETE=1 - } - unset CUSTOM_MUTATOR_PATH -} || { - $ECHO "$YELLOW[-] no python support in afl-fuzz, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Execution cmocka Unit-Tests $GREY" -unset AFL_CC -make -C .. unit || CODE=1 INCOMPLETE=1 : - -$ECHO "$GREY[*] all test cases completed.$RESET" -test "$INCOMPLETE" = "0" && $ECHO "$GREEN[+] all test cases executed" -test "$INCOMPLETE" = "1" && $ECHO "$YELLOW[-] not all test cases were executed" -test "$CODE" = "0" && $ECHO "$GREEN[+] all tests were successful :-)$RESET" -test "$CODE" = "0" || $ECHO "$RED[!] failure in tests :-($RESET" -exit $CODE diff --git a/test/travis/bionic/Dockerfile b/test/travis/bionic/Dockerfile index d1b53e70..00ab96f9 100644 --- a/test/travis/bionic/Dockerfile +++ b/test/travis/bionic/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get update && apt-get -y install \ ENV AFL_NO_UI=1 ENV AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 +ENV LLVM_CONFIG=llvm-config-6.0 RUN cd / && \ git clone https://github.com/AFLplusplus/AFLplusplus && \ |