diff options
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | docs/perf_tips.txt | 8 | ||||
-rw-r--r-- | gcc_plugin/Makefile | 6 | ||||
-rw-r--r-- | gcc_plugin/afl-gcc-fast.c | 2 | ||||
-rw-r--r-- | gcc_plugin/afl-gcc-rt.o.c | 100 | ||||
-rw-r--r-- | libdislocator/libdislocator.so.c | 4 | ||||
-rw-r--r-- | src/afl-fuzz.c | 4 | ||||
-rwxr-xr-x | test/test-performance.sh | 7 | ||||
-rwxr-xr-x | test/test.sh | 5 |
11 files changed, 127 insertions, 26 deletions
diff --git a/Makefile b/Makefile index 87c7cdef..c4269d6b 100644 --- a/Makefile +++ b/Makefile @@ -16,9 +16,6 @@ # For Heiko: #TEST_MMAP=1 -PROGNAME = afl -VERSION = $(shell grep '^\#define VERSION ' include/config.h | cut -d '"' -f2) - PREFIX ?= /usr/local BIN_PATH = $(PREFIX)/bin HELPER_PATH = $(PREFIX)/lib/afl @@ -26,6 +23,9 @@ DOC_PATH = $(PREFIX)/share/doc/afl MISC_PATH = $(PREFIX)/share/afl MAN_PATH = $(PREFIX)/man/man8 +PROGNAME = afl +VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2) + # PROGS intentionally omit afl-as, which gets installed elsewhere. PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze @@ -34,8 +34,8 @@ MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) CFLAGS ?= -O3 -funroll-loops CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ \ - -DAFL_PATH=\"$(HELPER_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" \ - -DBIN_PATH=\"$(BIN_PATH)\" -Wno-unused-function + -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ + -DDOC_PATH=\"$(DOC_PATH)\" -Wno-unused-function AFL_FUZZ_FILES = $(wildcard src/afl-fuzz*.c) @@ -116,7 +116,7 @@ help: @echo "==========================================" @echo "all: just the main afl++ binaries" @echo "binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap" - @echo "source-only: everything for source code fuzzing: llvm_mode, libdislocator, libtokencap" + @echo "source-only: everything for source code fuzzing: llvm_mode, gcc_plugin, libdislocator, libtokencap" @echo "distrib: everything (for both binary-only and source code fuzzing)" @echo "man: creates simple man pages from the help option of the programs" @echo "install: installs everything you have compiled with the build option above" diff --git a/README.md b/README.md index 4b9537d2..de012e62 100644 --- a/README.md +++ b/README.md @@ -55,12 +55,14 @@ | Feature/Instrumentation | AFL-GCC | LLVM_MODE | GCC_PLUGIN | QEMU_MODE | Unicorn | | ----------------------- |:-------:|:---------:|:----------:|:---------:|:-------:| | laf-intel / CompCov | | x | | x | x | - | NeverZero | X | x(1) | | x | x | + | NeverZero | X | x(1) | (2) | x | x | | Persistent mode | | x | X | x | | | Whitelist | | x | X | | | | InsTrim | | x | | | | + neverZero: (1) only in LLVM >= 9.0 due to a bug in llvm in previous versions + (2) gcc create non-performant code, hence it is disabled in gcc_plugin So all in all this is the best-of AFL that is currently out there :-) diff --git a/TODO b/TODO index f2642b1a..05c89679 100644 --- a/TODO +++ b/TODO @@ -3,6 +3,7 @@ Roadmap 2.60: ============= afl-fuzz: + - change -T to use alarm() instead - radamsa mutator - test the libmutator actually works and does not run infinite (need an example though) diff --git a/docs/perf_tips.txt b/docs/perf_tips.txt index 215895b6..2fa19234 100644 --- a/docs/perf_tips.txt +++ b/docs/perf_tips.txt @@ -50,6 +50,9 @@ Even if you don't have a lightweight harness for a particular target, remember that you can always use another, related library to generate a corpus that will be then manually fed to a more resource-hungry program later on. +Also note that reading the fuzzing input via stdin is faster than reading from +a file. + 3) Use LLVM instrumentation --------------------------- @@ -161,6 +164,11 @@ and not waste CPU time. There are several OS-level factors that may affect fuzzing speed: + - If you have no risk of power loss then run your fuzzing on a tmpfs + partition. This increases the performance noticably. + Alternatively you can use AFL_TMPDIR to point to a tmpfs location to + just write the input file to a tmpfs. + - High system load. Use idle machines where possible. Kill any non-essential CPU hogs (idle browser windows, media players, complex screensavers, etc). diff --git a/gcc_plugin/Makefile b/gcc_plugin/Makefile index a603df78..287b6545 100644 --- a/gcc_plugin/Makefile +++ b/gcc_plugin/Makefile @@ -27,7 +27,7 @@ CFLAGS ?= -O3 -g -funroll-loops CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -Wno-pointer-sign \ -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ -CXXFLAGS ?= -O3 -g -funroll-loops +CXXFLAGS ?= -O3 -g -funroll-loops CXXEFLAGS := $(CXXFLAGS) -Wall -D_FORTIFY_SOURCE=2 CC ?= gcc @@ -35,8 +35,6 @@ CXX ?= g++ PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(shell $(CC) -print-file-name=plugin)/include" -PROGS = ../afl-gcc-fast ../afl-gcc-pass.so ../afl-gcc-rt.o - ifeq "$(shell echo '\#include <sys/ipc.h>@\#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) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 )" "1" SHMAT_OK=1 else @@ -51,6 +49,8 @@ ifeq "$(TEST_MMAP)" "1" LDFLAGS += -lrt endif +PROGS = ../afl-gcc-fast ../afl-gcc-pass.so ../afl-gcc-rt.o + all: test_shm test_deps $(PROGS) afl-gcc-fast.8 test_build all_done diff --git a/gcc_plugin/afl-gcc-fast.c b/gcc_plugin/afl-gcc-fast.c index b0461584..093249a0 100644 --- a/gcc_plugin/afl-gcc-fast.c +++ b/gcc_plugin/afl-gcc-fast.c @@ -282,6 +282,8 @@ int main(int argc, char** argv) { "afl-gcc-fast" VERSION cRST " initially by <aseipp@pobox.com>, maintainer: hexcoder-\n" "\n" + "afl-gcc-fast [options]\n" + "\n" "This is a helper application for afl-fuzz. It serves as a drop-in " "replacement\n" "for gcc, letting you recompile third-party code with the required " diff --git a/gcc_plugin/afl-gcc-rt.o.c b/gcc_plugin/afl-gcc-rt.o.c index dd79a0ec..5b70a247 100644 --- a/gcc_plugin/afl-gcc-rt.o.c +++ b/gcc_plugin/afl-gcc-rt.o.c @@ -21,12 +21,16 @@ */ +#ifdef __ANDROID__ +#include "android-ashmem.h" +#endif #include "../config.h" #include "../types.h" #include <stdlib.h> #include <signal.h> #include <unistd.h> +#include <string.h> #include <assert.h> #include <sys/mman.h> @@ -34,29 +38,43 @@ #include <sys/wait.h> #include <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> + /* Globals needed by the injected instrumentation. The __afl_area_initial region 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]; u8 *__afl_area_ptr = __afl_area_initial; -u32 __afl_prev_loc; -/* Running in persistent mode? */ - -static u8 is_persistent; +#ifdef __ANDROID__ +u32 __afl_prev_loc; +#else +__thread u32 __afl_prev_loc; +#endif /* Trace a basic block with some ID */ void __afl_trace(u32 x) { u32 l = __afl_prev_loc; - u32 n = l ^ x; - *(__afl_area_ptr + n) += 1; + +#if 0 /* enable for neverZero feature. By default disabled since too inefficient :-( */ + /* @Marc: avoid conditional jumps here */ + __afl_area_ptr[l ^ x] += 1 + (__afl_area_ptr[l ^ x] == (u8)~0); +#else + ++__afl_area_ptr[l ^ x]; +#endif + __afl_prev_loc = (x >> 1); return; } +/* Running in persistent mode? */ + +static u8 is_persistent; + /* SHM setup. */ static void __afl_map_shm(void) { @@ -69,9 +87,38 @@ static void __afl_map_shm(void) { if (id_str) { +#ifdef USEMMAP + const char* shm_file_path = id_str; + int shm_fd = -1; + unsigned char* shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + printf("shm_open() failed\n"); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + printf("mmap() failed\n"); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else u32 shm_id = atoi(id_str); __afl_area_ptr = shmat(shm_id, NULL, 0); +#endif /* Whooooops. */ @@ -95,6 +142,8 @@ static void __afl_start_forkserver(void) { u8 child_stopped = 0; + void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); + /* Phone home and tell the parent that we're OK. If parent isn't there, assume we're not running in forkserver mode and just execute program. */ @@ -131,6 +180,8 @@ static void __afl_start_forkserver(void) { if (!child_pid) { + signal(SIGCHLD, old_sigchld_handler); + close(FORKSRV_FD); close(FORKSRV_FD + 1); return; @@ -176,18 +227,47 @@ int __afl_persistent_loop(unsigned int max_cnt) { if (first_pass) { + /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate. + On subsequent calls, the parent will take care of that, but on the first + iteration, it's our job to erase any trace of whatever happened + before the loop. */ + + if (is_persistent) { + + memset(__afl_area_ptr, 0, MAP_SIZE); + __afl_area_ptr[0] = 1; + __afl_prev_loc = 0; + + } + cycle_cnt = max_cnt; first_pass = 0; return 1; } - if (is_persistent && --cycle_cnt) { + if (is_persistent) { - raise(SIGSTOP); - return 1; + if (--cycle_cnt) { - } else + raise(SIGSTOP); + + __afl_area_ptr[0] = 1; + __afl_prev_loc = 0; + + return 1; + + } else { + + /* When exiting __AFL_LOOP(), make sure that the subsequent code that + follows the loop is not traced. We do that by pivoting back to the + dummy output region. */ + + __afl_area_ptr = __afl_area_initial; + + } + + } return 0; diff --git a/libdislocator/libdislocator.so.c b/libdislocator/libdislocator.so.c index f3f02c8b..7f44071a 100644 --- a/libdislocator/libdislocator.so.c +++ b/libdislocator/libdislocator.so.c @@ -88,7 +88,7 @@ static u8 alloc_verbose, /* Additional debug messages */ hard_fail, /* abort() when max_mem exceeded? */ no_calloc_over; /* abort() on calloc() overflows? */ -#ifdef __OpenBSD__ +#if defined __OpenBSD__ || defined __APPLE__ #define __thread #warning no thread support available #endif @@ -121,7 +121,7 @@ static void* __dislocator_alloc(size_t len) { ret = mmap(NULL, (1 + PG_COUNT(len + 8)) * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (ret == (void*)-1) { + if (ret == MAP_FAILED) { if (hard_fail) FATAL("mmap() failed on alloc (OOM?)"); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bb342112..877df0d3 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -71,7 +71,9 @@ static void usage(u8* argv0) { " -V seconds - fuzz for a maximum total time of seconds then " "terminate\n" " -E execs - fuzz for a maximum number of total executions then " - "terminate\n\n" + "terminate\n" + " Note: -V/-E are not precise, they are checked after a queue entry " + "is done\n which can be many minutes/execs later\n\n" "Other stuff:\n" " -T text - text banner to show on the screen\n" diff --git a/test/test-performance.sh b/test/test-performance.sh index 198b58c4..87eea665 100755 --- a/test/test-performance.sh +++ b/test/test-performance.sh @@ -1,6 +1,10 @@ #!/bin/bash -FILE=~/.afl_performance +# if you want a specific performance file (e.g. to compare features to another) +# you can set the AFL_PERFORMANCE_FILE environment variable: +FILE=$AFL_PERFORMANCE_FILE +# otherwise we use ~/.afl_performance +test -z "$FILE" && FILE=~/.afl_performance test -e $FILE || { echo Warning: This script measure the performance of afl++ and saves the result for future comparisons into $FILE @@ -17,6 +21,7 @@ unset AFL_USE_ASAN unset AFL_USE_MSAN unset AFL_CC unset AFL_PRELOAD +unset AFL_GCC_WHITELIST unset AFL_LLVM_WHITELIST unset AFL_LLVM_INSTRIM unset AFL_LLVM_LAF_SPLIT_SWITCHES diff --git a/test/test.sh b/test/test.sh index d1be014f..da0590ef 100755 --- a/test/test.sh +++ b/test/test.sh @@ -33,6 +33,7 @@ unset AFL_USE_ASAN unset AFL_USE_MSAN unset AFL_CC unset AFL_PRELOAD +unset AFL_GCC_WHITELIST unset AFL_LLVM_WHITELIST unset AFL_LLVM_INSTRIM unset AFL_LLVM_LAF_SPLIT_SWITCHES @@ -281,8 +282,8 @@ test -e ../libtokencap.so && { test -e ../libdislocator.so && { { ulimit -c 1 - # DYLD_INSERT_LIBRARIES is used on Darwin/MacOSX - LD_PRELOAD=../libdislocator.so DYLD_INSERT_LIBRARIES=../libdislocator.so ./test-compcov BUFFEROVERFLOW > test.out 2> /dev/null + # DYLD_INSERT_LIBRARIES and DYLD_FORCE_FLAT_NAMESPACE is used on Darwin/MacOSX + LD_PRELOAD=../libdislocator.so DYLD_INSERT_LIBRARIES=../libdislocator.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov BUFFEROVERFLOW > test.out 2> /dev/null } > /dev/null 2>&1 grep -q BUFFEROVERFLOW test.out > /dev/null 2>&1 && { $ECHO "$RED[!] libdislocator did not detect the memory corruption" |