diff options
author | van Hauser <vh@thc.org> | 2020-05-12 11:04:18 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-12 11:04:18 +0200 |
commit | 1317433a51a7f7336c82c80a592835ddda9ef60f (patch) | |
tree | e623506f1d0a8771c3fc266eed0a75b626a88724 | |
parent | bdd2a412c476cbd5aea0fff67ef096305815953b (diff) | |
parent | a578d719e1f556db07ca3c7e2fe38b7668c204d8 (diff) | |
download | afl++-1317433a51a7f7336c82c80a592835ddda9ef60f.tar.gz |
Merge pull request #359 from AFLplusplus/dev
push to master
55 files changed, 1842 insertions, 469 deletions
diff --git a/.clang-format b/.clang-format index fd5d9a41..bf15cdc0 100644 --- a/.clang-format +++ b/.clang-format @@ -72,7 +72,6 @@ IncludeCategories: Priority: 3 IncludeIsMainRegex: '([-_](test|unittest))?$' IndentCaseLabels: true -IndentPPDirectives: None IndentWidth: 2 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave diff --git a/.custom-format.py b/.custom-format.py index 164815b1..6f1b0bfa 100755 --- a/.custom-format.py +++ b/.custom-format.py @@ -70,8 +70,8 @@ def custom_format(filename): out = "" for line in src.split("\n"): - if line.startswith("#"): - if line.startswith("#define"): + if line.lstrip().startswith("#"): + if line[line.find("#")+1:].lstrip().startswith("define"): in_define = True if "/*" in line and not line.strip().startswith("/*") and line.endswith("*/") and len(line) < (COLUMN_LIMIT-2): diff --git a/GNUmakefile b/GNUmakefile index 3a1ae521..d3e9275c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -52,12 +52,28 @@ endif ifneq "$(shell uname)" "Darwin" ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - CFLAGS_OPT = -march=native + CFLAGS_OPT += -march=native endif # OS X does not like _FORTIFY_SOURCE=2 CFLAGS_OPT += -D_FORTIFY_SOURCE=2 endif +ifdef STATIC + $(info Compiling static version of binaries) + # Disable python for static compilation to simplify things + PYTHON_OK=0 + PYFLAGS= + + CFLAGS_OPT += -static + LDFLAGS += -lm -lpthread -lz -lutil +endif + +ifdef PROFILING + $(info Compiling with profiling information, for analysis: gprof ./afl-fuzz gmon.out > prof.txt) + CFLAGS_OPT += -pg -DPROFILING=1 + LDFLAGS += -pg +endif + ifneq "$(shell uname -m)" "x86_64" ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386" ifneq "$(shell uname -m)" "amd64" @@ -142,23 +158,23 @@ else endif ifneq "$(filter Linux GNU%,$(shell uname))" "" - LDFLAGS += -ldl + LDFLAGS += -ldl endif ifneq "$(findstring FreeBSD, $(shell uname))" "" - CFLAGS += -pthread - LDFLAGS += -lpthread + CFLAGS += -pthread + LDFLAGS += -lpthread endif ifneq "$(findstring NetBSD, $(shell uname))" "" - CFLAGS += -pthread - LDFLAGS += -lpthread + CFLAGS += -pthread + LDFLAGS += -lpthread endif ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" - TEST_CC = afl-gcc + TEST_CC = afl-gcc else - TEST_CC = afl-clang + TEST_CC = afl-clang endif COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h @@ -184,17 +200,8 @@ ifeq "$(shell svn proplist . 2>/dev/null && echo 1 || echo 0)" "1" IN_REPO=1 endif -ifdef STATIC - $(info Compiling static version of binaries) - # Disable python for static compilation to simplify things - PYTHON_OK=0 - PYFLAGS= - COMPILE_STATIC = -static - LDFLAGS += -lm -endif - ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer -ASAN_LDFLAGS+=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer +ASAN_LDFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer ifdef ASAN_BUILD $(info Compiling ASAN version of binaries) @@ -202,12 +209,6 @@ ifdef ASAN_BUILD LDFLAGS+=$(ASAN_LDFLAGS) endif -ifdef PROFILING - $(info Compiling profiling version of binaries) - CFLAGS+=-pg - LDFLAGS+=-pg -endif - ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" SHMAT_OK=1 else diff --git a/README.md b/README.md index 690451a2..04ead78e 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ afl++ has many build options. The easiest is to build and install everything: ```shell -$ sudo apt install build-essential libtool-bin python3 automake bison libglib2.0-dev libpixman-1-dev clang python-setuptools llvm +$ sudo apt install build-essential libtool-bin python3 automake flex bison libglib2.0-dev libpixman-1-dev clang python-setuptools llvm $ make distrib $ sudo make install ``` diff --git a/docs/Changelog.md b/docs/Changelog.md index 54564a5d..0b5c11e8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,9 +16,12 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. - an old, old bug in afl that would show negative stability in rare circumstances is now hopefully fixed - llvm_mode: + - afl-clang-fast/lto now do not skip single block functions. This + behaviour can be reactivated with AFL_LLVM_SKIPSINGLEBLOCK - if LLVM 11 is installed the posix shm_open+mmap is used and a fixed address for the shared memory map is used as this increases the fuzzing speed + - InsTrim now has an LTO version! :-) That is the best and fastest mode! - fixes to LTO mode if instrumented edges > MAP_SIZE - CTX and NGRAM can now be used together - CTX and NGRAM are now also supported in CFG/INSTRIM mode @@ -38,7 +41,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. network (not fuzzing tcp/ip services but running afl-fuzz on one system and the target being on an embedded device) - added examples/afl_untracer which does a binary-only fuzzing with the - modifications done in memory + modifications done in memory (intel32/64 and aarch64 support) - added examples/afl_proxy which can be easily used to fuzz and instrument non-standard things - all: @@ -2571,3 +2574,6 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. ### Version 0.21b (2013-11-12): - Initial public release. + + - Added support for use of multiple custom mutators which can be specified using + the environment variable AFL_CUSTOM_MUTATOR_LIBRARY. diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 49ce761e..3cd874b9 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -16,6 +16,10 @@ fuzzing by using libraries that perform mutations according to a given grammar. The custom mutator is passed to `afl-fuzz` via the `AFL_CUSTOM_MUTATOR_LIBRARY` or `AFL_PYTHON_MODULE` environment variable, and must export a fuzz function. +Now afl also supports multiple custom mutators which can be specified in the same `AFL_CUSTOM_MUTATOR_LIBRARY` environment variable like this. +```bash +export AFL_CUSTOM_MUTATOR_LIBRARY="full/path/to/mutator_first.so;full/path/to/mutator_second.so" +``` Please see [APIs](#2-apis) and [Usage](#3-usage) for detail. The custom mutation stage is set to be the first non-deterministic stage (right before the havoc stage). @@ -209,12 +213,15 @@ For C/C++ mutator, the source code must be compiled as a shared object: ```bash gcc -shared -Wall -O3 example.c -o example.so ``` +Note that if you specify multiple custom mutators, the corresponding functions will +be called in the order in which they are specified. e.g first `pre_save` function of +`example_first.so` will be called and then that of `example_second.so` ### Run C/C++ ```bash -export AFL_CUSTOM_MUTATOR_LIBRARY=/full/path/to/example.so +export AFL_CUSTOM_MUTATOR_LIBRARY="/full/path/to/example_first.so;/full/path/to/example_second.so" afl-fuzz /path/to/program ``` diff --git a/docs/env_variables.md b/docs/env_variables.md index ed81c8a3..36e5a432 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -83,6 +83,10 @@ tools make fairly broad use of environmental variables: The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset of the settings discussed in section #1, with the exception of: + - Setting AFL_LLVM_SKIPSINGLEBLOCK=1 will skip instrumenting + functions with a single basic block. This is useful for most C and + some C++ targets. This works for all instrumentation modes. + - AFL_AS, since this toolchain does not directly invoke GNU as. - TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are @@ -116,6 +120,9 @@ Then there are a few specific features that are only available in llvm_mode: afl-clang-lto/afl-clang-lto++ instead of afl-clang-fast, but is only built if LLVM 11 or newer is used. + - AFL_LLVM_INSTRUMENT=CFG will use Control Flow Graph instrumentation. + (recommended) + - AFL_LLVM_LTO_AUTODICTIONARY will generate a dictionary in the target binary based on string compare and memory compare functions. afl-fuzz will automatically get these transmitted when starting to @@ -139,7 +146,13 @@ Then there are a few specific features that are only available in llvm_mode: ### INSTRIM - This feature increases the speed by ~15% without any disadvantages. + This feature increases the speed by ~15% without any disadvantages to the + classic instrumentation. + + Note that there is also an LTO version (if you have llvm 11 or higher) - + that is the best instrumentation we have. Use `afl-clang-lto` to activate. + The InsTrim LTO version additionally has all the options and features of + LTO (see above). - Setting AFL_LLVM_INSTRIM or AFL_LLVM_INSTRUMENT=CFG to activates this mode @@ -147,10 +160,6 @@ Then there are a few specific features that are only available in llvm_mode: afl-fuzz will only be able to see the path the loop took, but not how many times it was called (unless it is a complex loop). - - Setting AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK=1 will skip instrumenting - functions with a single basic block. This is useful for most C and - some C++ targets. - See llvm_mode/README.instrim.md ### NGRAM diff --git a/examples/afl_network_proxy/GNUmakefile b/examples/afl_network_proxy/GNUmakefile index cf1cbad5..25a3df82 100644 --- a/examples/afl_network_proxy/GNUmakefile +++ b/examples/afl_network_proxy/GNUmakefile @@ -6,6 +6,8 @@ PROGRAMS = afl-network-client afl-network-server HASH=\# +CFLAGS += -Wno-pointer-sign + ifdef STATIC CFLAGS += -static endif diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c index 2de91cbd..59064b2c 100644 --- a/examples/afl_network_proxy/afl-network-server.c +++ b/examples/afl_network_proxy/afl-network-server.c @@ -142,6 +142,8 @@ static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, } + return ret; + } /* Handle Ctrl-C and the like. */ @@ -665,7 +667,7 @@ int main(int argc, char **argv_orig, char **envp) { while ((in_len = recv_testcase(s, (void **)&in_data, &max_len)) > 0) { // fprintf(stderr, "received %u\n", in_len); - run_target(fsrv, use_argv, in_data, in_len, 1); + (void)run_target(fsrv, use_argv, in_data, in_len, 1); memcpy(send_buf + 4, fsrv->trace_bits, fsrv->map_size); diff --git a/examples/afl_untracer/Makefile b/examples/afl_untracer/Makefile index 5c525877..14a09b41 100644 --- a/examples/afl_untracer/Makefile +++ b/examples/afl_untracer/Makefile @@ -1,10 +1,16 @@ +ifdef DEBUG + OPT=-O0 +else + OPT=-O3 +endif + all: afl-untracer libtestinstr.so afl-untracer: afl-untracer.c - $(CC) -I../../include -g -o afl-untracer afl-untracer.c -ldl -pthread + $(CC) $(OPT) -I../../include -g -o afl-untracer afl-untracer.c -ldl libtestinstr.so: libtestinstr.c - $(CC) -fPIC -o libtestinstr.so -shared libtestinstr.c + $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c clean: rm -f afl-untracer libtestinstr.so *~ core diff --git a/examples/afl_untracer/README.md b/examples/afl_untracer/README.md index 4ff96423..05fd8776 100644 --- a/examples/afl_untracer/README.md +++ b/examples/afl_untracer/README.md @@ -1,19 +1,58 @@ -# afl-untracer +# afl-untracer - fast fuzzing of binary-only libraries + +## Introduction afl-untracer is an example skeleton file which can easily be used to fuzz a closed source library. -It requires less memory than qemu_mode however it is way +It requires less memory and is x3-5 faster than qemu_mode however it is way more course grained and does not provide interesting features like compcov or cmplog. -Read and modify afl-untracer.c then `make` and use it as the afl-fuzz target -(or even remote via afl-network-proxy). +Supported is so far Intel (i386/x86_64) and AARCH64. + +## How-to + +### Modify afl-untracer.c + +Read and modify afl-untracer.c then `make`. +To adapt afl-untracer.c to your needs, read the header of the file and then +search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. + +### Generate patches.txt file To generate the `patches.txt` file for your target library use the `ida_get_patchpoints.py` script for IDA Pro or `ghidra_get_patchpoints.java` for Ghidra. +The patches.txt file has to be pointed to by `AFL_UNTRACER_FILE`. + +To easily run the scripts without needing to run the GUI with Ghidra: +``` +$ /opt/ghidra/support/analyzeHeadless /tmp/ tmp$$ -import libtestinstr.so -postscript ./ghidra_get_patchpoints.java +$ rm -rf /tmp/tmp$$ +``` + +### Fuzzing + +Example (after modifying afl-untracer.c to your needs, compiling and creating +patches.txt): +``` +AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer +``` +(or even remote via afl-network-proxy). + +### Testing and debugging + +For testing/debugging you can try: +``` +make DEBUG=1 +AFL_UNTRACER_FILE=./patches.txt AFL_DEBUG=1 gdb ./afl-untracer +``` +and then you can easily set breakpoints to "breakpoint" and "fuzz". + +# Background + This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL) and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz). This implementation is slower because the traps are not patched out with each diff --git a/examples/afl_untracer/afl-untracer.c b/examples/afl_untracer/afl-untracer.c index d319b530..af16a6bf 100644 --- a/examples/afl_untracer/afl-untracer.c +++ b/examples/afl_untracer/afl-untracer.c @@ -156,7 +156,7 @@ void read_library_information() { liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); if (debug) fprintf( - stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, + stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_end - 1); @@ -276,6 +276,18 @@ library_list_t *find_library(char *name) { } +/* for having an easy breakpoint location after loading the shared library */ +// this seems to work for clang too. nice :) requires gcc 4.4+ +#pragma GCC push_options +#pragma GCC optimize("O0") +void breakpoint() { + + if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); + +} + +#pragma GCC pop_options + /* Error reporting to forkserver controller */ void send_forkserver_error(int error) { @@ -433,10 +445,17 @@ static void __afl_end_testcase(int status) { } +#ifdef __aarch64__ +#define SHADOW(addr) \ + ((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \ + MEMORY_MAP_DECREMENT - \ + ((uintptr_t)addr & 0x7) * 0x10000000000)) +#else #define SHADOW(addr) \ ((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \ MEMORY_MAP_DECREMENT - \ ((uintptr_t)addr & 0x3) * 0x10000000000)) +#endif void setup_trap_instrumentation() { @@ -452,8 +471,12 @@ void setup_trap_instrumentation() { FILE *patches = fopen(filename, "r"); if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename); - // Index into the coverage bitmap for the current trap instruction. - int bitmap_index = 0; + // Index into the coverage bitmap for the current trap instruction. +#ifdef __aarch64__ + uint64_t bitmap_index = 0; +#else + uint32_t bitmap_index = 0; +#endif while ((nread = getline(&line, &len, patches)) != -1) { @@ -485,9 +508,15 @@ void setup_trap_instrumentation() { PROT_READ | PROT_WRITE | PROT_EXEC) != 0) FATAL("Failed to mprotect library %s writable", line); - // Create shadow memory. + // Create shadow memory. +#ifdef __aarch64__ + for (int i = 0; i < 8; i++) { + +#else for (int i = 0; i < 4; i++) { +#endif + void *shadow_addr = SHADOW(lib_addr + i); void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0); @@ -513,12 +542,18 @@ void setup_trap_instrumentation() { if (bitmap_index >= __afl_map_size) FATAL("Too many basic blocks to instrument"); - uint32_t *shadow = SHADOW(lib_addr + offset); +#ifdef __arch64__ + uint64_t +#else + uint32_t +#endif + *shadow = SHADOW(lib_addr + offset); if (*shadow != 0) continue; // skip duplicates // Make lookup entry in shadow memory. -#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__)) +#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \ + defined(__i386__)) // this is for Intel x64 @@ -531,15 +566,29 @@ void setup_trap_instrumentation() { lib_addr, offset, lib_addr + offset, orig_byte, shadow, bitmap_index, *shadow); +#elif defined(__aarch64__) + + // this is for aarch64 + + uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset); + uint32_t orig_bytes = *patch_bytes; + *shadow = (bitmap_index << 32) | orig_bytes; + *patch_bytes = 0xd4200000; // replace instruction with debug trap + if (debug) + fprintf(stderr, + "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n", + lib_addr, offset, lib_addr + offset, orig_bytes, shadow, + bitmap_index, *shadow); + #else - // this will be ARM and AARCH64 - // for ARM we will need to identify if the code is in thumb or ARM -#error "non x86_64 not supported yet" - //__arm__: - // linux thumb: 0xde01 - // linux arm: 0xe7f001f0 - //__aarch64__: - // linux aarch64: 0xd4200000 + // this will be ARM and AARCH64 + // for ARM we will need to identify if the code is in thumb or ARM +#error "non x86_64/aarch64 not supported yet" + //__arm__: + // linux thumb: 0xde01 + // linux arm: 0xe7f001f0 + //__aarch64__: + // linux aarch64: 0xd4200000 #endif bitmap_index++; @@ -573,8 +622,15 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) { ctx->uc_mcontext->__ss.__rip -= 1; addr = ctx->uc_mcontext->__ss.__rip; #elif defined(__linux__) +#if defined(__x86_64__) || defined(__i386__) ctx->uc_mcontext.gregs[REG_RIP] -= 1; addr = ctx->uc_mcontext.gregs[REG_RIP]; +#elif defined(__aarch64__) + ctx->uc_mcontext.pc -= 4; + addr = ctx->uc_mcontext.pc; +#else +#error "Unsupported processor" +#endif #elif defined(__FreeBSD__) && defined(__LP64__) ctx->uc_mcontext.mc_rip -= 1; addr = ctx->uc_mcontext.mc_rip; @@ -642,6 +698,7 @@ int main(int argc, char *argv[]) { // END STEP 2 /* setup instrumentation, shared memory and forkserver */ + breakpoint(); read_library_information(); setup_trap_instrumentation(); __afl_map_shm(); diff --git a/examples/afl_untracer/ida_get_patchpoints.py b/examples/afl_untracer/ida_get_patchpoints.py index c7e8f899..43cf6d89 100644 --- a/examples/afl_untracer/ida_get_patchpoints.py +++ b/examples/afl_untracer/ida_get_patchpoints.py @@ -57,3 +57,6 @@ with open(home + "/Desktop/patches.txt", "w") as f: f.write('\n') print("Done, found {} patchpoints".format(len(patchpoints))) + +# For headless script running remove the comment from the next line +#ida_pro.qexit() diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 2203cfdf..9f306b7e 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -33,7 +33,9 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif +#ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 +#endif #ifdef __ANDROID__ #include "android-ashmem.h" @@ -607,6 +609,9 @@ typedef struct afl_state { u8 * ex_buf; size_t ex_size; + u32 custom_mutators_count; + + list_t custom_mutator_list; /* this is a fixed buffer of size map_size that can be used by any function if * they do not call another function */ @@ -620,6 +625,7 @@ struct custom_mutator { void * dh; u8 * pre_save_buf; size_t pre_save_size; + u8 stacked_custom_prob, stacked_custom; void *data; /* custom mutator data ptr */ @@ -808,15 +814,16 @@ void read_afl_environment(afl_state_t *, char **); /**** Prototypes ****/ /* Custom mutators */ -void setup_custom_mutator(afl_state_t *); -void destroy_custom_mutator(afl_state_t *); -u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf); +void setup_custom_mutators(afl_state_t *); +void destroy_custom_mutators(afl_state_t *); +u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf, + struct custom_mutator *mutator); /* Python */ #ifdef USE_PYTHON -void load_custom_mutator_py(afl_state_t *, char *); -void finalize_py_module(void *); +struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *); +void finalize_py_module(void *); size_t pre_save_py(void *, u8 *, size_t, u8 **); s32 init_trim_py(void *, u8 *, size_t); diff --git a/include/alloc-inl.h b/include/alloc-inl.h index e5547fe0..485446de 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -214,8 +214,8 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { } -/* In non-debug mode, we just do straightforward aliasing of the above functions - to user-visible names such as ck_alloc(). */ +/* In non-debug mode, we just do straightforward aliasing of the above + functions to user-visible names such as ck_alloc(). */ #define ck_alloc DFL_ck_alloc #define ck_alloc_nozero DFL_ck_alloc_nozero @@ -247,7 +247,6 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { /* Macro to enforce allocation limits as a last-resort defense against integer overflows. */ - #define ALLOC_CHECK_SIZE(_s) \ do { \ \ @@ -358,7 +357,6 @@ static inline void DFL_ck_free(void *mem) { if (!mem) return; CHECK_PTR(mem); - #ifdef DEBUG_BUILD /* Catch pointer issues sooner. */ @@ -542,8 +540,8 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { #ifndef DEBUG_BUILD -/* In non-debug mode, we just do straightforward aliasing of the above functions - to user-visible names such as ck_alloc(). */ +/* In non-debug mode, we just do straightforward aliasing of the above + functions to user-visible names such as ck_alloc(). */ #define ck_alloc DFL_ck_alloc #define ck_alloc_nozero DFL_ck_alloc_nozero @@ -558,8 +556,8 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { #else -/* In debugging mode, we also track allocations to detect memory leaks, and the - flow goes through one more layer of indirection. */ +/* In debugging mode, we also track allocations to detect memory leaks, and + the flow goes through one more layer of indirection. */ /* Alloc tracking data structures: */ diff --git a/include/types.h b/include/types.h index e7ff131d..72d2ba51 100644 --- a/include/types.h +++ b/include/types.h @@ -89,6 +89,7 @@ typedef int64_t s64; _a < _b ? _a : _b; \ \ }) + #define MAX(a, b) \ ({ \ \ @@ -97,6 +98,7 @@ typedef int64_t s64; _a > _b ? _a : _b; \ \ }) + #endif /* !MIN */ #define SWAP16(_x) \ @@ -154,8 +156,12 @@ typedef int64_t s64; #define MEM_BARRIER() __asm__ volatile("" ::: "memory") #if __GNUC__ < 6 +#ifndef likely #define likely(_x) (_x) +#endif +#ifndef unlikely #define unlikely(_x) (_x) +#endif #else #ifndef likely #define likely(_x) __builtin_expect(!!(_x), 1) diff --git a/libtokencap/libtokencap.so.c b/libtokencap/libtokencap.so.c index 1588239c..56b2cd4b 100644 --- a/libtokencap/libtokencap.so.c +++ b/libtokencap/libtokencap.so.c @@ -45,7 +45,9 @@ #elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ #include <sys/types.h> #include <sys/sysctl.h> +#if !defined __NetBSD__ #include <sys/user.h> +#endif #include <sys/mman.h> #endif diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile index 93886e47..ab14e545 100644 --- a/llvm_mode/GNUmakefile +++ b/llvm_mode/GNUmakefile @@ -43,8 +43,9 @@ LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\. LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 ) LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//') LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) +LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null) LLVM_STDCXX = gnu++11 -LLVM_APPLE = $(shell clang -v 2>&1 | grep -iq apple && echo 1 || echo 0) +LLVM_APPLE_XCODE = $(shell clang -v 2>&1 | grep -q Apple && echo 1 || echo 0) LLVM_LTO = 0 ifeq "$(LLVMVER)" "" @@ -74,7 +75,7 @@ ifeq "$(LLVM_LTO)" "0" $(info [+] llvm_mode detected llvm < 11, afl-clang-lto LTO will not be build.) endif -ifeq "$(LLVM_APPLE)" "1" +ifeq "$(LLVM_APPLE_XCODE)" "1" $(warning llvm_mode will not compile with Xcode clang...) endif @@ -200,7 +201,8 @@ override CFLAGS += -Wall \ -g -Wno-pointer-sign -I ../include/ \ -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ - -DLLVM_VERSION=\"$(LLVMVER)\" -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ + -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ + -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ -DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function ifdef AFL_TRACE_PC @@ -211,13 +213,15 @@ CXXFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2 override CXXFLAGS += -Wall -g -I ../include/ \ -DVERSION=\"$(VERSION)\" -Wno-variadic-macros -CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS) +CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fpic $(CXXFLAGS) CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS) # User teor2345 reports that this is required to make things work on MacOS X. ifeq "$(shell uname)" "Darwin" CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress +else + CLANG_CFL += -Wl,-znodelete endif ifeq "$(shell uname)" "OpenBSD" @@ -238,14 +242,14 @@ ifeq "$(TEST_MMAP)" "1" LDFLAGS += -Wno-deprecated-declarations endif - PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-lto-whitelist.so ../afl-llvm-lto-instrumentation.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so + PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-lto-whitelist.so ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so # If prerequisites are not given, warn, do not build anything, and exit with code 0 ifeq "$(LLVMVER)" "" NO_BUILD = 1 endif -ifneq "$(LLVM_UNSUPPORTED)$(LLVM_APPLE)" "00" +ifneq "$(LLVM_UNSUPPORTED)$(LLVM_APPLE_XCODE)" "00" NO_BUILD = 1 endif @@ -277,7 +281,7 @@ no_build: test_deps: @echo "[*] Checking for working 'llvm-config'..." - ifneq "$(LLVM_APPLE)" "1" + ifneq "$(LLVM_APPLE_XCODE)" "1" @type $(LLVM_CONFIG) >/dev/null 2>&1 || ( echo "[-] Oops, can't find 'llvm-config'. Install clang or set \$$LLVM_CONFIG or \$$PATH beforehand."; echo " (Sometimes, the binary will be named llvm-config-3.5 or something like that.)"; exit 1 ) endif @echo "[*] Checking for working '$(CC)'..." @@ -296,7 +300,7 @@ afl-common.o: ../src/afl-common.c $(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS) ../afl-clang-fast: afl-clang-fast.c afl-common.o | test_deps - $(CC) $(CFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" + $(CC) $(CFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" -Dxxx ln -sf afl-clang-fast ../afl-clang-fast++ ifneq "$(AFL_CLANG_FLTO)" "" ifeq "$(LLVM_LTO)" "1" @@ -330,6 +334,11 @@ ifeq "$(LLVM_LTO)" "1" @$(CLANG_BIN) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi endif +../afl-llvm-lto-instrim.so: afl-llvm-lto-instrim.so.cc afl-llvm-common.o +ifeq "$(LLVM_LTO)" "1" + $(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o +endif + # laf ../split-switches-pass.so: split-switches-pass.so.cc afl-llvm-common.o | test_deps $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o @@ -373,7 +382,7 @@ all_done: test_build install: all install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi + if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi if [ -f ../afl-llvm-rt-32.o ]; then set -e; install -m 755 ../afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi if [ -f ../afl-llvm-rt-64.o ]; then set -e; install -m 755 ../afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi if [ -f ../compare-transform-pass.so ]; then set -e; install -m 755 ../compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index ad046a8b..837b093a 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -103,7 +103,6 @@ struct InsTrim : public ModulePass { bool runOnModule(Module &M) override { char be_quiet = 0; - int ngram_size = 0; if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { @@ -134,15 +133,17 @@ struct InsTrim : public ModulePass { } - if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") != NULL) + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || + getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; - unsigned PrevLocSize = 0; - char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); + unsigned int PrevLocSize = 0; + char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); char *ctx_str = getenv("AFL_LLVM_CTX"); #ifdef AFL_HAVE_VECTOR_INTRINSICS + unsigned int ngram_size = 0; /* Decide previous location vector size (must be a power of two) */ VectorType *PrevLocTy; @@ -340,6 +341,7 @@ struct InsTrim : public ModulePass { if (MS.find(&BB) == MS.end()) { continue; } IRBuilder<> IRB(&*BB.getFirstInsertionPt()); +#ifdef AFL_HAVE_VECTOR_INTRINSICS if (ngram_size) { LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc); @@ -356,7 +358,10 @@ struct InsTrim : public ModulePass { ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - } else { + } else + +#endif + { IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), AFLPrevLoc); @@ -394,7 +399,7 @@ struct InsTrim : public ModulePass { if ((callInst = dyn_cast<CallInst>(&IN))) { Function *Callee = callInst->getCalledFunction(); - if (!Callee || Callee->size() < 2) + if (!Callee || Callee->size() < function_minimum_size) continue; else { diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index d8e4766d..fa5b8665 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -6,6 +6,8 @@ This version requires a current llvm 11 compiled from the github master. 1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better coverage than anything else that is out there in the AFL world + 1a. Set AFL_LLVM_INSTRUMENT=CFG if you want the InsTrimLTO version + (recommended) 2. You can use it together with llvm_mode: laf-intel and whitelisting features and can be combined with cmplog/Redqueen @@ -14,6 +16,11 @@ This version requires a current llvm 11 compiled from the github master. 4. AUTODICTIONARY feature! see below +5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib` also + note that if that target uses _init functions or early constructors then + also set `AFL_LLVM_MAP_DYNAMIC=1` as your target will crash otherwise + + ## Introduction and problem description A big issue with how afl/afl++ works is that the basic block IDs that are @@ -41,7 +48,7 @@ and many dead ends until we got to this: -fsanitize=coverage edge coverage mode :) The result: - * 10-20% speed gain compared to llvm_mode + * 10-25% speed gain compared to llvm_mode * guaranteed non-colliding edge coverage :-) * The compile time especially for libraries can be longer @@ -80,11 +87,13 @@ Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc. Also whitelisting (AFL_LLVM_WHITELIST -> [README.whitelist.md](README.whitelist.md)) and laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. -Instrim does not - but we can not really use it anyway for our approach. +InsTrim (control flow graph instrumentation) is supported and recommended! + (set `AFL_LLVM_INSTRUMENT=CFG`) Example: ``` -CC=afl-clang-lto CXX=afl-clang-lto++ ./configure +CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure +export AFL_LLVM_INSTRUMENT=CFG make ``` @@ -130,15 +139,27 @@ Other targets ignore environment variables and need the parameters set via afl-clang-lto is still work in progress. Known issues: - * Anything that llvm11 cannot compile, afl-clang-lto can not compile either - obviously + * Anything that llvm 11 cannot compile, afl-clang-lto can not compile either - obviously * Anything that does not compile with LTO, afl-clang-lto can not compile either - obviously Hence if building a target with afl-clang-lto fails try to build it with llvm11 and LTO enabled (`CC=clang-11` `CXX=clang++-11` `CFLAGS=-flto=full` and `CXXFLAGS=-flto=full`). + +An example that does not build with llvm 11 and LTO is ffmpeg. + If this succeeeds then there is an issue with afl-clang-lto. Please report at [https://github.com/AFLplusplus/AFLplusplus/issues/226](https://github.com/AFLplusplus/AFLplusplus/issues/226) +### Target crashes immediately + +If the target is using early constructors (priority values smaller than 6) +or have their own _init/.init functions and these are instrumented then the +target will likely crash when started. This can be avoided by compiling with +`AFL_LLVM_MAP_DYNAMIC=1` . + +This can e.g. happen with OpenSSL. + ## Upcoming Work 1. Currently the LTO whitelist feature does not allow to instrument main, diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 2d1b427c..07754d1d 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -43,13 +43,13 @@ static u8 * obj_path; /* Path to runtime libraries */ static u8 **cc_params; /* Parameters passed to the real CC */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */ static u8 llvm_fullpath[PATH_MAX]; -static u8 instrument_mode, instrument_opt_mode, ngram_size; -static u8 * lto_flag = AFL_CLANG_FLTO; -static u8 * march_opt = CFLAGS_OPT; -static u8 debug; -static u8 cwd[4096]; -static u8 cmplog_mode; -u8 use_stdin = 0; /* dummy */ +static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode, cpp_mode; +static u8 *lto_flag = AFL_CLANG_FLTO; +static u8 *march_opt = CFLAGS_OPT; +static u8 debug; +static u8 cwd[4096]; +static u8 cmplog_mode; +u8 use_stdin = 0; /* dummy */ enum { @@ -170,7 +170,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { else ++name; - if (instrument_mode == INSTRUMENT_LTO) + if (lto_mode) if (lto_flag[0] != '-') FATAL( "Using afl-clang-lto is not possible because Makefile magic did not " @@ -184,6 +184,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { else sprintf(llvm_fullpath, CLANGPP_BIN); cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)llvm_fullpath; + cpp_mode = 1; } else if (!strcmp(name, "afl-clang-fast") || @@ -205,12 +206,18 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - /* There are three ways to compile with afl-clang-fast. In the traditional + if (lto_mode && cpp_mode) + cc_params[cc_par_cnt++] = "-lc++"; // needed by fuzzbench, early + + /* There are several ways to compile with afl-clang-fast. In the traditional mode, we use afl-llvm-pass.so, then there is libLLVMInsTrim.so which is - much faster but has less coverage. Finally there is the experimental - 'trace-pc-guard' mode, we use native LLVM instrumentation callbacks - instead. For trace-pc-guard see: + faster and creates less map pollution. + Then there is the 'trace-pc-guard' mode, we use native LLVM + instrumentation callbacks instead. For trace-pc-guard see: http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards + The best instrumentatation is with the LTO modes, the classic and + InsTrimLTO, the latter is faster. The LTO modes are activated by using + afl-clang-lto(++) */ // laf @@ -227,8 +234,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { - if (!be_quiet && getenv("AFL_LLVM_LTO_AUTODICTIONARY") && - instrument_mode != INSTRUMENT_LTO) + if (!be_quiet && getenv("AFL_LLVM_LTO_AUTODICTIONARY") && lto_mode) WARNF( "using AFL_LLVM_LAF_TRANSFORM_COMPARES together with " "AFL_LLVM_LTO_AUTODICTIONARY makes no sense. Use only " @@ -281,7 +287,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (instrument_mode == INSTRUMENT_LTO) { + if (lto_mode) { if (getenv("AFL_LLVM_WHITELIST") != NULL) { @@ -295,8 +301,12 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD); cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); + if (instrument_mode == INSTRUMENT_CFG) + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so", obj_path); + else + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); cc_params[cc_par_cnt++] = lto_flag; } else { @@ -323,6 +333,22 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-Qunused-arguments"; + // in case LLVM is installed not via a package manager or "make install" + // e.g. compiled download or compiled from github then it's ./lib directory + // might not be in the search path. Add it if so. + u8 *libdir = strdup(LLVM_LIBDIR); + if (cpp_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && + strncmp(libdir, "/lib", 4)) { + + cc_params[cc_par_cnt++] = "-rpath"; + cc_params[cc_par_cnt++] = libdir; + + } else { + + free(libdir); + + } + /* Detect stray -v calls from ./configure scripts. */ while (--argc) { @@ -391,7 +417,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("AFL_USE_CFISAN")) { - if (instrument_mode != INSTRUMENT_LTO) { + if (!lto_mode) { uint32_t i = 0, found = 0; while (envp[i] != NULL && !found) @@ -417,9 +443,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || getenv("LAF_TRANSFORM_COMPARES") || - (instrument_mode == INSTRUMENT_LTO && - (getenv("AFL_LLVM_LTO_AUTODICTIONARY") || - getenv("AFL_LLVM_AUTODICTIONARY")))) { + (lto_mode && (getenv("AFL_LLVM_LTO_AUTODICTIONARY") || + getenv("AFL_LLVM_AUTODICTIONARY")))) { cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; @@ -500,7 +525,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { case 0: cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); - if (instrument_mode == INSTRUMENT_LTO) + if (lto_mode) cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); break; @@ -509,7 +534,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) FATAL("-m32 is not supported by your compiler"); - if (instrument_mode == INSTRUMENT_LTO) { + if (lto_mode) { cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); @@ -524,7 +549,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) FATAL("-m64 is not supported by your compiler"); - if (instrument_mode == INSTRUMENT_LTO) { + if (lto_mode) { cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); @@ -548,7 +573,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { int main(int argc, char **argv, char **envp) { int i; - char *callname = "afl-clang-fast", *ptr; + char *callname = "afl-clang-fast", *ptr = NULL; if (getenv("AFL_DEBUG")) { @@ -630,7 +655,13 @@ int main(int argc, char **argv, char **envp) { if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 || strncasecmp(ptr, "instrim", strlen("instrim")) == 0) { - if (!instrument_mode || instrument_mode == INSTRUMENT_CFG) + if (instrument_mode == INSTRUMENT_LTO) { + + instrument_mode = INSTRUMENT_CFG; + lto_mode = 1; + + } else if (!instrument_mode || instrument_mode == INSTRUMENT_CFG) + instrument_mode = INSTRUMENT_CFG; else FATAL("main instrumentation mode already set with %s", @@ -640,9 +671,10 @@ int main(int argc, char **argv, char **envp) { if (strncasecmp(ptr, "lto", strlen("lto")) == 0) { + lto_mode = 1; if (!instrument_mode || instrument_mode == INSTRUMENT_LTO) instrument_mode = INSTRUMENT_LTO; - else + else if (instrument_mode != INSTRUMENT_CFG) FATAL("main instrumentation mode already set with %s", instrument_mode_string[instrument_mode]); @@ -684,37 +716,54 @@ int main(int argc, char **argv, char **envp) { } - if (!instrument_opt_mode) - ptr = instrument_mode_string[instrument_mode]; - else if (instrument_opt_mode == INSTRUMENT_OPT_CTX) - ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]); - else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM) - ptr = alloc_printf("%s + NGRAM-%u", instrument_mode_string[instrument_mode], - ngram_size); - else - ptr = alloc_printf("%s + CTX + NGRAM-%u", - instrument_mode_string[instrument_mode], ngram_size); - if (strstr(argv[0], "afl-clang-lto") != NULL) { - if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO) { + if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || + instrument_mode == INSTRUMENT_CFG) { + lto_mode = 1; callname = "afl-clang-lto"; - instrument_mode = INSTRUMENT_LTO; - ptr = instrument_mode_string[instrument_mode]; + if (!instrument_mode) { + + instrument_mode = INSTRUMENT_LTO; + ptr = instrument_mode_string[instrument_mode]; + + } } else { if (!be_quiet) WARNF("afl-clang-lto called with mode %s, using that mode instead", - ptr); + instrument_mode_string[instrument_mode]); } } + if (instrument_opt_mode && lto_mode) + FATAL( + "CTX and NGRAM can not be used in LTO mode (and would make LTO " + "useless)"); + + if (!instrument_opt_mode) { + + if (lto_mode && instrument_mode == INSTRUMENT_CFG) + ptr = alloc_printf("InsTrimLTO"); + else + ptr = instrument_mode_string[instrument_mode]; + + } else if (instrument_opt_mode == INSTRUMENT_OPT_CTX) + + ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]); + else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM) + ptr = alloc_printf("%s + NGRAM-%u", instrument_mode_string[instrument_mode], + ngram_size); + else + ptr = alloc_printf("%s + CTX + NGRAM-%u", + instrument_mode_string[instrument_mode], ngram_size); + #ifndef AFL_CLANG_FLTO - if (instrument_mode == INSTRUMENT_LTO) + if (lto_mode) FATAL( "instrumentation mode LTO specified but LLVM support not available " "(requires LLVM 11 or higher)"); @@ -733,7 +782,7 @@ int main(int argc, char **argv, char **envp) { if (argc < 2 || strcmp(argv[1], "-h") == 0) { - if (instrument_mode != INSTRUMENT_LTO) + if (!lto_mode) printf("afl-clang-fast" VERSION " by <lszekeres@google.com> in %s mode\n", ptr); else @@ -831,7 +880,7 @@ int main(int argc, char **argv, char **envp) { getenv("AFL_DEBUG") != NULL) { - if (instrument_mode != INSTRUMENT_LTO) + if (!lto_mode) SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com> in %s mode\n", @@ -846,7 +895,7 @@ int main(int argc, char **argv, char **envp) { } u8 *ptr2; - if (!be_quiet && instrument_mode != INSTRUMENT_LTO && + if (!be_quiet && !lto_mode && ((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) { u32 map_size = atoi(ptr2); diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 04dd9475..db604e14 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -43,10 +43,29 @@ char *getBBName(const llvm::BasicBlock *BB) { /* Note: this blacklist check is also called in isInWhitelist() */ bool isBlacklisted(const llvm::Function *F) { + // Starting from "LLVMFuzzer" these are functions used in libfuzzer based + // fuzzing campaign installations, e.g. oss-fuzz + static const char *Blacklist[] = { - "asan.", "llvm.", "sancov.", "__ubsan_handle_", "ign.", "__afl_", - "_fini", "__libc_csu", "__asan", "__msan", "msan." + "asan.", + "llvm.", + "sancov.", + "__ubsan_handle_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "msan.", + "LLVMFuzzer", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" }; @@ -201,3 +220,20 @@ bool isInWhitelist(llvm::Function *F) { } +// Calculate the number of average collisions that would occur if all +// location IDs would be assigned randomly (like normal afl/afl++). +// This uses the "balls in bins" algorithm. +unsigned long long int calculateCollisions(uint32_t edges) { + + double bins = MAP_SIZE; + double balls = edges; + double step1 = 1 - (1 / bins); + double step2 = pow(step1, balls); + double step3 = bins * step2; + double step4 = round(step3); + unsigned long long int empty = step4; + unsigned long long int collisions = edges - (MAP_SIZE - empty); + return collisions; + +} + diff --git a/llvm_mode/afl-llvm-common.h b/llvm_mode/afl-llvm-common.h index 5b5e08d0..cf14d2e1 100644 --- a/llvm_mode/afl-llvm-common.h +++ b/llvm_mode/afl-llvm-common.h @@ -32,10 +32,11 @@ typedef long double max_align_t; #include "llvm/Support/CFG.h" #endif -char *getBBName(const llvm::BasicBlock *BB); -bool isBlacklisted(const llvm::Function *F); -void initWhitelist(); -bool isInWhitelist(llvm::Function *F); +char * getBBName(const llvm::BasicBlock *BB); +bool isBlacklisted(const llvm::Function *F); +void initWhitelist(); +bool isInWhitelist(llvm::Function *F); +unsigned long long int calculateCollisions(uint32_t edges); #endif diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc new file mode 100644 index 00000000..a7d9b756 --- /dev/null +++ b/llvm_mode/afl-llvm-lto-instrim.so.cc @@ -0,0 +1,935 @@ +/* + american fuzzy lop++ - LLVM-mode instrumentation pass + --------------------------------------------------- + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This library is plugged into LLVM when invoking clang through afl-clang-fast + or afl-clang-lto with AFL_LLVM_INSTRUMENT=CFG or =INSTRIM + + */ + +#define AFL_LLVM_PASS + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <sys/time.h> + +#include <unordered_set> +#include <list> +#include <string> +#include <fstream> +#include <set> + +#include "llvm/Config/llvm-config.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#include "llvm/Analysis/ValueTracking.h" + +#include "MarkNodes.h" +#include "afl-llvm-common.h" + +#include "config.h" +#include "debug.h" + +using namespace llvm; + +static cl::opt<bool> MarkSetOpt("markset", cl::desc("MarkSet"), + cl::init(false)); +static cl::opt<bool> LoopHeadOpt("loophead", cl::desc("LoopHead"), + cl::init(false)); + +namespace { + +struct InsTrimLTO : public ModulePass { + + protected: + uint32_t function_minimum_size = 1; + char * skip_nozero = NULL; + int afl_global_id = 1, debug = 0, autodictionary = 0; + uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0; + uint64_t map_addr = 0x10000; + + public: + static char ID; + + InsTrimLTO() : ModulePass(ID) { + + char *ptr; + + if (getenv("AFL_DEBUG")) debug = 1; + if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) + if ((afl_global_id = atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE) + FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n", + ptr, MAP_SIZE - 1); + + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + + ModulePass::getAnalysisUsage(AU); + AU.addRequired<DominatorTreeWrapperPass>(); + AU.addRequired<LoopInfoWrapperPass>(); + + } + + StringRef getPassName() const override { + + return "InstTrim LTO Instrumentation"; + + } + + bool runOnModule(Module &M) override { + + char be_quiet = 0; + char *ptr; + + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + + SAYF(cCYA "InsTrimLTO" VERSION cRST + " by csienslab and Marc \"vanHauser\" Heuse\n"); + + } else + + be_quiet = 1; + + /* Process environment variables */ + + if (getenv("AFL_LLVM_AUTODICTIONARY") || + getenv("AFL_LLVM_LTO_AUTODICTIONARY")) + autodictionary = 1; + + if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; + + if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { + + uint64_t val; + if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) { + + map_addr = 0; + + } else if (map_addr == 0) { + + FATAL( + "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used " + "together"); + + } else if (strncmp(ptr, "0x", 2) != 0) { + + map_addr = 0x10000; // the default + + } else { + + val = strtoull(ptr, NULL, 16); + if (val < 0x100 || val > 0xffffffff00000000) { + + FATAL( + "AFL_LLVM_MAP_ADDR must be a value between 0x100 and " + "0xffffffff00000000"); + + } + + map_addr = val; + + } + + } + + if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); } + + if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || + getenv("LOOPHEAD") != NULL) { + + LoopHeadOpt = true; + + } + + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || + getenv("AFL_LLVM_SKIPSINGLEBLOCK")) + function_minimum_size = 2; + + // this is our default + MarkSetOpt = true; + + /* Initialize LLVM instrumentation */ + + LLVMContext & C = M.getContext(); + std::vector<std::string> dictionary; + std::vector<CallInst *> calls; + DenseMap<Value *, std::string *> valueMap; + + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); + IntegerType *Int64Ty = IntegerType::getInt64Ty(C); + + ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); + ConstantInt *One = ConstantInt::get(Int8Ty, 1); + + /* Get/set globals for the SHM region. */ + + GlobalVariable *AFLMapPtr = NULL; + Value * MapPtrFixed = NULL; + + if (!map_addr) { + + AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + + } else { + + ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); + MapPtrFixed = + ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty)); + + } + + if (autodictionary) { + + /* Some implementation notes. + * + * We try to handle 3 cases: + * - memcmp("foo", arg, 3) <- literal string + * - static char globalvar[] = "foo"; + * memcmp(globalvar, arg, 3) <- global variable + * - char localvar[] = "foo"; + * memcmp(locallvar, arg, 3) <- local variable + * + * The local variable case is the hardest. We can only detect that + * case if there is no reassignment or change in the variable. + * And it might not work across llvm version. + * What we do is hooking the initializer function for local variables + * (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned + * variable. And if that variable is then used in a compare function + * we use that noted string. + * This seems not to work for tokens that have a size <= 4 :-( + * + * - if the compared length is smaller than the string length we + * save the full string. This is likely better for fuzzing but + * might be wrong in a few cases depending on optimizers + * + * - not using StringRef because there is a bug in the llvm 11 + * checkout I am using which sometimes points to wrong strings + * + * Over and out. Took me a full day. damn. mh/vh + */ + + for (Function &F : M) { + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast<CallInst>(&IN))) { + + bool isStrcmp = true; + bool isMemcmp = true; + bool isStrncmp = true; + bool isStrcasecmp = true; + bool isStrncasecmp = true; + bool isIntMemcpy = true; + bool addedNull = false; + uint8_t optLen = 0; + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + std::string FuncName = Callee->getName().str(); + isStrcmp &= !FuncName.compare("strcmp"); + isMemcmp &= !FuncName.compare("memcmp"); + isStrncmp &= !FuncName.compare("strncmp"); + isStrcasecmp &= !FuncName.compare("strcasecmp"); + isStrncasecmp &= !FuncName.compare("strncasecmp"); + isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp + * function prototype */ + FunctionType *FT = Callee->getFunctionType(); + + isStrcmp &= FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()); + isStrcasecmp &= FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()); + isMemcmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy() && + FT->getParamType(2)->isIntegerTy(); + isStrncmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* is a str{n,}{case,}cmp/memcmp, check if we have + * str{case,}cmp(x, "const") or str{case,}cmp("const", x) + * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, + * ..) memcmp(x, "const", ..) or memcmp("const", x, ..) */ + Value *Str1P = callInst->getArgOperand(0), + *Str2P = callInst->getArgOperand(1); + std::string Str1, Str2; + StringRef TmpStr; + bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); + if (TmpStr.empty()) + HasStr1 = false; + else + Str1 = TmpStr.str(); + bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); + if (TmpStr.empty()) + HasStr2 = false; + else + Str2 = TmpStr.str(); + + if (debug) + fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", + FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), + Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, + Str2P->getName().str().c_str(), Str2.c_str(), + HasStr2 == true ? "true" : "false"); + + // we handle the 2nd parameter first because of llvm memcpy + if (!HasStr2) { + + auto *Ptr = dyn_cast<ConstantExpr>(Str2P); + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = + dyn_cast<GlobalVariable>(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast<ConstantDataArray>( + Var->getInitializer())) { + + HasStr2 = true; + Str2 = Array->getAsString().str(); + + } + + } + + } + + } + + } + + // for the internal memcpy routine we only care for the second + // parameter and are not reporting anything. + if (isIntMemcpy == true) { + + if (HasStr2 == true) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast<ConstantInt>(op2); + if (ilen) { + + uint64_t literalLength = Str2.size(); + uint64_t optLength = ilen->getZExtValue(); + if (literalLength + 1 == optLength) { + + Str2.append("\0", 1); // add null byte + addedNull = true; + + } + + } + + valueMap[Str1P] = new std::string(Str2); + + if (debug) + fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P); + continue; + + } + + continue; + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr2) { + + std::string *strng = valueMap[Str2P]; + if (strng && !strng->empty()) { + + Str2 = *strng; + HasStr2 = true; + if (debug) + fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), + Str2P); + + } + + } + + if (!HasStr1) { + + auto Ptr = dyn_cast<ConstantExpr>(Str1P); + + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = + dyn_cast<GlobalVariable>(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast<ConstantDataArray>( + Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString().str(); + + } + + } + + } + + } + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr1) { + + std::string *strng = valueMap[Str1P]; + if (strng && !strng->empty()) { + + Str1 = *strng; + HasStr1 = true; + if (debug) + fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), + Str1P); + + } + + } + + /* handle cases of one string is const, one string is variable */ + if (!(HasStr1 ^ HasStr2)) continue; + + std::string thestring; + + if (HasStr1) + thestring = Str1; + else + thestring = Str2; + + optLen = thestring.length(); + + if (isMemcmp || isStrncmp || isStrncasecmp) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast<ConstantInt>(op2); + if (ilen) { + + uint64_t literalLength = optLen; + optLen = ilen->getZExtValue(); + if (literalLength + 1 == optLen) { // add null byte + thestring.append("\0", 1); + addedNull = true; + + } + + } + + } + + // add null byte if this is a string compare function and a null + // was not already added + if (addedNull == false && !isMemcmp) { + + thestring.append("\0", 1); // add null byte + optLen++; + + } + + if (!be_quiet) { + + std::string outstring; + fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen, + (unsigned int)thestring.length()); + for (uint8_t i = 0; i < thestring.length(); i++) { + + uint8_t c = thestring[i]; + if (c <= 32 || c >= 127) + fprintf(stderr, "\\x%02x", c); + else + fprintf(stderr, "%c", c); + + } + + fprintf(stderr, "\"\n"); + + } + + // we take the longer string, even if the compare was to a + // shorter part. Note that depending on the optimizer of the + // compiler this can be wrong, but it is more likely that this + // is helping the fuzzer + if (optLen != thestring.length()) optLen = thestring.length(); + if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA; + if (optLen < MIN_AUTO_EXTRA) // too short? skip + continue; + + dictionary.push_back(thestring.substr(0, optLen)); + + } + + } + + } + + } + + } + + /* InsTrim instrumentation starts here */ + + u64 total_rs = 0; + u64 total_hs = 0; + + for (Function &F : M) { + + if (debug) { + + uint32_t bb_cnt = 0; + + for (auto &BB : F) + if (BB.size() > 0) ++bb_cnt; + SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n", + F.getName().str().c_str(), F.size(), bb_cnt); + + } + + // if the function below our minimum size skip it (1 or 2) + if (F.size() < function_minimum_size) continue; + if (isBlacklisted(&F)) continue; + + std::unordered_set<BasicBlock *> MS; + if (!MarkSetOpt) { + + for (auto &BB : F) { + + MS.insert(&BB); + + } + + total_rs += F.size(); + + } else { + + auto Result = markNodes(&F); + auto RS = Result.first; + auto HS = Result.second; + + MS.insert(RS.begin(), RS.end()); + if (!LoopHeadOpt) { + + MS.insert(HS.begin(), HS.end()); + total_rs += MS.size(); + + } else { + + DenseSet<std::pair<BasicBlock *, BasicBlock *>> EdgeSet; + DominatorTreeWrapperPass * DTWP = + &getAnalysis<DominatorTreeWrapperPass>(F); + auto DT = &DTWP->getDomTree(); + + total_rs += RS.size(); + total_hs += HS.size(); + + for (BasicBlock *BB : HS) { + + bool Inserted = false; + for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) { + + auto Edge = BasicBlockEdge(*BI, BB); + if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) { + + EdgeSet.insert({*BI, BB}); + Inserted = true; + break; + + } + + } + + if (!Inserted) { + + MS.insert(BB); + total_rs += 1; + total_hs -= 1; + + } + + } + + for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) { + + auto PredBB = I->first; + auto SuccBB = I->second; + auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT, + nullptr, nullptr, false); + MS.insert(NewBB); + + } + + } + + } + + for (BasicBlock &BB : F) { + + auto PI = pred_begin(&BB); + auto PE = pred_end(&BB); + IRBuilder<> IRB(&*BB.getFirstInsertionPt()); + Value * L = NULL; + + if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; } + + if (PI == PE) { + + L = ConstantInt::get(Int32Ty, afl_global_id++); + + } else { + + auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin()); + DenseMap<BasicBlock *, unsigned> PredMap; + for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) { + + BasicBlock *PBB = *PI; + auto It = PredMap.insert({PBB, afl_global_id++}); + unsigned Label = It.first->second; + PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB); + + } + + L = PN; + + } + + /* Load SHM pointer */ + Value *MapPtrIdx; + + if (map_addr) { + + MapPtrIdx = IRB.CreateGEP(MapPtrFixed, L); + + } else { + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + MapPtr->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + MapPtrIdx = IRB.CreateGEP(MapPtr, L); + + } + + /* Update bitmap */ + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + Value *Incr = IRB.CreateAdd(Counter, One); + + if (skip_nozero) { + + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx) + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + // done :) + + inst_blocks++; + + } + + } + + // save highest location ID to global variable + // do this after each function to fail faster + if (!be_quiet && afl_global_id > MAP_SIZE && + afl_global_id > FS_OPT_MAX_MAPSIZE) { + + uint32_t pow2map = 1, map = afl_global_id; + while ((map = map >> 1)) + pow2map++; + WARNF( + "We have %u blocks to instrument but the map size is only %u. Either " + "edit config.h and set MAP_SIZE_POW2 from %u to %u, then recompile " + "afl-fuzz and llvm_mode and then make this target - or set " + "AFL_MAP_SIZE with at least size %u when running afl-fuzz with this " + "target.", + afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id); + + } + + if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) { + + // yes we could create our own function, insert it into ctors ... + // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o + + Function *f = M.getFunction("__afl_auto_init_globals"); + + if (!f) { + + fprintf(stderr, + "Error: init function could not be found (this should not " + "happen)\n"); + exit(-1); + + } + + BasicBlock *bb = &f->getEntryBlock(); + if (!bb) { + + fprintf(stderr, + "Error: init function does not have an EntryBlock (this should " + "not happen)\n"); + exit(-1); + + } + + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + if (map_addr) { + + GlobalVariable *AFLMapAddrFixed = + new GlobalVariable(M, Int64Ty, true, GlobalValue::ExternalLinkage, + 0, "__afl_map_addr"); + ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); + StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed); + StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { + + uint32_t write_loc = afl_global_id; + + if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3); + + GlobalVariable *AFLFinalLoc = + new GlobalVariable(M, Int32Ty, true, GlobalValue::ExternalLinkage, + 0, "__afl_final_loc"); + ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc); + StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); + StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + if (dictionary.size()) { + + size_t memlen = 0, count = 0, offset = 0; + char * ptr; + + for (auto token : dictionary) { + + memlen += token.length(); + count++; + + } + + if (!be_quiet) + printf("AUTODICTIONARY: %lu string%s found\n", count, + count == 1 ? "" : "s"); + + if (count) { + + if ((ptr = (char *)malloc(memlen + count)) == NULL) { + + fprintf(stderr, "Error: malloc for %lu bytes failed!\n", + memlen + count); + exit(-1); + + } + + count = 0; + + for (auto token : dictionary) { + + if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) { + + ptr[offset++] = (uint8_t)token.length(); + memcpy(ptr + offset, token.c_str(), token.length()); + offset += token.length(); + count++; + + } + + } + + GlobalVariable *AFLDictionaryLen = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, + "__afl_dictionary_len"); + ConstantInt *const_len = ConstantInt::get(Int32Ty, offset); + StoreInst * StoreDictLen = + IRB.CreateStore(const_len, AFLDictionaryLen); + StoreDictLen->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset); + GlobalVariable *AFLInternalDictionary = new GlobalVariable( + M, ArrayTy, true, GlobalValue::ExternalLinkage, + ConstantDataArray::get( + C, *(new ArrayRef<char>((char *)ptr, offset))), + "__afl_internal_dictionary"); + AFLInternalDictionary->setInitializer(ConstantDataArray::get( + C, *(new ArrayRef<char>((char *)ptr, offset)))); + AFLInternalDictionary->setConstant(true); + + GlobalVariable *AFLDictionary = new GlobalVariable( + M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_dictionary"); + + Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero); + Value *AFLDictPtr = + IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0)); + StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary); + StoreDict->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + } + + // count basic blocks for comparison with classic instrumentation + + u32 edges = 0; + for (auto &F : M) { + + if (F.size() < function_minimum_size) continue; + + for (auto &BB : F) { + + bool would_instrument = false; + + for (BasicBlock *Pred : predecessors(&BB)) { + + int count = 0; + for (BasicBlock *Succ : successors(Pred)) + if (Succ != NULL) count++; + + if (count > 1) return true; + + } + + if (would_instrument == true) edges++; + + } + + } + + /* Say something nice. */ + + if (!be_quiet) { + + if (!inst_blocks) + WARNF("No instrumentation targets found."); + else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations (%llu, %llu) with no collisions (on " + "average %llu collisions would be in afl-gcc/afl-clang-fast for %u " + "edges) (%s mode).", + inst_blocks, total_rs, total_hs, calculateCollisions(edges), edges, + modeline); + + } + + } + + return true; + + } + +}; // end of struct InsTrim + +} // end of anonymous namespace + +char InsTrimLTO::ID = 0; + +static void registerInsTrimLTO(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new InsTrimLTO()); + +} + +static RegisterPass<InsTrimLTO> X("afl-lto-instrim", + "afl++ InsTrim LTO instrumentation pass", + false, false); + +static RegisterStandardPasses RegisterInsTrimLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerInsTrimLTO); + diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 838e45af..f44b336e 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -1,14 +1,9 @@ /* - american fuzzy lop++ - LLVM-mode instrumentation pass - --------------------------------------------------- + american fuzzy lop++ - LLVM LTO instrumentation pass + ---------------------------------------------------- - Written by Laszlo Szekeres <lszekeres@google.com> and - Michal Zalewski + Written by Marc Heuse <mh@mh-sec.de> - LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted - from afl-as.c are Michal's fault. - - Copyright 2015, 2016 Google Inc. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,9 +12,7 @@ http://www.apache.org/licenses/LICENSE-2.0 - This library is plugged into LLVM when invoking clang through afl-clang-fast. - It tells the compiler to add code roughly equivalent to the bits discussed - in ../afl-as.h. + This library is plugged into LLVM when invoking clang through afl-clang-lto. */ @@ -32,11 +25,12 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <sys/time.h> #include <list> #include <string> #include <fstream> -#include <sys/time.h> +#include <set> #include "llvm/Config/llvm-config.h" #include "llvm/ADT/Statistic.h" @@ -56,7 +50,6 @@ #include "llvm/Analysis/ValueTracking.h" #include "llvm/Pass.h" -#include <set> #include "afl-llvm-common.h" using namespace llvm; @@ -90,27 +83,11 @@ class AFLLTOPass : public ModulePass { } - // Calculate the number of average collisions that would occur if all - // location IDs would be assigned randomly (like normal afl/afl++). - // This uses the "balls in bins" algorithm. - unsigned long long int calculateCollisions(uint32_t edges) { - - double bins = MAP_SIZE; - double balls = edges; - double step1 = 1 - (1 / bins); - double step2 = pow(step1, balls); - double step3 = bins * step2; - double step4 = round(step3); - unsigned long long int empty = step4; - unsigned long long int collisions = edges - (MAP_SIZE - empty); - return collisions; - - } - bool runOnModule(Module &M) override; protected: int afl_global_id = 1, debug = 0, autodictionary = 0; + uint32_t function_minimum_size = 1; uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0; uint64_t map_addr = 0x10000; char * skip_nozero = NULL; @@ -131,8 +108,6 @@ bool AFLLTOPass::runOnModule(Module &M) { IntegerType *Int32Ty = IntegerType::getInt32Ty(C); IntegerType *Int64Ty = IntegerType::getInt64Ty(C); - if (getenv("AFL_DEBUG")) debug = 1; - /* Show a banner */ if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { @@ -150,6 +125,10 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || + getenv("AFL_LLVM_SKIPSINGLEBLOCK")) + function_minimum_size = 2; + if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { uint64_t val; @@ -185,12 +164,10 @@ bool AFLLTOPass::runOnModule(Module &M) { if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); } - /* Get globals for the SHM region and the previous location. Note that - __afl_prev_loc is thread-local. */ + /* Get/set the globals for the SHM region. */ GlobalVariable *AFLMapPtr = NULL; - ; - Value *MapPtrFixed = NULL; + Value * MapPtrFixed = NULL; if (!map_addr) { @@ -217,7 +194,7 @@ bool AFLLTOPass::runOnModule(Module &M) { // fprintf(stderr, "DEBUG: Function %s\n", F.getName().str().c_str()); - if (F.size() < 2) continue; + if (F.size() < function_minimum_size) continue; if (isBlacklisted(&F)) continue; std::vector<BasicBlock *> InsBlocks; @@ -354,11 +331,15 @@ bool AFLLTOPass::runOnModule(Module &M) { if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) { - if (auto *Array = - dyn_cast<ConstantDataArray>(Var->getInitializer())) { + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast<ConstantDataArray>( + Var->getInitializer())) { - HasStr2 = true; - Str2 = Array->getAsString().str(); + HasStr2 = true; + Str2 = Array->getAsString().str(); + + } } @@ -426,11 +407,15 @@ bool AFLLTOPass::runOnModule(Module &M) { if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) { - if (auto *Array = - dyn_cast<ConstantDataArray>(Var->getInitializer())) { + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast<ConstantDataArray>( + Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString().str(); - HasStr1 = true; - Str1 = Array->getAsString().str(); + } } diff --git a/llvm_mode/afl-llvm-lto-whitelist.so.cc b/llvm_mode/afl-llvm-lto-whitelist.so.cc index 5e157472..a116c4ea 100644 --- a/llvm_mode/afl-llvm-lto-whitelist.so.cc +++ b/llvm_mode/afl-llvm-lto-whitelist.so.cc @@ -111,7 +111,7 @@ bool AFLwhitelist::runOnModule(Module &M) { char be_quiet = 0; - if (isatty(2) && !getenv("AFL_QUIET")) { + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { SAYF(cCYA "afl-llvm-lto-whitelist" VERSION cRST " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 0d9e0aba..2d23ad21 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -84,6 +84,7 @@ class AFLCoverage : public ModulePass { uint32_t ngram_size = 0; uint32_t debug = 0; uint32_t map_size = MAP_SIZE; + uint32_t function_minimum_size = 1; char * ctx_str = NULL, *skip_nozero = NULL; }; @@ -182,6 +183,10 @@ bool AFLCoverage::runOnModule(Module &M) { #endif skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || + getenv("AFL_LLVM_SKIPSINGLEBLOCK")) + function_minimum_size = 2; + unsigned PrevLocSize = 0; char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); @@ -294,13 +299,15 @@ bool AFLCoverage::runOnModule(Module &M) { if (!isInWhitelist(&F)) continue; + if (F.size() < function_minimum_size) continue; + for (auto &BB : F) { BasicBlock::iterator IP = BB.getFirstInsertionPt(); IRBuilder<> IRB(&(*IP)); // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock() && F.size() > 1) { + if (ctx_str && &BB == &F.getEntryBlock()) { // load the context ID of the previous function and write to to a local // variable on the stack @@ -318,7 +325,7 @@ bool AFLCoverage::runOnModule(Module &M) { if ((callInst = dyn_cast<CallInst>(&IN))) { Function *Callee = callInst->getCalledFunction(); - if (!Callee || Callee->size() < 2) + if (!Callee || Callee->size() < function_minimum_size) continue; else { @@ -389,11 +396,11 @@ bool AFLCoverage::runOnModule(Module &M) { } // fprintf(stderr, " == %d\n", more_than_one); - if (more_than_one != 1) { + if (F.size() > 1 && more_than_one != 1) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX - if (ctx_str && has_calls && F.size() > 1) { + if (ctx_str && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) { @@ -526,7 +533,7 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX. // Currently this is only needed for the Ubuntu clang-6.0 bug - if (ctx_str && has_calls && F.size() > 1) { + if (ctx_str && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) { diff --git a/llvm_mode/afl-llvm-rt-lto.o.c b/llvm_mode/afl-llvm-rt-lto.o.c index 5921f968..e53785ff 100644 --- a/llvm_mode/afl-llvm-rt-lto.o.c +++ b/llvm_mode/afl-llvm-rt-lto.o.c @@ -10,6 +10,9 @@ */ +#include <stdio.h> +#include <stdlib.h> + // to prevent the function from being removed unsigned char __afl_lto_mode = 0; @@ -17,6 +20,7 @@ unsigned char __afl_lto_mode = 0; __attribute__((constructor(0))) void __afl_auto_init_globals(void) { + if (getenv("AFL_DEBUG")) fprintf(stderr, "[__afl_auto_init_globals]\n"); __afl_lto_mode = 1; } diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 56038f7a..ce8df332 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -63,7 +63,11 @@ is used for instrumentation output before __afl_map_shm() has a chance to run. It will end up as .comm, so it shouldn't be too wasteful. */ -u8 __afl_area_initial[MAP_SIZE]; +#ifdef AFL_REAL_LD +u8 __afl_area_initial[256000]; +#else +u8 __afl_area_initial[MAP_SIZE]; +#endif u8 *__afl_area_ptr = __afl_area_initial; u8 *__afl_dictionary; diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc index 2111b646..00732dbc 100644 --- a/llvm_mode/compare-transform-pass.so.cc +++ b/llvm_mode/compare-transform-pass.so.cc @@ -506,7 +506,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, bool CompareTransform::runOnModule(Module &M) { - if (isatty(2) && getenv("AFL_QUIET") == NULL) + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, " "extended by heiko@hexco.de\n"; else diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc index 3444d6a1..e8639347 100644 --- a/llvm_mode/split-switches-pass.so.cc +++ b/llvm_mode/split-switches-pass.so.cc @@ -418,7 +418,7 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { bool SplitSwitchesTransform::runOnModule(Module &M) { - if (isatty(2) && getenv("AFL_QUIET") == NULL) + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n"; else be_quiet = 1; diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh index 1b6690c6..bd18927f 100755 --- a/qemu_mode/build_qemu_support.sh +++ b/qemu_mode/build_qemu_support.sh @@ -163,6 +163,8 @@ fi cd qemu-$VERSION || exit 1 +echo Building for CPU target $CPU_TARGET + echo "[*] Applying patches..." patch -p1 <../patches/elfload.diff || exit 1 @@ -188,6 +190,7 @@ echo "[+] Patching done." if [ "$STATIC" = "1" ]; then + echo Building STATIC binary ./configure --extra-cflags="-O3 -ggdb -DAFL_QEMU_STATIC_BUILD=1" \ --disable-bsd-user --disable-guest-agent --disable-strip --disable-werror \ --disable-gcrypt --disable-debug-info --disable-debug-tcg --disable-tcg-interpreter \ diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h index 6e9ddc3b..78a8f800 100644 --- a/qemu_mode/patches/afl-qemu-cpu-inl.h +++ b/qemu_mode/patches/afl-qemu-cpu-inl.h @@ -123,12 +123,11 @@ struct afl_chain { /* Some forward decls: */ -TranslationBlock *tb_htable_lookup(CPUState *, target_ulong, target_ulong, - uint32_t, uint32_t); static inline TranslationBlock *tb_find(CPUState *, TranslationBlock *, int, uint32_t); static inline void tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb_next); +int open_self_maps(void *cpu_env, int fd); /************************* * ACTUAL IMPLEMENTATION * diff --git a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h index 014471ca..2e740ad9 100644 --- a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h +++ b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h @@ -35,7 +35,7 @@ #include "tcg.h" #include "tcg-op.h" -#if TCG_TARGET_LONG_BITS == 64 +#if TCG_TARGET_REG_BITS == 64 #define _DEFAULT_MO MO_64 #else #define _DEFAULT_MO MO_32 diff --git a/qemu_mode/patches/syscall.diff b/qemu_mode/patches/syscall.diff index b8c5ff39..b635a846 100644 --- a/qemu_mode/patches/syscall.diff +++ b/qemu_mode/patches/syscall.diff @@ -43,16 +43,17 @@ index b13a170e..3f5cc902 100644 ts = (TaskState *)cpu->opaque; if (flags & CLONE_SETTLS) cpu_set_tls (env, newtls); -@@ -6554,7 +6558,7 @@ static int open_self_cmdline(void *cpu_env, int fd) +@@ -6554,7 +6558,8 @@ static int open_self_cmdline(void *cpu_env, int fd) return 0; } -static int open_self_maps(void *cpu_env, int fd) ++int open_self_maps(void *cpu_env, int fd); +int open_self_maps(void *cpu_env, int fd) { CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env); TaskState *ts = cpu->opaque; -@@ -7324,10 +7328,12 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, +@@ -7324,10 +7329,12 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_stime /* not on alpha */ case TARGET_NR_stime: { @@ -68,7 +69,7 @@ index b13a170e..3f5cc902 100644 } #endif #ifdef TARGET_NR_alarm /* not on alpha */ -@@ -10529,7 +10535,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, +@@ -10529,7 +10536,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return TARGET_PAGE_SIZE; #endif case TARGET_NR_gettid: @@ -77,7 +78,7 @@ index b13a170e..3f5cc902 100644 #ifdef TARGET_NR_readahead case TARGET_NR_readahead: #if TARGET_ABI_BITS == 32 -@@ -10813,8 +10819,19 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, +@@ -10813,8 +10820,19 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return get_errno(safe_tkill((int)arg1, target_to_host_signal(arg2))); case TARGET_NR_tgkill: diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 8f48b1d0..4e973672 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -723,7 +723,7 @@ static void set_up_environment(void) { } - prog_in = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, getpid()); + prog_in = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, (u32)getpid()); } diff --git a/src/afl-as.c b/src/afl-as.c index 486a6afa..cf7f8bb6 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -233,7 +233,7 @@ static void edit_params(int argc, char **argv) { } modified_file = - alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(), (u32)time(NULL)); + alloc_printf("%s/.afl-%u-%u.s", tmp_dir, (u32)getpid(), (u32)time(NULL)); wrap_things_up: diff --git a/src/afl-common.c b/src/afl-common.c index 54b2e790..d9d57863 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -64,15 +64,15 @@ char *afl_environment_variables[] = { "AFL_LD_PRELOAD", "AFL_LD_VERBOSE", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CTX", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", "AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY", - "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", "AFL_LLVM_LAF_SPLIT_COMPARES", - "AFL_LLVM_LAF_SPLIT_COMPARES_BITW", "AFL_LLVM_LAF_SPLIT_FLOATS", - "AFL_LLVM_LAF_SPLIT_SWITCHES", "AFL_LLVM_LAF_TRANSFORM_COMPARES", - "AFL_LLVM_MAP_ADDR", "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", - "AFL_NGRAM_SIZE", "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", - "AFL_LLVM_SKIP_NEVERZERO", "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", - "AFL_LLVM_LTO_DONTWRITEID", "AFL_NO_ARITH", "AFL_NO_BUILTIN", - "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", "AFL_NO_UI", "AFL_NO_PYTHON", - "AFL_UNTRACER_FILE", + "AFL_LLVM_SKIPSINGLEBLOCK", "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", + "AFL_LLVM_LAF_SPLIT_COMPARES", "AFL_LLVM_LAF_SPLIT_COMPARES_BITW", + "AFL_LLVM_LAF_SPLIT_FLOATS", "AFL_LLVM_LAF_SPLIT_SWITCHES", + "AFL_LLVM_LAF_TRANSFORM_COMPARES", "AFL_LLVM_MAP_ADDR", + "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", "AFL_NGRAM_SIZE", + "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", "AFL_LLVM_SKIP_NEVERZERO", + "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID", + "AFL_NO_ARITH", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", + "AFL_NO_UI", "AFL_NO_PYTHON", "AFL_UNTRACER_FILE", "AFL_NO_X86", // not really an env but we dont want to warn on it "AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_PATH", "AFL_PERFORMANCE_FILE", //"AFL_PERSISTENT", // not implemented anymore, so warn additionally diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 2289183c..d4966889 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -437,13 +437,13 @@ u8 *describe_op(afl_state_t *afl, u8 hnb) { sprintf(ret, "src:%06u", afl->current_entry); - sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time); - if (afl->splicing_with >= 0) { sprintf(ret + strlen(ret), "+%06d", afl->splicing_with); } + + sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time); sprintf(ret + strlen(ret), ",op:%s", afl->stage_short); diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index beb89092..027add49 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -26,27 +26,55 @@ #include "afl-fuzz.h" -void load_custom_mutator(afl_state_t *, const char *); +struct custom_mutator *load_custom_mutator(afl_state_t *, const char *); +#ifdef USE_PYTHON +struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *); +#endif -void setup_custom_mutator(afl_state_t *afl) { +void setup_custom_mutators(afl_state_t *afl) { /* Try mutator library first */ - u8 *fn = afl->afl_env.afl_custom_mutator_library; + struct custom_mutator *mutator; + u8 * fn = afl->afl_env.afl_custom_mutator_library; + u32 prev_mutator_count = 0; if (fn) { - if (afl->limit_time_sig) { - + if (afl->limit_time_sig) FATAL( "MOpt and custom mutator are mutually exclusive. We accept pull " "requests that integrates MOpt with the optional mutators " - "(custom/radamsa/redquenn/...)."); + "(custom/radamsa/redqueen/...)."); - } + u8 *fn_token = (u8 *)strsep((char **)&fn, ";:,"); + + if (likely(!fn_token)) { - load_custom_mutator(afl, fn); + mutator = load_custom_mutator(afl, fn); + list_append(&afl->custom_mutator_list, mutator); + afl->custom_mutators_count++; - return; + } else { + + while (fn_token) { + + if (*fn_token) { // strsep can be empty if ";;" + + if (afl->not_on_tty && afl->debug) + SAYF("[Custom] Processing: %s\n", fn_token); + prev_mutator_count = afl->custom_mutators_count; + mutator = load_custom_mutator(afl, fn_token); + list_append(&afl->custom_mutator_list, mutator); + afl->custom_mutators_count++; + if (prev_mutator_count > afl->custom_mutators_count) + FATAL("Maximum Custom Mutator count reached."); + fn_token = (u8 *)strsep((char **)&fn, ";:,"); + + } + + } + + } } @@ -65,7 +93,9 @@ void setup_custom_mutator(afl_state_t *afl) { } - load_custom_mutator_py(afl, module_name); + struct custom_mutator *mutator = load_custom_mutator_py(afl, module_name); + afl->custom_mutators_count++; + list_append(&afl->custom_mutator_list, mutator); } @@ -80,114 +110,87 @@ void setup_custom_mutator(afl_state_t *afl) { } -void destroy_custom_mutator(afl_state_t *afl) { +void destroy_custom_mutators(afl_state_t *afl) { - if (afl->mutator) { + if (afl->custom_mutators_count) { - afl->mutator->afl_custom_deinit(afl->mutator->data); + LIST_FOREACH_CLEAR(&afl->custom_mutator_list, struct custom_mutator, { - if (afl->mutator->dh) { dlclose(afl->mutator->dh); } + if (!el->data) { FATAL("Deintializing NULL mutator"); } + el->afl_custom_deinit(el->data); + if (el->dh) dlclose(el->dh); - if (afl->mutator->pre_save_buf) { + if (el->pre_save_buf) { - ck_free(afl->mutator->pre_save_buf); - afl->mutator->pre_save_buf = NULL; - afl->mutator->pre_save_size = 0; + ck_free(el->pre_save_buf); + el->pre_save_buf = NULL; + el->pre_save_size = 0; - } + } - ck_free(afl->mutator); - afl->mutator = NULL; + ck_free(el); + + }); } } -void load_custom_mutator(afl_state_t *afl, const char *fn) { +struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { - void *dh; - afl->mutator = ck_alloc(sizeof(struct custom_mutator)); - afl->mutator->pre_save_buf = NULL; - afl->mutator->pre_save_size = 0; + void * dh; + struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator)); - afl->mutator->name = fn; + mutator->name = fn; ACTF("Loading custom mutator library from '%s'...", fn); dh = dlopen(fn, RTLD_NOW); - if (!dh) { FATAL("%s", dlerror()); } - afl->mutator->dh = dh; + if (!dh) FATAL("%s", dlerror()); + mutator->dh = dh; /* Mutator */ - /* "afl_custom_init", required */ - afl->mutator->afl_custom_init = dlsym(dh, "afl_custom_init"); - if (!afl->mutator->afl_custom_init) { - - FATAL("Symbol 'afl_custom_init' not found."); - - } - - /* "afl_custom_deinit", required */ - afl->mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit"); - if (!afl->mutator->afl_custom_deinit) { - - FATAL("Symbol 'afl_custom_deinit' not found."); - - } + /* "afl_custom_init", optional for backward compatibility */ + mutator->afl_custom_init = dlsym(dh, "afl_custom_init"); + if (!mutator->afl_custom_init) WARNF("Symbol 'afl_custom_init' not found."); /* "afl_custom_fuzz" or "afl_custom_mutator", required */ - afl->mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz"); - if (!afl->mutator->afl_custom_fuzz) { + mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz"); + if (!mutator->afl_custom_fuzz) { /* Try "afl_custom_mutator" for backward compatibility */ WARNF("Symbol 'afl_custom_fuzz' not found. Try 'afl_custom_mutator'."); - afl->mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator"); - if (!afl->mutator->afl_custom_fuzz) { - + mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator"); + if (!mutator->afl_custom_fuzz) FATAL("Symbol 'afl_custom_mutator' not found."); - } - } /* "afl_custom_pre_save", optional */ - afl->mutator->afl_custom_pre_save = dlsym(dh, "afl_custom_pre_save"); - if (!afl->mutator->afl_custom_pre_save) { - + mutator->afl_custom_pre_save = dlsym(dh, "afl_custom_pre_save"); + if (!mutator->afl_custom_pre_save) WARNF("Symbol 'afl_custom_pre_save' not found."); - } - u8 notrim = 0; /* "afl_custom_init_trim", optional */ - afl->mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim"); - if (!afl->mutator->afl_custom_init_trim) { - + mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim"); + if (!mutator->afl_custom_init_trim) WARNF("Symbol 'afl_custom_init_trim' not found."); - } - /* "afl_custom_trim", optional */ - afl->mutator->afl_custom_trim = dlsym(dh, "afl_custom_trim"); - if (!afl->mutator->afl_custom_trim) { - - WARNF("Symbol 'afl_custom_trim' not found."); - - } + mutator->afl_custom_trim = dlsym(dh, "afl_custom_trim"); + if (!mutator->afl_custom_trim) WARNF("Symbol 'afl_custom_trim' not found."); /* "afl_custom_post_trim", optional */ - afl->mutator->afl_custom_post_trim = dlsym(dh, "afl_custom_post_trim"); - if (!afl->mutator->afl_custom_post_trim) { - + mutator->afl_custom_post_trim = dlsym(dh, "afl_custom_post_trim"); + if (!mutator->afl_custom_post_trim) WARNF("Symbol 'afl_custom_post_trim' not found."); - } - if (notrim) { - afl->mutator->afl_custom_init_trim = NULL; - afl->mutator->afl_custom_trim = NULL; - afl->mutator->afl_custom_post_trim = NULL; + mutator->afl_custom_init_trim = NULL; + mutator->afl_custom_trim = NULL; + mutator->afl_custom_post_trim = NULL; WARNF( "Custom mutator does not implement all three trim APIs, standard " "trimming will be used."); @@ -195,53 +198,42 @@ void load_custom_mutator(afl_state_t *afl, const char *fn) { } /* "afl_custom_havoc_mutation", optional */ - afl->mutator->afl_custom_havoc_mutation = - dlsym(dh, "afl_custom_havoc_mutation"); - if (!afl->mutator->afl_custom_havoc_mutation) { - + mutator->afl_custom_havoc_mutation = dlsym(dh, "afl_custom_havoc_mutation"); + if (!mutator->afl_custom_havoc_mutation) WARNF("Symbol 'afl_custom_havoc_mutation' not found."); - } - /* "afl_custom_havoc_mutation", optional */ - afl->mutator->afl_custom_havoc_mutation_probability = + mutator->afl_custom_havoc_mutation_probability = dlsym(dh, "afl_custom_havoc_mutation_probability"); - if (!afl->mutator->afl_custom_havoc_mutation_probability) { - + if (!mutator->afl_custom_havoc_mutation_probability) WARNF("Symbol 'afl_custom_havoc_mutation_probability' not found."); - } - /* "afl_custom_queue_get", optional */ - afl->mutator->afl_custom_queue_get = dlsym(dh, "afl_custom_queue_get"); - if (!afl->mutator->afl_custom_queue_get) { - + mutator->afl_custom_queue_get = dlsym(dh, "afl_custom_queue_get"); + if (!mutator->afl_custom_queue_get) WARNF("Symbol 'afl_custom_queue_get' not found."); - } - /* "afl_custom_queue_new_entry", optional */ - afl->mutator->afl_custom_queue_new_entry = - dlsym(dh, "afl_custom_queue_new_entry"); - if (!afl->mutator->afl_custom_queue_new_entry) { - + mutator->afl_custom_queue_new_entry = dlsym(dh, "afl_custom_queue_new_entry"); + if (!mutator->afl_custom_queue_new_entry) WARNF("Symbol 'afl_custom_queue_new_entry' not found"); - } - OKF("Custom mutator '%s' installed successfully.", fn); /* Initialize the custom mutator */ - if (afl->mutator->afl_custom_init) { + if (mutator->afl_custom_init) + mutator->data = mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF)); - afl->mutator->data = - afl->mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF)); + mutator->stacked_custom = (mutator && mutator->afl_custom_havoc_mutation); + mutator->stacked_custom_prob = + 6; // like one of the default mutations in havoc - } + return mutator; } -u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { +u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, + struct custom_mutator *mutator) { u8 needs_write = 0, fault = 0; u32 trim_exec = 0; @@ -254,8 +246,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { /* Initialize trimming in the custom mutator */ afl->stage_cur = 0; - afl->stage_max = - afl->mutator->afl_custom_init_trim(afl->mutator->data, in_buf, q->len); + afl->stage_max = mutator->afl_custom_init_trim(mutator->data, in_buf, q->len); if (unlikely(afl->stage_max) < 0) { FATAL("custom_init_trim error ret: %d", afl->stage_max); @@ -278,7 +269,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { u32 cksum; - size_t retlen = afl->mutator->afl_custom_trim(afl->mutator->data, &retbuf); + size_t retlen = mutator->afl_custom_trim(mutator->data, &retbuf); if (unlikely(!retbuf)) { @@ -318,8 +309,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { } /* Tell the custom mutator that the trimming was successful */ - afl->stage_cur = - afl->mutator->afl_custom_post_trim(afl->mutator->data, 1); + afl->stage_cur = mutator->afl_custom_post_trim(mutator->data, 1); if (afl->not_on_tty && afl->debug) { @@ -331,8 +321,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { } else { /* Tell the custom mutator that the trimming was unsuccessful */ - afl->stage_cur = - afl->mutator->afl_custom_post_trim(afl->mutator->data, 0); + afl->stage_cur = mutator->afl_custom_post_trim(mutator->data, 0); if (unlikely(afl->stage_cur < 0)) { FATAL("Error ret in custom_post_trim: %d", afl->stage_cur); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 6d399a03..ddd15c84 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -384,16 +384,20 @@ u8 fuzz_one_original(afl_state_t *afl) { #else - if (unlikely(afl->mutator) && unlikely(afl->mutator->afl_custom_queue_get)) { + if (unlikely(afl->custom_mutators_count)) { /* The custom mutator will decide to skip this test case or not. */ - if (!afl->mutator->afl_custom_queue_get(afl->mutator->data, - afl->queue_cur->fname)) { + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - return 1; + if (el->afl_custom_queue_get && + !el->afl_custom_queue_get(el->data, afl->queue_cur->fname)) { - } + return 1; + + } + + }); } @@ -1646,13 +1650,13 @@ custom_mutator_stage: * CUSTOM MUTATORS * *******************/ - if (likely(!afl->mutator)) { goto havoc_stage; } - if (likely(!afl->mutator->afl_custom_fuzz)) { goto havoc_stage; } + if (likely(!afl->custom_mutators_count)) { goto havoc_stage; } afl->stage_name = "custom mutator"; afl->stage_short = "custom"; afl->stage_max = HAVOC_CYCLES * perf_score / afl->havoc_div / 100; afl->stage_val_type = STAGE_VAL_NONE; + bool has_custom_fuzz = false; if (afl->stage_max < HAVOC_MIN) { afl->stage_max = HAVOC_MIN; } @@ -1660,98 +1664,112 @@ custom_mutator_stage: orig_hit_cnt = afl->queued_paths + afl->unique_crashes; - for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - struct queue_entry *target; - u32 tid; - u8 * new_buf; + if (el->afl_custom_fuzz) { - retry_external_pick: - /* Pick a random other queue entry for passing to external API */ - do { + has_custom_fuzz = true; - tid = rand_below(afl, afl->queued_paths); + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; + ++afl->stage_cur) { - } while (tid == afl->current_entry && afl->queued_paths > 1); + struct queue_entry *target; + u32 tid; + u8 * new_buf; - target = afl->queue; + retry_external_pick: + /* Pick a random other queue entry for passing to external API */ + do { - while (tid >= 100) { + tid = rand_below(afl, afl->queued_paths); - target = target->next_100; - tid -= 100; + } while (tid == afl->current_entry && afl->queued_paths > 1); - } + target = afl->queue; - while (tid--) { + while (tid >= 100) { - target = target->next; + target = target->next_100; + tid -= 100; - } + } - /* Make sure that the target has a reasonable length. */ + while (tid--) { - while (target && (target->len < 2 || target == afl->queue_cur) && - afl->queued_paths > 1) { + target = target->next; - target = target->next; - ++afl->splicing_with; + } - } + /* Make sure that the target has a reasonable length. */ - if (!target) { goto retry_external_pick; } + while (target && (target->len < 2 || target == afl->queue_cur) && + afl->queued_paths > 1) { - /* Read the additional testcase into a new buffer. */ - fd = open(target->fname, O_RDONLY); - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } + target = target->next; + ++afl->splicing_with; - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len); - ck_read(fd, new_buf, target->len, target->fname); - close(fd); + } - u8 *mutated_buf = NULL; + if (!target) { goto retry_external_pick; } - size_t mutated_size = afl->mutator->afl_custom_fuzz( - afl->mutator->data, out_buf, len, &mutated_buf, new_buf, target->len, - max_seed_size); + /* Read the additional testcase into a new buffer. */ + fd = open(target->fname, O_RDONLY); + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - if (unlikely(!mutated_buf)) { + new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len); + ck_read(fd, new_buf, target->len, target->fname); + close(fd); - FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); + u8 *mutated_buf = NULL; - } + size_t mutated_size = + el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, + target->len, max_seed_size); - if (mutated_size > 0) { + if (unlikely(!mutated_buf)) { - if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { + FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); - goto abandon_entry; + } - } + if (mutated_size > 0) { + + if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { + + goto abandon_entry; + + } - /* If we're finding new stuff, let's run for a bit longer, limits - permitting. */ + /* If we're finding new stuff, let's run for a bit longer, limits + permitting. */ - if (afl->queued_paths != havoc_queued) { + if (afl->queued_paths != havoc_queued) { - if (perf_score <= afl->havoc_max_mult * 100) { + if (perf_score <= afl->havoc_max_mult * 100) { - afl->stage_max *= 2; - perf_score *= 2; + afl->stage_max *= 2; + perf_score *= 2; + + } + + havoc_queued = afl->queued_paths; + + } } - havoc_queued = afl->queued_paths; + /* `(afl->)out_buf` may have been changed by the call to custom_fuzz */ + /* TODO: Only do this when `mutated_buf` == `out_buf`? Branch vs Memcpy. + */ + memcpy(out_buf, in_buf, len); } } - /* `(afl->)out_buf` may have been changed by the call to custom_fuzz */ - /* TODO: Only do this when `mutated_buf` == `out_buf`? Branch vs Memcpy. */ - memcpy(out_buf, in_buf, len); + }); - } + if (!has_custom_fuzz) goto havoc_stage; new_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -1803,20 +1821,26 @@ havoc_stage: havoc_queued = afl->queued_paths; - u8 stacked_custom = (afl->mutator && afl->mutator->afl_custom_havoc_mutation); - u8 stacked_custom_prob = 6; // like one of the default mutations in havoc + if (afl->custom_mutators_count) { - if (stacked_custom && afl->mutator->afl_custom_havoc_mutation_probability) { + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - stacked_custom_prob = - afl->mutator->afl_custom_havoc_mutation_probability(afl->mutator->data); - if (stacked_custom_prob > 100) { + if (el->stacked_custom && el->afl_custom_havoc_mutation_probability) { - FATAL( - "The probability returned by afl_custom_havoc_mutation_propability " - "has to be in the range 0-100."); + el->stacked_custom_prob = + el->afl_custom_havoc_mutation_probability(el->data); + if (el->stacked_custom_prob > 100) { - } + FATAL( + "The probability returned by " + "afl_custom_havoc_mutation_propability " + "has to be in the range 0-100."); + + } + + } + + }); } @@ -1831,28 +1855,37 @@ havoc_stage: for (i = 0; i < use_stacking; ++i) { - if (stacked_custom && rand_below(afl, 100) < stacked_custom_prob) { + if (afl->custom_mutators_count) { - u8 * custom_havoc_buf = NULL; - size_t new_len = afl->mutator->afl_custom_havoc_mutation( - afl->mutator->data, out_buf, temp_len, &custom_havoc_buf, MAX_FILE); - if (unlikely(!custom_havoc_buf)) { + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - FATAL("Error in custom_havoc (return %zd)", new_len); + if (el->stacked_custom && + rand_below(afl, 100) < el->stacked_custom_prob) { - } + u8 * custom_havoc_buf = NULL; + size_t new_len = el->afl_custom_havoc_mutation( + el->data, out_buf, temp_len, &custom_havoc_buf, MAX_FILE); + if (unlikely(!custom_havoc_buf)) { + + FATAL("Error in custom_havoc (return %zd)", new_len); - if (likely(new_len > 0 && custom_havoc_buf)) { + } - temp_len = new_len; - if (out_buf != custom_havoc_buf) { + if (likely(new_len > 0 && custom_havoc_buf)) { - ck_maybe_grow(BUF_PARAMS(out), temp_len); - memcpy(out_buf, custom_havoc_buf, temp_len); + temp_len = new_len; + if (out_buf != custom_havoc_buf) { + + ck_maybe_grow(BUF_PARAMS(out), temp_len); + memcpy(out_buf, custom_havoc_buf, temp_len); + + } + + } } - } + }); } diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 64cabcad..832dba06 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -71,7 +71,7 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, PyTuple_SetItem(py_args, 1, py_value); - /* max_size */ +/* max_size */ #if PY_MAJOR_VERSION >= 3 py_value = PyLong_FromLong(max_size); #else @@ -295,80 +295,75 @@ void deinit_py(void *py_mutator) { } -void load_custom_mutator_py(afl_state_t *afl, char *module_name) { +struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, + char * module_name) { - afl->mutator = ck_alloc(sizeof(struct custom_mutator)); - afl->mutator->pre_save_buf = NULL; - afl->mutator->pre_save_size = 0; + struct custom_mutator *mutator; - afl->mutator->name = module_name; + mutator = ck_alloc(sizeof(struct custom_mutator)); + mutator->pre_save_buf = NULL; + mutator->pre_save_size = 0; + + mutator->name = module_name; ACTF("Loading Python mutator library from '%s'...", module_name); py_mutator_t *py_mutator; py_mutator = init_py_module(afl, module_name); - afl->mutator->data = py_mutator; + mutator->data = py_mutator; if (!py_mutator) { FATAL("Failed to load python mutator."); } PyObject **py_functions = py_mutator->py_functions; - if (py_functions[PY_FUNC_INIT]) { - - afl->mutator->afl_custom_init = unsupported; - - } - - if (py_functions[PY_FUNC_DEINIT]) { - - afl->mutator->afl_custom_deinit = deinit_py; + if (py_functions[PY_FUNC_INIT]) { mutator->afl_custom_init = unsupported; } - } + if (py_functions[PY_FUNC_DEINIT]) { mutator->afl_custom_deinit = deinit_py; } /* "afl_custom_fuzz" should not be NULL, but the interface of Python mutator is quite different from the custom mutator. */ - afl->mutator->afl_custom_fuzz = fuzz_py; + mutator->afl_custom_fuzz = fuzz_py; if (py_functions[PY_FUNC_PRE_SAVE]) { - afl->mutator->afl_custom_pre_save = pre_save_py; + mutator->afl_custom_pre_save = pre_save_py; } if (py_functions[PY_FUNC_INIT_TRIM]) { - afl->mutator->afl_custom_init_trim = init_trim_py; + mutator->afl_custom_init_trim = init_trim_py; } if (py_functions[PY_FUNC_POST_TRIM]) { - afl->mutator->afl_custom_post_trim = post_trim_py; + mutator->afl_custom_post_trim = post_trim_py; } - if (py_functions[PY_FUNC_TRIM]) { afl->mutator->afl_custom_trim = trim_py; } + if (py_functions[PY_FUNC_TRIM]) { mutator->afl_custom_trim = trim_py; } if (py_functions[PY_FUNC_HAVOC_MUTATION]) { - afl->mutator->afl_custom_havoc_mutation = havoc_mutation_py; + mutator->afl_custom_havoc_mutation = havoc_mutation_py; } if (py_functions[PY_FUNC_HAVOC_MUTATION_PROBABILITY]) { - afl->mutator->afl_custom_havoc_mutation_probability = + mutator->afl_custom_havoc_mutation_probability = havoc_mutation_probability_py; } if (py_functions[PY_FUNC_QUEUE_GET]) { - afl->mutator->afl_custom_queue_get = queue_get_py; + mutator->afl_custom_queue_get = queue_get_py; } if (py_functions[PY_FUNC_QUEUE_NEW_ENTRY]) { - afl->mutator->afl_custom_queue_new_entry = queue_new_entry_py; + mutator->afl_custom_queue_new_entry = queue_new_entry_py; } @@ -377,6 +372,8 @@ void load_custom_mutator_py(afl_state_t *afl, char *module_name) { /* Initialize the custom mutator */ init_py(afl, py_mutator, rand_below(afl, 0xFFFFFFFF)); + return mutator; + } size_t pre_save_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf) { @@ -545,7 +542,7 @@ size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size, PyTuple_SetItem(py_args, 0, py_value); - /* max_size */ +/* max_size */ #if PY_MAJOR_VERSION >= 3 py_value = PyLong_FromLong(max_size); #else @@ -627,7 +624,7 @@ u8 queue_get_py(void *py_mutator, const u8 *filename) { py_args = PyTuple_New(1); - // File name +// File name #if PY_MAJOR_VERSION >= 3 py_value = PyUnicode_FromString(filename); #else @@ -677,7 +674,7 @@ void queue_new_entry_py(void *py_mutator, const u8 *filename_new_queue, py_args = PyTuple_New(2); - // New queue +// New queue #if PY_MAJOR_VERSION >= 3 py_value = PyUnicode_FromString(filename_new_queue); #else diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index f998c06b..cfeb6c5e 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -140,15 +140,22 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { afl->last_path_time = get_cur_time(); - if (afl->mutator && afl->mutator->afl_custom_queue_new_entry) { + if (afl->custom_mutators_count) { - u8 *fname_orig = NULL; + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - /* At the initialization stage, queue_cur is NULL */ - if (afl->queue_cur) { fname_orig = afl->queue_cur->fname; } + if (el->afl_custom_queue_new_entry) { - afl->mutator->afl_custom_queue_new_entry(afl->mutator->data, fname, - fname_orig); + u8 *fname_orig = NULL; + + /* At the initialization stage, queue_cur is NULL */ + if (afl->queue_cur) fname_orig = afl->queue_cur->fname; + + el->afl_custom_queue_new_entry(el->data, fname, fname_orig); + + } + + }); } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 692026d4..4a22dad6 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -30,13 +30,37 @@ #include "cmplog.h" +#ifdef PROFILING +u64 time_spent_working = 0; +#endif + /* Execute target application, monitoring for timeouts. Return status information. The called program will update afl->fsrv->trace_bits. */ fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { +#ifdef PROFILING + static u64 time_spent_start = 0; + struct timespec spec; + if (time_spent_start) { + + u64 current; + clock_gettime(CLOCK_REALTIME, &spec); + current = (spec.tv_sec * 1000000000) + spec.tv_nsec; + time_spent_working += (current - time_spent_start); + + } + +#endif + fsrv_run_result_t res = afl_fsrv_run_target(fsrv, timeout, &afl->stop_soon); + +#ifdef PROFILING + clock_gettime(CLOCK_REALTIME, &spec); + time_spent_start = (spec.tv_sec * 1000000000) + spec.tv_nsec; +#endif + // TODO: Don't classify for faults? classify_counts(fsrv); return res; @@ -65,21 +89,40 @@ void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { #endif - if (unlikely(afl->mutator && afl->mutator->afl_custom_pre_save)) { + if (unlikely(afl->custom_mutators_count)) { + + u8 * new_buf = NULL; + ssize_t new_size = len; + void * new_mem = mem; + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_pre_save) { + + new_size = + el->afl_custom_pre_save(el->data, new_mem, new_size, &new_buf); - u8 *new_buf = NULL; + } + + new_mem = new_buf; - size_t new_size = afl->mutator->afl_custom_pre_save(afl->mutator->data, mem, - len, &new_buf); + }); - if (unlikely(!new_buf)) { + if (unlikely(!new_buf && (new_size <= 0))) { FATAL("Custom_pre_save failed (ret: %lu)", (long unsigned)new_size); - } + } else if (likely(new_buf)) { + + /* everything as planned. use the new data. */ + afl_fsrv_write_to_testcase(&afl->fsrv, new_buf, new_size); - /* everything as planned. use the new data. */ - afl_fsrv_write_to_testcase(&afl->fsrv, new_buf, new_size); + } else { + + /* custom mutators do not has a custom_pre_save function */ + afl_fsrv_write_to_testcase(&afl->fsrv, mem, len); + + } } else { @@ -489,9 +532,23 @@ void sync_fuzzers(afl_state_t *afl) { u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { /* Custom mutator trimmer */ - if (afl->mutator && afl->mutator->afl_custom_trim) { + if (afl->custom_mutators_count) { + + u8 trimmed_case = 0; + bool custom_trimmed = false; + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_trim) { + + trimmed_case = trim_case_custom(afl, q, in_buf, el); + custom_trimmed = true; + + } + + }); - return trim_case_custom(afl, q, in_buf); + if (custom_trimmed) return trimmed_case; } diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 3cbb2d8c..014ed34d 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -72,7 +72,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "start_time : %llu\n" "last_update : %llu\n" "run_time : %llu\n" - "fuzzer_pid : %d\n" + "fuzzer_pid : %u\n" "cycles_done : %llu\n" "cycles_wo_finds : %llu\n" "execs_done : %llu\n" @@ -106,7 +106,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "target_mode : %s%s%s%s%s%s%s%s\n" "command_line : %s\n", afl->start_time / 1000, cur_time / 1000, - (cur_time - afl->start_time) / 1000, getpid(), + (cur_time - afl->start_time) / 1000, (u32)getpid(), afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds, afl->fsrv.total_execs, afl->fsrv.total_execs / @@ -792,7 +792,7 @@ void show_stats(afl_state_t *afl) { } - if (afl->mutator) { + if (afl->custom_mutators_count) { sprintf(tmp, "%s/%s", u_stringify_int(IB(0), afl->stage_finds[STAGE_CUSTOM_MUTATOR]), diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5920f5c0..aaf615e9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -27,6 +27,10 @@ #include "cmplog.h" #include <limits.h> +#ifdef PROFILING +extern u64 time_spent_working; +#endif + static u8 *get_libradamsa_path(u8 *own_loc) { u8 *tmp, *cp, *rsl, *own_copy; @@ -644,10 +648,7 @@ int main(int argc, char **argv_orig, char **envp) { } afl->limit_time_puppet = limit_time_puppet2; - - SAYF("limit_time_puppet %d\n", afl->limit_time_puppet); afl->swarm_now = 0; - if (afl->limit_time_puppet == 0) { afl->key_puppet = 1; } int i; @@ -1073,7 +1074,7 @@ int main(int argc, char **argv_orig, char **envp) { setup_dirs_fds(afl); - setup_custom_mutator(afl); + setup_custom_mutators(afl); setup_cmdline_file(afl, argv + optind); @@ -1351,10 +1352,17 @@ stop_fuzzing: } +#ifdef PROFILING + SAYF(cYEL "[!] " cRST + "Profiling information: %llu ms total work, %llu ns/run\n", + time_spent_working / 1000000, + time_spent_working / afl->fsrv.total_execs); +#endif + fclose(afl->fsrv.plot_file); destroy_queue(afl); destroy_extras(afl); - destroy_custom_mutator(afl); + destroy_custom_mutators(afl); afl_shm_deinit(&afl->shm); afl_fsrv_deinit(&afl->fsrv); if (afl->orig_cmdline) { ck_free(afl->orig_cmdline); } diff --git a/src/afl-showmap.c b/src/afl-showmap.c index a51d520d..ed59f2f5 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -865,7 +865,8 @@ int main(int argc, char **argv_orig, char **envp) { } - stdin_file = alloc_printf("%s/.afl-showmap-temp-%u", use_dir, getpid()); + stdin_file = + alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid()); unlink(stdin_file); atexit(at_exit_handler); fsrv->out_fd = open(stdin_file, O_RDWR | O_CREAT | O_EXCL, 0600); diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 98568473..e15dc72d 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -619,7 +619,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { } - out_file = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid()); + out_file = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, (u32)getpid()); } diff --git a/test/test-multiple-mutators.c b/test/test-multiple-mutators.c new file mode 100644 index 00000000..0f6f5c64 --- /dev/null +++ b/test/test-multiple-mutators.c @@ -0,0 +1,24 @@ +/** + * Test-Case for multiple custom mutators in C + * Reference: + * https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/blob/master/4_libprotobuf_aflpp_custom_mutator/vuln.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int main(int argc, char **argv) { + + int a = 0; + char s[16]; + memset(s, 0, 16); + read(0, s, 0xa0); + + if (s[17] != '\x00') { abort(); } + + return 0; + +} + diff --git a/test/test.sh b/test/test.sh index 90633a9f..919d7a9c 100755 --- a/test/test.sh +++ b/test/test.sh @@ -949,7 +949,7 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { } 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 + # 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 } || { @@ -959,6 +959,16 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { ../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 make -C ../examples/custom_mutators libexamplemutator.so > /dev/null 2>&1 test -e test-custom-mutator -a -e ${CUSTOM_MUTATOR_PATH}/libexamplemutator.so && { @@ -986,6 +996,25 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { # 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 20 seconds" + { + AFL_CUSTOM_MUTATOR_LIBRARY="${CUSTOM_MUTATOR_PATH}/libexamplemutator.so;${CUSTOM_MUTATOR_PATH}/libexamplemutator.so" ../afl-fuzz -V20 -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 10 seconds" { @@ -1010,6 +1039,7 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { # Clean rm -rf in out errors rm -rf ${CUSTOM_MUTATOR_PATH}/__pycache__/ + rm -f test-multiple-mutators } || { ls . ls ${CUSTOM_MUTATOR_PATH} @@ -1021,6 +1051,7 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { 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 diff --git a/unicorn_mode/UNICORNAFL_VERSION b/unicorn_mode/UNICORNAFL_VERSION index c0cc9e02..336c171b 100644 --- a/unicorn_mode/UNICORNAFL_VERSION +++ b/unicorn_mode/UNICORNAFL_VERSION @@ -1 +1 @@ -94c1976 +a5b7900 diff --git a/unicorn_mode/samples/persistent/COMPILE.md b/unicorn_mode/samples/persistent/COMPILE.md index 781f15c0..111dfc54 100644 --- a/unicorn_mode/samples/persistent/COMPILE.md +++ b/unicorn_mode/samples/persistent/COMPILE.md @@ -1,6 +1,6 @@ # C Sample -This shows a simple persistent harness for unicornafl in C +This shows a simple persistent harness for unicornafl in C. In contrast to the normal c harness, this harness manually resets the unicorn state on each new input. Thanks to this, we can rerun the testcase in unicorn multiple times, without the need to fork again. @@ -21,4 +21,4 @@ was built in case you want to rebuild it or recompile it for any reason. The pre-built binary (persistent_target_x86_64.bin) was built using -g -O0 in gcc. -We then load the binary we execute the main function directly. +We then load the binary and we execute the main function directly. diff --git a/unicorn_mode/samples/persistent/Makefile b/unicorn_mode/samples/persistent/Makefile index 9c7ed7aa..9596facc 100644 --- a/unicorn_mode/samples/persistent/Makefile +++ b/unicorn_mode/samples/persistent/Makefile @@ -17,9 +17,9 @@ CFLAGS += -Wall -Werror -I../../unicornafl/include LDFLAGS += -L$(LIBDIR) -lpthread -lm -_LRT = $(_UNIQ)$(UNAME_S:Linux=) -__LRT = $(_LRT:$(_UNIQ)=-lrt) -LRT = $(__LRT:$(_UNIQ)=) +_LRT = $(_UNIQ)$(UNAME_S) +__LRT = $(_LRT:$(_UNIQ)Linux=-lrt) +LRT = $(__LRT:$(_UNIQ)$(UNAME_S)=) LDFLAGS += $(LRT) @@ -48,3 +48,6 @@ debug: harness-debug.o fuzz: harness ../../../afl-fuzz -m none -i sample_inputs -o out -- ./harness @@ + +debugmake: + @echo UNAME_S=$(UNAME_S), _LRT=$(_LRT), __LRT=$(__LRT), LRT=$(LRT) diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl -Subproject 94c1976975518691a03602f7ec5a817e2f34118 +Subproject a5b79002ca18219c83f9aec4e71007917c6be2e diff --git a/unicorn_mode/update_uc_ref.sh b/unicorn_mode/update_uc_ref.sh index d198fa2a..486f37d6 100755 --- a/unicorn_mode/update_uc_ref.sh +++ b/unicorn_mode/update_uc_ref.sh @@ -1,20 +1,40 @@ #/bin/sh +################################################## +# AFL++ internal tool to update unicornafl ref. +# Usage: ./update_uc_ref.sh <new commit hash> +# If no commit hash was provided, it'll take HEAD. +################################################## + UC_VERSION_FILE='./UNICORNAFL_VERSION' NEW_VERSION="$1" -if [ -z "$NEW_VERSION" ]; then + +if [ "$NEW_VERSION" = "-h" ]; then echo "Internal script to update bound unicornafl version." echo echo "Usage: ./update_uc_ref.sh <new commit hash>" + echo "If no commit hash is provided, will use HEAD." + echo "-h to show this help screen." exit 1 fi git submodule init && git submodule update || exit 1 -cd ./unicornafl +cd ./unicornafl || exit 1 git fetch origin master 1>/dev/null || exit 1 git stash 1>/dev/null 2>/dev/null git stash drop 1>/dev/null 2>/dev/null + +if [ -z "$NEW_VERSION" ]; then + # No version provided, take HEAD. + NEW_VERSION=$(git rev-parse --short HEAD) +fi + +if [ -z "$NEW_VERSION" ]; then + echo "Error getting version." + exit 1 +fi + git checkout "$NEW_VERSION" || exit 1 cd .. |