From aa3253ddbf102a1e9428cb3dff1e3ef2c3292eeb Mon Sep 17 00:00:00 2001 From: liangdzou Date: Tue, 28 Mar 2017 14:56:55 +0800 Subject: [PATCH 1/2] Updated to afl-2.39b --- Makefile | 43 +- afl-analyze.c | 1063 +++++++++++++++++ afl-as.h | 6 +- afl-cmin | 14 +- afl-fuzz.c | 927 +++++++++----- afl-gcc.c | 36 +- afl-gotcpu.c | 142 ++- afl-showmap.c | 123 +- afl-tmin.c | 151 ++- afl-whatsup | 12 +- config.h | 25 +- debug.h | 65 +- dictionaries/README.dictionaries | 43 + {testcases/_extras => dictionaries}/gif.dict | 0 .../_extras => dictionaries}/html_tags.dict | 0 {testcases/_extras => dictionaries}/jpeg.dict | 0 {testcases/_extras => dictionaries}/js.dict | 0 {testcases/_extras => dictionaries}/pdf.dict | 0 {testcases/_extras => dictionaries}/png.dict | 0 {testcases/_extras => dictionaries}/sql.dict | 0 {testcases/_extras => dictionaries}/tiff.dict | 0 {testcases/_extras => dictionaries}/webp.dict | 0 {testcases/_extras => dictionaries}/xml.dict | 0 docs/ChangeLog | 476 +++++++- docs/INSTALL | 41 +- docs/QuickStartGuide.txt | 5 +- docs/README | 210 ++-- docs/env_variables.txt | 68 +- docs/life_pro_tips.txt | 128 ++ docs/notes_for_asan.txt | 4 +- docs/parallel_fuzzing.txt | 32 +- docs/perf_tips.txt | 11 +- docs/sister_projects.txt | 90 +- docs/status_screen.txt | 79 +- docs/technical_details.txt | 149 ++- experimental/README.experiments | 4 - .../distributed_fuzzing/sync_script.sh | 4 +- .../instrumented_cmp/instrumented_cmp.c | 69 -- experimental/post_library/post_library.so.c | 4 +- .../post_library/post_library_png.so.c | 2 +- hash.h | 10 + libdislocator/Makefile | 38 + libdislocator/README.dislocator | 60 + libdislocator/libdislocator.so.c | 258 ++++ libtokencap/Makefile | 38 + libtokencap/README.tokencap | 60 + libtokencap/libtokencap.so.c | 253 ++++ llvm_mode/Makefile | 43 +- llvm_mode/README.llvm | 38 +- llvm_mode/afl-clang-fast.c | 105 +- llvm_mode/afl-llvm-pass.so.cc | 26 +- llvm_mode/afl-llvm-rt.o.c | 104 +- qemu_mode/README.qemu | 5 + qemu_mode/build_qemu_support.sh | 46 +- qemu_mode/patches/afl-qemu-cpu-inl.h | 12 +- testcases/README.testcases | 64 +- types.h | 5 +- 57 files changed, 4288 insertions(+), 903 deletions(-) create mode 100644 afl-analyze.c create mode 100644 dictionaries/README.dictionaries rename {testcases/_extras => dictionaries}/gif.dict (100%) rename {testcases/_extras => dictionaries}/html_tags.dict (100%) rename {testcases/_extras => dictionaries}/jpeg.dict (100%) rename {testcases/_extras => dictionaries}/js.dict (100%) rename {testcases/_extras => dictionaries}/pdf.dict (100%) rename {testcases/_extras => dictionaries}/png.dict (100%) rename {testcases/_extras => dictionaries}/sql.dict (100%) rename {testcases/_extras => dictionaries}/tiff.dict (100%) rename {testcases/_extras => dictionaries}/webp.dict (100%) rename {testcases/_extras => dictionaries}/xml.dict (100%) create mode 100644 docs/life_pro_tips.txt delete mode 100644 experimental/instrumented_cmp/instrumented_cmp.c create mode 100644 libdislocator/Makefile create mode 100644 libdislocator/README.dislocator create mode 100644 libdislocator/libdislocator.so.c create mode 100644 libtokencap/Makefile create mode 100644 libtokencap/README.tokencap create mode 100644 libtokencap/libtokencap.so.c diff --git a/Makefile b/Makefile index 4012893..44d1ffa 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # # Written and maintained by Michal Zalewski # -# Copyright 2013, 2014, 2015 Google Inc. All rights reserved. +# Copyright 2013, 2014, 2015, 2016 Google Inc. 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. @@ -14,7 +14,7 @@ # PROGNAME = afl -VERSION = 1.95b +VERSION = $(shell grep '^\#define VERSION ' config.h | cut -d '"' -f2) PREFIX ?= /usr/local BIN_PATH = $(PREFIX)/bin @@ -22,12 +22,15 @@ HELPER_PATH = $(PREFIX)/lib/afl DOC_PATH = $(PREFIX)/share/doc/afl MISC_PATH = $(PREFIX)/share/afl -PROGS = afl-gcc afl-as afl-fuzz afl-showmap afl-tmin afl-gotcpu +# PROGS intentionally omit afl-as, which gets installed elsewhere. + +PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze +SH_PROGS = afl-plot afl-cmin afl-whatsup CFLAGS ?= -O3 -funroll-loops CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \ -DAFL_PATH=\"$(HELPER_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" \ - -DBIN_PATH=\"$(BIN_PATH)\" -DVERSION=\"$(VERSION)\" + -DBIN_PATH=\"$(BIN_PATH)\" ifneq "$(filter Linux GNU%,$(shell uname))" "" LDFLAGS += -ldl @@ -41,20 +44,20 @@ endif COMM_HDR = alloc-inl.h config.h debug.h types.h -all: test_x86 $(PROGS) test_build all_done +all: test_x86 $(PROGS) afl-as test_build all_done -ifndef AFL_NOX86 +ifndef AFL_NO_X86 test_x86: @echo "[*] Checking for the ability to compile x86 code..." - @echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) -w -x c - -o .test || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "You can still try using the LLVM or QEMU mode, but see docs/INSTALL first."; echo "To ignore this error, set AFL_NOX86=1."; echo; exit 1 ) + @echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) -w -x c - -o .test || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 ) @rm -f .test @echo "[+] Everything seems to be working, ready to compile." else test_x86: - @echo "[!] Note: skipping x86 compilation checks (AFL_NOX86 set)." + @echo "[!] Note: skipping x86 compilation checks (AFL_NO_X86 set)." endif @@ -63,7 +66,7 @@ afl-gcc: afl-gcc.c $(COMM_HDR) | test_x86 set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done afl-as: afl-as.c afl-as.h $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) ln -sf afl-as as afl-fuzz: afl-fuzz.c $(COMM_HDR) | test_x86 @@ -75,10 +78,13 @@ afl-showmap: afl-showmap.c $(COMM_HDR) | test_x86 afl-tmin: afl-tmin.c $(COMM_HDR) | test_x86 $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) +afl-analyze: afl-analyze.c $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) + afl-gotcpu: afl-gotcpu.c $(COMM_HDR) | test_x86 $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) -ifndef AFL_NOX86 +ifndef AFL_NO_X86 test_build: afl-gcc afl-as afl-showmap @echo "[*] Testing the CC wrapper and instrumentation output..." @@ -97,6 +103,7 @@ test_build: afl-gcc afl-as afl-showmap endif all_done: test_build + @if [ ! "`which clang 2>/dev/null`" = "" ]; then echo "[+] LLVM users: see llvm_mode/README.llvm for a faster alternative to afl-gcc."; fi @echo "[+] All done! Be sure to review README - it's pretty short and useful." @if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi @! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.txt for advice.\033[0m\n" 2>/dev/null @@ -104,25 +111,35 @@ all_done: test_build .NOTPARALLEL: clean clean: - rm -f $(PROGS) as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test test-instr .test-instr0 .test-instr1 qemu_mode/qemu-2.3.0.tar.bz2 afl-qemu-trace + rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test test-instr .test-instr0 .test-instr1 qemu_mode/qemu-2.3.0.tar.bz2 afl-qemu-trace rm -rf out_dir qemu_mode/qemu-2.3.0 $(MAKE) -C llvm_mode clean + $(MAKE) -C libdislocator clean + $(MAKE) -C libtokencap clean install: all mkdir -p -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh - install -m 755 afl-gcc afl-fuzz afl-showmap afl-plot afl-tmin afl-cmin afl-gotcpu afl-whatsup $${DESTDIR}$(BIN_PATH) + install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH) + rm -f $${DESTDIR}$(BIN_PATH)/afl-as if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi +ifndef AFL_TRACE_PC if [ -f afl-clang-fast -a -f afl-llvm-pass.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 afl-llvm-pass.so afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi +else + if [ -f afl-clang-fast -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 afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi +endif + 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 set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/$$i; done install -m 755 afl-as $${DESTDIR}$(HELPER_PATH) ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as install -m 644 docs/README docs/ChangeLog docs/*.txt $${DESTDIR}$(DOC_PATH) cp -r testcases/ $${DESTDIR}$(MISC_PATH) + cp -r dictionaries/ $${DESTDIR}$(MISC_PATH) publish: clean test "`basename $$PWD`" = "afl" || exit 1 - test -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz; if [ "$$?" = "0" ]; then echo; echo "Change program version in Makefile, mmkay?"; echo; exit 1; fi + test -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz; if [ "$$?" = "0" ]; then echo; echo "Change program version in config.h, mmkay?"; echo; exit 1; fi cd ..; rm -rf $(PROGNAME)-$(VERSION); cp -pr $(PROGNAME) $(PROGNAME)-$(VERSION); \ tar -cvz -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz $(PROGNAME)-$(VERSION) chmod 644 ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz diff --git a/afl-analyze.c b/afl-analyze.c new file mode 100644 index 0000000..fd0a522 --- /dev/null +++ b/afl-analyze.c @@ -0,0 +1,1063 @@ +/* + american fuzzy lop - file format analyzer + ----------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2016 Google Inc. 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 + + A nifty utility that grabs an input file and takes a stab at explaining + its structure by observing how changes to it affect the execution path. + + If the output scrolls past the edge of the screen, pipe it to 'less -r'. + + */ + +#define AFL_MAIN + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "hash.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static s32 child_pid; /* PID of the tested program */ + +static u8* trace_bits; /* SHM with instrumentation bitmap */ + +static u8 *in_file, /* Analyzer input test case */ + *prog_in, /* Targeted program input file */ + *target_path, /* Path to target binary */ + *doc_path; /* Path to docs */ + +static u8 *in_data; /* Input data for analysis */ + +static u32 in_len, /* Input data length */ + orig_cksum, /* Original checksum */ + total_execs, /* Total number of execs */ + exec_hangs, /* Total number of hangs */ + exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */ + +static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ + +static s32 shm_id, /* ID of the SHM region */ + dev_null_fd = -1; /* FD to /dev/null */ + +static u8 edges_only, /* Ignore hit counts? */ + use_stdin = 1; /* Use stdin for program input? */ + +static volatile u8 + stop_soon, /* Ctrl-C pressed? */ + child_timed_out; /* Child timed out? */ + + +/* Constants used for describing byte behavior. */ + +#define RESP_NONE 0x00 /* Changing byte is a no-op. */ +#define RESP_MINOR 0x01 /* Some changes have no effect. */ +#define RESP_VARIABLE 0x02 /* Changes produce variable paths. */ +#define RESP_FIXED 0x03 /* Changes produce fixed patterns. */ + +#define RESP_LEN 0x04 /* Potential length field */ +#define RESP_CKSUM 0x05 /* Potential checksum */ +#define RESP_SUSPECT 0x06 /* Potential "suspect" blob */ + + +/* Classify tuple counts. This is a slow & naive version, but good enough here. */ + +static u8 count_class_lookup[256] = { + + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 4, + [4 ... 7] = 8, + [8 ... 15] = 16, + [16 ... 31] = 32, + [32 ... 127] = 64, + [128 ... 255] = 128 + +}; + +static void classify_counts(u8* mem) { + + u32 i = MAP_SIZE; + + if (edges_only) { + + while (i--) { + if (*mem) *mem = 1; + mem++; + } + + } else { + + while (i--) { + *mem = count_class_lookup[*mem]; + mem++; + } + + } + +} + + +/* See if any bytes are set in the bitmap. */ + +static inline u8 anything_set(void) { + + u32* ptr = (u32*)trace_bits; + u32 i = (MAP_SIZE >> 2); + + while (i--) if (*(ptr++)) return 1; + + return 0; + +} + + +/* Get rid of shared memory and temp files (atexit handler). */ + +static void remove_shm(void) { + + unlink(prog_in); /* Ignore errors */ + shmctl(shm_id, IPC_RMID, NULL); + +} + + +/* Configure shared memory. */ + +static void setup_shm(void) { + + u8* shm_str; + + shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); + + if (shm_id < 0) PFATAL("shmget() failed"); + + atexit(remove_shm); + + shm_str = alloc_printf("%d", shm_id); + + setenv(SHM_ENV_VAR, shm_str, 1); + + ck_free(shm_str); + + trace_bits = shmat(shm_id, NULL, 0); + + if (!trace_bits) PFATAL("shmat() failed"); + +} + + +/* Read initial file. */ + +static void read_initial_file(void) { + + struct stat st; + s32 fd = open(in_file, O_RDONLY); + + if (fd < 0) PFATAL("Unable to open '%s'", in_file); + + if (fstat(fd, &st) || !st.st_size) + FATAL("Zero-sized input file."); + + if (st.st_size >= TMIN_MAX_FILE) + FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024); + + in_len = st.st_size; + in_data = ck_alloc_nozero(in_len); + + ck_read(fd, in_data, in_len, in_file); + + close(fd); + + OKF("Read %u byte%s from '%s'.", in_len, in_len == 1 ? "" : "s", in_file); + +} + + +/* Write output file. */ + +static s32 write_to_file(u8* path, u8* mem, u32 len) { + + s32 ret; + + unlink(path); /* Ignore errors */ + + ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (ret < 0) PFATAL("Unable to create '%s'", path); + + ck_write(ret, mem, len, path); + + lseek(ret, 0, SEEK_SET); + + return ret; + +} + + +/* Handle timeout signal. */ + +static void handle_timeout(int sig) { + + child_timed_out = 1; + if (child_pid > 0) kill(child_pid, SIGKILL); + +} + + +/* Execute target application. Returns exec checksum, or 0 if program + times out. */ + +static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { + + static struct itimerval it; + int status = 0; + + s32 prog_in_fd; + u32 cksum; + + memset(trace_bits, 0, MAP_SIZE); + MEM_BARRIER(); + + prog_in_fd = write_to_file(prog_in, mem, len); + + child_pid = fork(); + + if (child_pid < 0) PFATAL("fork() failed"); + + if (!child_pid) { + + struct rlimit r; + + if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 || + dup2(dev_null_fd, 1) < 0 || + dup2(dev_null_fd, 2) < 0) { + + *(u32*)trace_bits = EXEC_FAIL_SIG; + PFATAL("dup2() failed"); + + } + + close(dev_null_fd); + close(prog_in_fd); + + if (mem_limit) { + + r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; + +#ifdef RLIMIT_AS + + setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + +#else + + setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ + +#endif /* ^RLIMIT_AS */ + + } + + r.rlim_max = r.rlim_cur = 0; + setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + + execv(target_path, argv); + + *(u32*)trace_bits = EXEC_FAIL_SIG; + exit(0); + + } + + close(prog_in_fd); + + /* Configure timeout, wait for child, cancel timeout. */ + + child_timed_out = 0; + it.it_value.tv_sec = (exec_tmout / 1000); + it.it_value.tv_usec = (exec_tmout % 1000) * 1000; + + setitimer(ITIMER_REAL, &it, NULL); + + if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); + + child_pid = 0; + it.it_value.tv_sec = 0; + it.it_value.tv_usec = 0; + + setitimer(ITIMER_REAL, &it, NULL); + + MEM_BARRIER(); + + /* Clean up bitmap, analyze exit condition, etc. */ + + if (*(u32*)trace_bits == EXEC_FAIL_SIG) + FATAL("Unable to execute '%s'", argv[0]); + + classify_counts(trace_bits); + total_execs++; + + if (stop_soon) { + SAYF(cRST cLRD "\n+++ Analysis aborted by user +++\n" cRST); + exit(1); + } + + /* Always discard inputs that time out. */ + + if (child_timed_out) { + + exec_hangs++; + return 0; + + } + + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + + /* We don't actually care if the target is crashing or not, + except that when it does, the checksum should be different. */ + + if (WIFSIGNALED(status) || + (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) || + (WIFEXITED(status) && WEXITSTATUS(status))) { + + cksum ^= 0xffffffff; + + } + + if (first_run) orig_cksum = cksum; + + return cksum; + +} + + +#ifdef USE_COLOR + +/* Helper function to display a human-readable character. */ + +static void show_char(u8 val) { + + switch (val) { + + case 0 ... 32: + case 127 ... 255: SAYF("#%02x", val); break; + + default: SAYF(" %c ", val); + + } + +} + + +/* Show the legend */ + +static void show_legend(void) { + + SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block " + cBLK bgLGN " 01 " cRST " - suspected length field\n" + " " cBRI bgGRA " 01 " cRST " - superficial content " + cBLK bgYEL " 01 " cRST " - suspected cksum or magic int\n" + " " cBLK bgCYA " 01 " cRST " - critical stream " + cBLK bgLRD " 01 " cRST " - suspected checksummed block\n" + " " cBLK bgMGN " 01 " cRST " - \"magic value\" section\n\n"); + +} + +#endif /* USE_COLOR */ + + +/* Interpret and report a pattern in the input file. */ + +static void dump_hex(u8* buf, u32 len, u8* b_data) { + + u32 i; + + for (i = 0; i < len; i++) { + +#ifdef USE_COLOR + u32 rlen = 1, off; +#else + u32 rlen = 1; +#endif /* ^USE_COLOR */ + + u8 rtype = b_data[i] & 0x0f; + + /* Look ahead to determine the length of run. */ + + while (i + rlen < len && (b_data[i] >> 7) == (b_data[i + rlen] >> 7)) { + + if (rtype < (b_data[i + rlen] & 0x0f)) rtype = b_data[i + rlen] & 0x0f; + rlen++; + + } + + /* Try to do some further classification based on length & value. */ + + if (rtype == RESP_FIXED) { + + switch (rlen) { + + case 2: { + + u16 val = *(u16*)(in_data + i); + + /* Small integers may be length fields. */ + + if (val && (val <= in_len || SWAP16(val) <= in_len)) { + rtype = RESP_LEN; + break; + } + + /* Uniform integers may be checksums. */ + + if (val && abs(in_data[i] - in_data[i + 1]) > 32) { + rtype = RESP_CKSUM; + break; + } + + break; + + } + + case 4: { + + u32 val = *(u32*)(in_data + i); + + /* Small integers may be length fields. */ + + if (val && (val <= in_len || SWAP32(val) <= in_len)) { + rtype = RESP_LEN; + break; + } + + /* Uniform integers may be checksums. */ + + if (val && (in_data[i] >> 7 != in_data[i + 1] >> 7 || + in_data[i] >> 7 != in_data[i + 2] >> 7 || + in_data[i] >> 7 != in_data[i + 3] >> 7)) { + rtype = RESP_CKSUM; + break; + } + + break; + + } + + case 1: case 3: case 5 ... MAX_AUTO_EXTRA - 1: break; + + default: rtype = RESP_SUSPECT; + + } + + } + + /* Print out the entire run. */ + +#ifdef USE_COLOR + + for (off = 0; off < rlen; off++) { + + /* Every 16 digits, display offset. */ + + if (!((i + off) % 16)) { + + if (off) SAYF(cRST cLCY ">"); + SAYF(cRST cGRA "%s[%06u] " cRST, (i + off) ? "\n" : "", i + off); + + } + + switch (rtype) { + + case RESP_NONE: SAYF(cLGR bgGRA); break; + case RESP_MINOR: SAYF(cBRI bgGRA); break; + case RESP_VARIABLE: SAYF(cBLK bgCYA); break; + case RESP_FIXED: SAYF(cBLK bgMGN); break; + case RESP_LEN: SAYF(cBLK bgLGN); break; + case RESP_CKSUM: SAYF(cBLK bgYEL); break; + case RESP_SUSPECT: SAYF(cBLK bgLRD); break; + + } + + show_char(in_data[i + off]); + + if (off != rlen - 1 && (i + off + 1) % 16) SAYF(" "); else SAYF(cRST " "); + + } + +#else + + SAYF(" Offset %u, length %u: ", i, rlen); + + switch (rtype) { + + case RESP_NONE: SAYF("no-op block\n"); break; + case RESP_MINOR: SAYF("superficial content\n"); break; + case RESP_VARIABLE: SAYF("critical stream\n"); break; + case RESP_FIXED: SAYF("\"magic value\" section\n"); break; + case RESP_LEN: SAYF("suspected length field\n"); break; + case RESP_CKSUM: SAYF("suspected cksum or magic int\n"); break; + case RESP_SUSPECT: SAYF("suspected checksummed block\n"); break; + + } + +#endif /* ^USE_COLOR */ + + i += rlen - 1; + + } + +#ifdef USE_COLOR + SAYF(cRST "\n"); +#endif /* USE_COLOR */ + +} + + + +/* Actually analyze! */ + +static void analyze(char** argv) { + + u32 i; + u32 boring_len = 0, prev_xff = 0, prev_x01 = 0, prev_s10 = 0, prev_a10 = 0; + + u8* b_data = ck_alloc(in_len + 1); + u8 seq_byte = 0; + + b_data[in_len] = 0xff; /* Intentional terminator. */ + + ACTF("Analyzing input file (this may take a while)...\n"); + +#ifdef USE_COLOR + show_legend(); +#endif /* USE_COLOR */ + + for (i = 0; i < in_len; i++) { + + u32 xor_ff, xor_01, sub_10, add_10; + u8 xff_orig, x01_orig, s10_orig, a10_orig; + + /* Perform walking byte adjustments across the file. We perform four + operations designed to elicit some response from the underlying + code. */ + + in_data[i] ^= 0xff; + xor_ff = run_target(argv, in_data, in_len, 0); + + in_data[i] ^= 0xfe; + xor_01 = run_target(argv, in_data, in_len, 0); + + in_data[i] = (in_data[i] ^ 0x01) - 0x10; + sub_10 = run_target(argv, in_data, in_len, 0); + + in_data[i] += 0x20; + add_10 = run_target(argv, in_data, in_len, 0); + in_data[i] -= 0x10; + + /* Classify current behavior. */ + + xff_orig = (xor_ff == orig_cksum); + x01_orig = (xor_01 == orig_cksum); + s10_orig = (sub_10 == orig_cksum); + a10_orig = (add_10 == orig_cksum); + + if (xff_orig && x01_orig && s10_orig && a10_orig) { + + b_data[i] = RESP_NONE; + boring_len++; + + } else if (xff_orig || x01_orig || s10_orig || a10_orig) { + + b_data[i] = RESP_MINOR; + boring_len++; + + } else if (xor_ff == xor_01 && xor_ff == sub_10 && xor_ff == add_10) { + + b_data[i] = RESP_FIXED; + + } else b_data[i] = RESP_VARIABLE; + + /* When all checksums change, flip most significant bit of b_data. */ + + if (prev_xff != xor_ff && prev_x01 != xor_01 && + prev_s10 != sub_10 && prev_a10 != add_10) seq_byte ^= 0x80; + + b_data[i] |= seq_byte; + + prev_xff = xor_ff; + prev_x01 = xor_01; + prev_s10 = sub_10; + prev_a10 = add_10; + + } + + dump_hex(in_data, in_len, b_data); + + SAYF("\n"); + + OKF("Analysis complete. Interesting bits: %0.02f%% of the input file.", + 100.0 - ((double)boring_len * 100) / in_len); + + if (exec_hangs) + WARNF(cLRD "Encountered %u timeouts - results may be skewed." cRST, + exec_hangs); + + ck_free(b_data); + +} + + + +/* Handle Ctrl-C and the like. */ + +static void handle_stop_sig(int sig) { + + stop_soon = 1; + + if (child_pid > 0) kill(child_pid, SIGKILL); + +} + + +/* Do basic preparations - persistent fds, filenames, etc. */ + +static void set_up_environment(void) { + + u8* x; + + dev_null_fd = open("/dev/null", O_RDWR); + if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); + + if (!prog_in) { + + u8* use_dir = "."; + + if (!access(use_dir, R_OK | W_OK | X_OK)) { + + use_dir = getenv("TMPDIR"); + if (!use_dir) use_dir = "/tmp"; + + prog_in = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid()); + + } + + } + + /* Set sane defaults... */ + + x = getenv("ASAN_OPTIONS"); + + if (x) { + + if (!strstr(x, "abort_on_error=1")) + FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + + if (!strstr(x, "symbolize=0")) + FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + + } + + x = getenv("MSAN_OPTIONS"); + + if (x) { + + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) + FATAL("Custom MSAN_OPTIONS set without exit_code=" + STRINGIFY(MSAN_ERROR) " - please fix!"); + + if (!strstr(x, "symbolize=0")) + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + + } + + setenv("ASAN_OPTIONS", "abort_on_error=1:" + "detect_leaks=0:" + "symbolize=0:" + "allocator_may_return_null=1", 0); + + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "abort_on_error=1:" + "allocator_may_return_null=1:" + "msan_track_origins=0", 0); + + if (getenv("AFL_PRELOAD")) { + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + } + +} + + +/* Setup signal handlers, duh. */ + +static void setup_signal_handlers(void) { + + struct sigaction sa; + + sa.sa_handler = NULL; + sa.sa_flags = SA_RESTART; + sa.sa_sigaction = NULL; + + sigemptyset(&sa.sa_mask); + + /* Various ways of saying "stop". */ + + sa.sa_handler = handle_stop_sig; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + /* Exec timeout notifications. */ + + sa.sa_handler = handle_timeout; + sigaction(SIGALRM, &sa, NULL); + +} + + +/* Detect @@ in args. */ + +static void detect_file_args(char** argv) { + + u32 i = 0; + u8* cwd = getcwd(NULL, 0); + + if (!cwd) PFATAL("getcwd() failed"); + + while (argv[i]) { + + u8* aa_loc = strstr(argv[i], "@@"); + + if (aa_loc) { + + u8 *aa_subst, *n_arg; + + /* Be sure that we're always using fully-qualified paths. */ + + if (prog_in[0] == '/') aa_subst = prog_in; + else aa_subst = alloc_printf("%s/%s", cwd, prog_in); + + /* Construct a replacement argv value. */ + + *aa_loc = 0; + n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); + argv[i] = n_arg; + *aa_loc = '@'; + + if (prog_in[0] != '/') ck_free(aa_subst); + + } + + i++; + + } + + free(cwd); /* not tracked */ + +} + + +/* Display usage hints. */ + +static void usage(u8* argv0) { + + SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" + + "Required parameters:\n\n" + + " -i file - input test case to be analyzed by the tool\n" + + "Execution control settings:\n\n" + + " -f file - input file read by the tested program (stdin)\n" + " -t msec - timeout for each run (%u ms)\n" + " -m megs - memory limit for child process (%u MB)\n" + " -Q - use binary-only instrumentation (QEMU mode)\n\n" + + "Analysis settings:\n\n" + + " -e - look for edge coverage only, ignore hit counts\n\n" + + "For additional tips, please consult %s/README.\n\n", + + argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); + + exit(1); + +} + + +/* Find binary. */ + +static void find_binary(u8* fname) { + + u8* env_path = 0; + struct stat st; + + if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { + + target_path = ck_strdup(fname); + + if (stat(target_path, &st) || !S_ISREG(st.st_mode) || + !(st.st_mode & 0111) || st.st_size < 4) + FATAL("Program '%s' not found or not executable", fname); + + } else { + + while (env_path) { + + u8 *cur_elem, *delim = strchr(env_path, ':'); + + if (delim) { + + cur_elem = ck_alloc(delim - env_path + 1); + memcpy(cur_elem, env_path, delim - env_path); + delim++; + + } else cur_elem = ck_strdup(env_path); + + env_path = delim; + + if (cur_elem[0]) + target_path = alloc_printf("%s/%s", cur_elem, fname); + else + target_path = ck_strdup(fname); + + ck_free(cur_elem); + + if (!stat(target_path, &st) && S_ISREG(st.st_mode) && + (st.st_mode & 0111) && st.st_size >= 4) break; + + ck_free(target_path); + target_path = 0; + + } + + if (!target_path) FATAL("Program '%s' not found or not executable", fname); + + } + +} + + +/* Fix up argv for QEMU. */ + +static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { + + char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); + u8 *tmp, *cp, *rsl, *own_copy; + + memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); + + /* Now we need to actually find qemu for argv[0]. */ + + new_argv[2] = target_path; + new_argv[1] = "--"; + + tmp = getenv("AFL_PATH"); + + if (tmp) { + + cp = alloc_printf("%s/afl-qemu-trace", tmp); + + if (access(cp, X_OK)) + FATAL("Unable to find '%s'", tmp); + + target_path = new_argv[0] = cp; + return new_argv; + + } + + own_copy = ck_strdup(own_loc); + rsl = strrchr(own_copy, '/'); + + if (rsl) { + + *rsl = 0; + + cp = alloc_printf("%s/afl-qemu-trace", own_copy); + ck_free(own_copy); + + if (!access(cp, X_OK)) { + + target_path = new_argv[0] = cp; + return new_argv; + + } + + } else ck_free(own_copy); + + if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { + + target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; + return new_argv; + + } + + FATAL("Unable to find 'afl-qemu-trace'."); + +} + + +/* Main entry point */ + +int main(int argc, char** argv) { + + s32 opt; + u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0; + char** use_argv; + + doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; + + SAYF(cCYA "afl-analyze " cBRI VERSION cRST " by \n"); + + while ((opt = getopt(argc,argv,"+i:f:m:t:eQ")) > 0) + + switch (opt) { + + case 'i': + + if (in_file) FATAL("Multiple -i options not supported"); + in_file = optarg; + break; + + case 'f': + + if (prog_in) FATAL("Multiple -f options not supported"); + use_stdin = 0; + prog_in = optarg; + break; + + case 'e': + + if (edges_only) FATAL("Multiple -e options not supported"); + edges_only = 1; + break; + + case 'm': { + + u8 suffix = 'M'; + + if (mem_limit_given) FATAL("Multiple -m options not supported"); + mem_limit_given = 1; + + if (!strcmp(optarg, "none")) { + + mem_limit = 0; + break; + + } + + if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || + optarg[0] == '-') FATAL("Bad syntax used for -m"); + + switch (suffix) { + + case 'T': mem_limit *= 1024 * 1024; break; + case 'G': mem_limit *= 1024; break; + case 'k': mem_limit /= 1024; break; + case 'M': break; + + default: FATAL("Unsupported suffix or bad syntax for -m"); + + } + + if (mem_limit < 5) FATAL("Dangerously low value of -m"); + + if (sizeof(rlim_t) == 4 && mem_limit > 2000) + FATAL("Value of -m out of range on 32-bit systems"); + + } + + break; + + case 't': + + if (timeout_given) FATAL("Multiple -t options not supported"); + timeout_given = 1; + + exec_tmout = atoi(optarg); + + if (exec_tmout < 10 || optarg[0] == '-') + FATAL("Dangerously low value of -t"); + + break; + + case 'Q': + + if (qemu_mode) FATAL("Multiple -Q options not supported"); + if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; + + qemu_mode = 1; + break; + + default: + + usage(argv[0]); + + } + + if (optind == argc || !in_file) usage(argv[0]); + + setup_shm(); + setup_signal_handlers(); + + set_up_environment(); + + find_binary(argv[optind]); + detect_file_args(argv + optind); + + if (qemu_mode) + use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); + else + use_argv = argv + optind; + + SAYF("\n"); + + read_initial_file(); + + ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", + mem_limit, exec_tmout, edges_only ? ", edges only" : ""); + + run_target(use_argv, in_data, in_len, 1); + + if (child_timed_out) + FATAL("Target binary times out (adjusting -t may help)."); + + if (!anything_set()) FATAL("No instrumentation detected."); + + analyze(use_argv); + + OKF("We're done here. Have a nice day!\n"); + + exit(0); + +} + diff --git a/afl-as.h b/afl-as.h index 1cca798..ebd5710 100644 --- a/afl-as.h +++ b/afl-as.h @@ -98,7 +98,7 @@ of every .c file. This should have no impact in any practical sense. Another side effect of this design is that getenv() will be called once per - every .o file when running in non-instrumented mode; an since getenv() tends + every .o file when running in non-instrumented mode; and since getenv() tends to be optimized in funny ways, we need to be very careful to save every oddball register it may touch. @@ -181,7 +181,9 @@ static const u8* main_payload_32 = " xorl %ecx, %edi\n" " shrl $1, %ecx\n" " movl %ecx, __afl_prev_loc\n" -#endif /* !COVERAGE_ONLY */ +#else + " movl %ecx, %edi\n" +#endif /* ^!COVERAGE_ONLY */ "\n" #ifdef SKIP_COUNTS " orb $1, (%edx, %edi, 1)\n" diff --git a/afl-cmin b/afl-cmin index 5af4b77..70e5dac 100755 --- a/afl-cmin +++ b/afl-cmin @@ -240,13 +240,23 @@ fi IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`)) if [ "$IN_COUNT" = "0" ]; then - echo "No inputs in the target directory - nothing to be done." + echo "[+] Hmm, no inputs in the target directory. Nothing to be done." rm -rf "$TRACE_DIR" exit 1 fi FIRST_FILE=`ls "$IN_DIR" | head -1` +# Make sure that we're not dealing with a directory. + +if [ -d "$IN_DIR/$FIRST_FILE" ]; then + echo "[-] Error: The target directory contains subdirectories - please fix." 1>&2 + rm -rf "$TRACE_DIR" + exit 1 +fi + +# Check for the more efficient way to copy files... + if ln "$IN_DIR/$FIRST_FILE" "$TRACE_DIR/.link_test" 2>/dev/null; then CP_TOOL=ln else @@ -384,7 +394,7 @@ sort -k1,1 -s -u "$TRACE_DIR/.candidate_list" | \ sed 's/^/BEST_FILE[/;s/ /]="/;s/$/"/' >"$TRACE_DIR/.candidate_script" if [ ! -s "$TRACE_DIR/.candidate_script" ]; then - echo "[-] Error: no traces obtained from test cases, check syntax!" + echo "[-] Error: no traces obtained from test cases, check syntax!" 1>&2 test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR" exit 1 fi diff --git a/afl-fuzz.c b/afl-fuzz.c index 46d1002..f17d473 100644 --- a/afl-fuzz.c +++ b/afl-fuzz.c @@ -6,7 +6,7 @@ Forkserver design by Jann Horn - Copyright 2013, 2014, 2015 Google Inc. All rights reserved. + Copyright 2013, 2014, 2015, 2016 Google Inc. 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. @@ -44,7 +44,9 @@ #include #include #include + #include +#include #include #include @@ -55,6 +57,7 @@ #include #include #include + #include #include #include @@ -63,11 +66,27 @@ # include #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ +/* For systems that have sched_setaffinity; right now just Linux, but one + can hope... */ + +#ifdef __linux__ +# define HAVE_AFFINITY 1 +#endif /* __linux__ */ + +/* A toggle to export some variables when building as a library. Not very + useful for the general public. */ + +#ifdef AFL_LIB +# define EXP_ST +#else +# define EXP_ST static +#endif /* ^AFL_LIB */ /* Lots of globals, but mostly for the status UI and other things where it really makes no sense to haul them around as function parameters. */ -static u8 *in_dir, /* Input directory with test cases */ + +EXP_ST u8 *in_dir, /* Input directory with test cases */ *out_file, /* File to fuzz, if any */ *out_dir, /* Working & output directory */ *sync_dir, /* Synchronization directory */ @@ -78,12 +97,12 @@ static u8 *in_dir, /* Input directory with test cases */ *target_path, /* Path to target binary */ *orig_cmdline; /* Original command line */ -static u32 exec_tmout = EXEC_TIMEOUT; /* Configurable exec timeout (ms) */ -static u64 mem_limit = MEM_LIMIT; /* Memory cap for child (MB) */ +EXP_ST u32 exec_tmout = EXEC_TIMEOUT; /* Configurable exec timeout (ms) */ +EXP_ST u64 mem_limit = MEM_LIMIT; /* Memory cap for child (MB) */ static u32 stats_update_freq = 1; /* Stats update frequency (execs) */ -static u8 skip_deterministic, /* Skip deterministic stages? */ +EXP_ST u8 skip_deterministic, /* Skip deterministic stages? */ force_deterministic, /* Force deterministic stages? */ use_splicing, /* Recombine input files? */ dumb_mode, /* Run in non-instrumented mode? */ @@ -99,11 +118,12 @@ static u8 skip_deterministic, /* Skip deterministic stages? */ in_place_resume, /* Attempt in-place resume? */ auto_changed, /* Auto-generated tokens changed? */ no_cpu_meter_red, /* Feng shui on the status screen */ - no_var_check, /* Don't detect variable behavior */ + shuffle_queue, /* Shuffle input queue? */ bitmap_changed = 1, /* Time to update bitmap? */ qemu_mode, /* Running in QEMU mode? */ skip_requested, /* Skip request, via SIGUSR1 */ - run_over10m; /* Run time over 10 minutes? */ + run_over10m, /* Run time over 10 minutes? */ + persistent_mode; /* Running in persistent mode? */ static s32 out_fd, /* Persistent fd for out_file */ dev_urandom_fd = -1, /* Persistent fd for /dev/urandom */ @@ -115,19 +135,21 @@ static s32 forksrv_pid, /* PID of the fork server */ child_pid = -1, /* PID of the fuzzed program */ out_dir_fd = -1; /* FD of the lock file */ -static u8* trace_bits; /* SHM with instrumentation bitmap */ +EXP_ST u8* trace_bits; /* SHM with instrumentation bitmap */ -static u8 virgin_bits[MAP_SIZE], /* Regions yet untouched by fuzzing */ +EXP_ST u8 virgin_bits[MAP_SIZE], /* Regions yet untouched by fuzzing */ virgin_hang[MAP_SIZE], /* Bits we haven't seen in hangs */ virgin_crash[MAP_SIZE]; /* Bits we haven't seen in crashes */ +static u8 var_bytes[MAP_SIZE]; /* Bytes that appear to be variable */ + static s32 shm_id; /* ID of the SHM region */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ clear_screen = 1, /* Window resized? */ child_timed_out; /* Traced process timed out? */ -static u32 queued_paths, /* Total number of queued testcases */ +EXP_ST u32 queued_paths, /* Total number of queued testcases */ queued_variable, /* Testcases with variable behavior */ queued_at_start, /* Total number of initial inputs */ queued_discovered, /* Items discovered during this run */ @@ -140,10 +162,11 @@ static u32 queued_paths, /* Total number of queued testcases */ cur_depth, /* Current path depth */ max_depth, /* Max path depth */ useless_at_start, /* Number of useless starting paths */ + var_byte_count, /* Bitmap bytes with var behavior */ current_entry, /* Current queue entry ID */ havoc_div = 1; /* Cycle count divisor for havoc */ -static u64 total_crashes, /* Total number of crashes */ +EXP_ST u64 total_crashes, /* Total number of crashes */ unique_crashes, /* Crashes with unique signatures */ total_hangs, /* Total number of hangs */ unique_hangs, /* Hangs with unique signatures */ @@ -152,6 +175,7 @@ static u64 total_crashes, /* Total number of crashes */ last_path_time, /* Time for most recent path (ms) */ last_crash_time, /* Time for most recent crash (ms) */ last_hang_time, /* Time for most recent hang (ms) */ + last_crash_execs, /* Exec counter at last crash */ queue_cycle, /* Queue round counter */ cycles_wo_finds, /* Cycles without any new paths */ trim_execs, /* Execs done to trim input files */ @@ -169,6 +193,8 @@ static u8 *stage_name = "init", /* Name of the current fuzz stage */ static s32 stage_cur, stage_max; /* Stage progression */ static s32 splicing_with = -1; /* Splicing with which test case? */ +static u32 master_id, master_max; /* Master instance job splitting */ + static u32 syncing_case; /* Syncing with case #... */ static s32 stage_cur_byte, /* Byte offset of current stage op */ @@ -187,7 +213,13 @@ static u64 total_cal_us, /* Total calibration time (us) */ static u64 total_bitmap_size, /* Total bit count for all bitmaps */ total_bitmap_entries; /* Number of bitmaps counted */ -static u32 cpu_core_count; /* CPU core count */ +static s32 cpu_core_count; /* CPU core count */ + +#ifdef HAVE_AFFINITY + +static s32 cpu_aff = -1; /* Selected CPU core */ + +#endif /* HAVE_AFFINITY */ static FILE* plot_file; /* Gnuplot output file */ @@ -346,7 +378,7 @@ static u64 get_cur_time_us(void) { static inline u32 UR(u32 limit) { - if (!rand_cnt--) { + if (unlikely(!rand_cnt--)) { u32 seed[2]; @@ -362,6 +394,140 @@ static inline u32 UR(u32 limit) { } +/* Shuffle an array of pointers. Might be slightly biased. */ + +static void shuffle_ptrs(void** ptrs, u32 cnt) { + + u32 i; + + for (i = 0; i < cnt - 2; i++) { + + u32 j = i + UR(cnt - i); + void *s = ptrs[i]; + ptrs[i] = ptrs[j]; + ptrs[j] = s; + + } + +} + + +#ifdef HAVE_AFFINITY + +/* Build a list of processes bound to specific cores. Returns -1 if nothing + can be found. Assumes an upper bound of 4k CPUs. */ + +static void bind_to_free_cpu(void) { + + DIR* d; + struct dirent* de; + cpu_set_t c; + + u8 cpu_used[4096] = { 0 }; + u32 i; + + if (cpu_core_count < 2) return; + + if (getenv("AFL_NO_AFFINITY")) { + + WARNF("Not binding to a CPU core (AFL_NO_AFFINITY set)."); + return; + + } + + d = opendir("/proc"); + + if (!d) { + + WARNF("Unable to access /proc - can't scan for free CPU cores."); + return; + + } + + ACTF("Checking CPU core loadout..."); + + /* Introduce some jitter, in case multiple AFL tasks are doing the same + thing at the same time... */ + + usleep(R(1000) * 250); + + /* Scan all /proc//status entries, checking for Cpus_allowed_list. + Flag all processes bound to a specific CPU using cpu_used[]. This will + fail for some exotic binding setups, but is likely good enough in almost + all real-world use cases. */ + + while ((de = readdir(d))) { + + u8* fn; + FILE* f; + u8 tmp[MAX_LINE]; + u8 has_vmsize = 0; + + if (!isdigit(de->d_name[0])) continue; + + fn = alloc_printf("/proc/%s/status", de->d_name); + + if (!(f = fopen(fn, "r"))) { + ck_free(fn); + continue; + } + + while (fgets(tmp, MAX_LINE, f)) { + + u32 hval; + + /* Processes without VmSize are probably kernel tasks. */ + + if (!strncmp(tmp, "VmSize:\t", 8)) has_vmsize = 1; + + if (!strncmp(tmp, "Cpus_allowed_list:\t", 19) && + !strchr(tmp, '-') && !strchr(tmp, ',') && + sscanf(tmp + 19, "%u", &hval) == 1 && hval < sizeof(cpu_used) && + has_vmsize) { + + cpu_used[hval] = 1; + break; + + } + + } + + ck_free(fn); + fclose(f); + + } + + closedir(d); + + for (i = 0; i < cpu_core_count; i++) if (!cpu_used[i]) break; + + if (i == cpu_core_count) { + + SAYF("\n" cLRD "[-] " cRST + "Uh-oh, looks like all %u CPU cores on your system are allocated to\n" + " other instances of afl-fuzz (or similar CPU-locked tasks). Starting\n" + " another fuzzer on this machine is probably a bad plan, but if you are\n" + " absolutely sure, you can set AFL_NO_AFFINITY and try again.\n", + cpu_core_count); + + FATAL("No more free CPU cores"); + + } + + OKF("Found a free CPU core, binding to #%u.", i); + + cpu_aff = i; + + CPU_ZERO(&c); + CPU_SET(i, &c); + + if (sched_setaffinity(0, sizeof(c), &c)) + PFATAL("sched_setaffinity failed"); + +} + +#endif /* HAVE_AFFINITY */ + #ifndef IGNORE_FINDS /* Helper function to compare buffers; returns first and last differing offset. We @@ -651,6 +817,8 @@ static void add_to_queue(u8* fname, u32 len, u8 passed_det) { queued_paths++; pending_not_fuzzed++; + cycles_wo_finds = 0; + if (!(queued_paths % 100)) { q_prev100->next_100 = q; @@ -665,7 +833,7 @@ static void add_to_queue(u8* fname, u32 len, u8 passed_det) { /* Destroy the entire queue. */ -static void destroy_queue(void) { +EXP_ST void destroy_queue(void) { struct queue_entry *q = queue, *n; @@ -686,7 +854,7 @@ static void destroy_queue(void) { -B option, to focus a separate fuzzing session on a particular interesting input without rediscovering all the others. */ -static void write_bitmap(void) { +EXP_ST void write_bitmap(void) { u8* fname; s32 fd; @@ -709,7 +877,7 @@ static void write_bitmap(void) { /* Read bitmap from file. This is for the -B option again. */ -static void read_bitmap(u8* fname) { +EXP_ST void read_bitmap(u8* fname) { s32 fd = open(fname, O_RDONLY); @@ -730,9 +898,6 @@ static void read_bitmap(u8* fname) { This function is called after every exec() on a fairly large buffer, so it needs to be fast. We do this in 32-bit and 64-bit flavors. */ -#define FFL(_b) (0xffULL << ((_b) << 3)) -#define FF(_b) (0xff << ((_b) << 3)) - static inline u8 has_new_bits(u8* virgin_map) { #ifdef __x86_64__ @@ -755,53 +920,39 @@ static inline u8 has_new_bits(u8* virgin_map) { while (i--) { -#ifdef __x86_64__ - - u64 cur = *current; - u64 vir = *virgin; - -#else - - u32 cur = *current; - u32 vir = *virgin; - -#endif /* ^__x86_64__ */ + /* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap + that have not been already cleared from the virgin map - since this will + almost always be the case. */ - /* Optimize for *current == ~*virgin, since this will almost always be the - case. */ + if (unlikely(*current) && unlikely(*current & *virgin)) { - if (cur & vir) { + if (likely(ret < 2)) { - if (ret < 2) { + u8* cur = (u8*)current; + u8* vir = (u8*)virgin; - /* This trace did not have any new bytes yet; see if there's any - current[] byte that is non-zero when virgin[] is 0xff. */ + /* Looks like we have not found any new bytes yet; see if any non-zero + bytes in current[] are pristine in virgin[]. */ #ifdef __x86_64__ - if (((cur & FFL(0)) && (vir & FFL(0)) == FFL(0)) || - ((cur & FFL(1)) && (vir & FFL(1)) == FFL(1)) || - ((cur & FFL(2)) && (vir & FFL(2)) == FFL(2)) || - ((cur & FFL(3)) && (vir & FFL(3)) == FFL(3)) || - ((cur & FFL(4)) && (vir & FFL(4)) == FFL(4)) || - ((cur & FFL(5)) && (vir & FFL(5)) == FFL(5)) || - ((cur & FFL(6)) && (vir & FFL(6)) == FFL(6)) || - ((cur & FFL(7)) && (vir & FFL(7)) == FFL(7))) ret = 2; + if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) || + (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff) || + (cur[4] && vir[4] == 0xff) || (cur[5] && vir[5] == 0xff) || + (cur[6] && vir[6] == 0xff) || (cur[7] && vir[7] == 0xff)) ret = 2; else ret = 1; #else - if (((cur & FF(0)) && (vir & FF(0)) == FF(0)) || - ((cur & FF(1)) && (vir & FF(1)) == FF(1)) || - ((cur & FF(2)) && (vir & FF(2)) == FF(2)) || - ((cur & FF(3)) && (vir & FF(3)) == FF(3))) ret = 2; + if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) || + (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff)) ret = 2; else ret = 1; #endif /* ^__x86_64__ */ } - *virgin = vir & ~cur; + *virgin &= ~*current; } @@ -849,6 +1000,8 @@ static u32 count_bits(u8* mem) { } +#define FF(_b) (0xff << ((_b) << 3)) + /* Count the number of bytes set in the bitmap. Called fairly sporadically, mostly to update the status screen or calibrate and examine confirmed new paths. */ @@ -910,21 +1063,11 @@ static u32 count_non_255_bytes(u8* mem) { is hit or not. Called on every new crash or hang, should be reasonably fast. */ -#define AREP4(_sym) (_sym), (_sym), (_sym), (_sym) -#define AREP8(_sym) AREP4(_sym), AREP4(_sym) -#define AREP16(_sym) AREP8(_sym), AREP8(_sym) -#define AREP32(_sym) AREP16(_sym), AREP16(_sym) -#define AREP64(_sym) AREP32(_sym), AREP32(_sym) -#define AREP128(_sym) AREP64(_sym), AREP64(_sym) - -static u8 simplify_lookup[256] = { - /* 4 */ 1, 128, 128, 128, - /* +4 */ AREP4(128), - /* +8 */ AREP8(128), - /* +16 */ AREP16(128), - /* +32 */ AREP32(128), - /* +64 */ AREP64(128), - /* +128 */ AREP128(128) +static const u8 simplify_lookup[256] = { + + [0] = 1, + [1 ... 255] = 128 + }; #ifdef __x86_64__ @@ -937,7 +1080,7 @@ static void simplify_trace(u64* mem) { /* Optimize for sparse bitmaps. */ - if (*mem) { + if (unlikely(*mem)) { u8* mem8 = (u8*)mem; @@ -968,7 +1111,7 @@ static void simplify_trace(u32* mem) { /* Optimize for sparse bitmaps. */ - if (*mem) { + if (unlikely(*mem)) { u8* mem8 = (u8*)mem; @@ -991,17 +1134,36 @@ static void simplify_trace(u32* mem) { preprocessing step for any newly acquired traces. Called on every exec, must be fast. */ -static u8 count_class_lookup[256] = { +static const u8 count_class_lookup8[256] = { - /* 0 - 3: 4 */ 0, 1, 2, 4, - /* 4 - 7: +4 */ AREP4(8), - /* 8 - 15: +8 */ AREP8(16), - /* 16 - 31: +16 */ AREP16(32), - /* 32 - 127: +96 */ AREP64(64), AREP32(64), - /* 128+: +128 */ AREP128(128) + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 4, + [4 ... 7] = 8, + [8 ... 15] = 16, + [16 ... 31] = 32, + [32 ... 127] = 64, + [128 ... 255] = 128 }; +static u16 count_class_lookup16[65536]; + + +static void init_count_class16(void) { + + u32 b1, b2; + + for (b1 = 0; b1 < 256; b1++) + for (b2 = 0; b2 < 256; b2++) + count_class_lookup16[(b1 << 8) + b2] = + (count_class_lookup8[b1] << 8) | + count_class_lookup8[b2]; + +} + + #ifdef __x86_64__ static inline void classify_counts(u64* mem) { @@ -1012,18 +1174,14 @@ static inline void classify_counts(u64* mem) { /* Optimize for sparse bitmaps. */ - if (*mem) { + if (unlikely(*mem)) { - u8* mem8 = (u8*)mem; + u16* mem16 = (u16*)mem; - mem8[0] = count_class_lookup[mem8[0]]; - mem8[1] = count_class_lookup[mem8[1]]; - mem8[2] = count_class_lookup[mem8[2]]; - mem8[3] = count_class_lookup[mem8[3]]; - mem8[4] = count_class_lookup[mem8[4]]; - mem8[5] = count_class_lookup[mem8[5]]; - mem8[6] = count_class_lookup[mem8[6]]; - mem8[7] = count_class_lookup[mem8[7]]; + mem16[0] = count_class_lookup16[mem16[0]]; + mem16[1] = count_class_lookup16[mem16[1]]; + mem16[2] = count_class_lookup16[mem16[2]]; + mem16[3] = count_class_lookup16[mem16[3]]; } @@ -1043,14 +1201,12 @@ static inline void classify_counts(u32* mem) { /* Optimize for sparse bitmaps. */ - if (*mem) { + if (unlikely(*mem)) { - u8* mem8 = (u8*)mem; + u16* mem16 = (u16*)mem; - mem8[0] = count_class_lookup[mem8[0]]; - mem8[1] = count_class_lookup[mem8[1]]; - mem8[2] = count_class_lookup[mem8[2]]; - mem8[3] = count_class_lookup[mem8[3]]; + mem16[0] = count_class_lookup16[mem16[0]]; + mem16[1] = count_class_lookup16[mem16[1]]; } @@ -1206,7 +1362,7 @@ static void cull_queue(void) { /* Configure shared memory and virgin_bits. This is called at startup. */ -static void setup_shm(void) { +EXP_ST void setup_shm(void) { u8* shm_str; @@ -1303,6 +1459,13 @@ static void read_testcases(void) { } + if (shuffle_queue && nl_cnt > 1) { + + ACTF("Shuffling queue..."); + shuffle_ptrs((void**)nl, nl_cnt); + + } + for (i = 0; i < nl_cnt; i++) { struct stat st; @@ -1550,7 +1713,7 @@ static void load_extras(u8* dir) { } - if (x) FATAL("Dictinary levels not supported for directories."); + if (x) FATAL("Dictionary levels not supported for directories."); while ((de = readdir(d))) { @@ -1824,12 +1987,13 @@ static void destroy_extras(void) { } + /* Code to fuzz targets across localhost/127.0.0.1/::1 network interface - * + * * The network fuzzing code operates in each of two modes depending upon * the type of target: - * - * (1) as a "listener" or "server" to fuzz targets that send a request to + * + * (1) as a "listener" or "server" to fuzz targets that send a request to * another process and expect a response. These targets are called * "clients". The relevant functions are network_setup_listener(), * which creates a socket and binds that socket to a (local) port @@ -1837,14 +2001,14 @@ static void destroy_extras(void) { * to receive a packet (UDP) or stream of data (TCP) from the target * and sends a fuzzed response. This mode is selected using the -L * command line option, together with the -N command line option. - * + * * (2) as a "client" to fuzz targets that expect to receive a request from * another process. These targets are called "servers" or "daemons". * The relevant function is network_send(), which sends a fuzzed * packet (UDP) or stream of data (TCP) to the target. This mode is * selected using the -N command line option without the -L command * line option. - * + * * */ void network_setup_listener(void) { @@ -1854,16 +2018,16 @@ void network_setup_listener(void) { /* two cases: SOCK_STREAM (for TCP) and SOCK_DGRAM (for UDP) */ if (N_results->ai_socktype == SOCK_STREAM) { /* TCP (stream) and connections are used. - * - * A connection must be established from the target each - * time network_listen() is called, and closed after the data are + * + * A connection must be established from the target each + * time network_listen() is called, and closed after the data are * transfered. network_setup_listener() creates a stream socket - * (with the file descriptor N_fd) and listens for connection requests. + * (with the file descriptor N_fd) and listens for connection requests. * This must be done before a target that expects to connect is executed. * N_myaddr_valid tells the codes that the listening socket has been - * setup (and keeps this code from running twice as a safety net). + * setup (and keeps this code from running twice as a safety net). * UDP is connectionless and quite different. See below. - * + * * Local variables: */ int optval = 1; if (N_myaddr_valid == 0) { /* don't do this twice! */ @@ -1900,14 +2064,14 @@ void network_setup_listener(void) { } } else if (N_results->ai_socktype == SOCK_DGRAM) { /* UDP datagrams are used. - * + * * Create a socket to be used to both receive and send packets, referenced * by the file descriptor N_fd. - * + * * N_fd is kept open for the duration of the afl run (closed on exit) * and reused. N_myaddr_valid signals the code that the UDP socket * has been set up and bound to the sending side of the address & port. - * + * * First time: find the appropriate sockaddr structure to be used and * set up the sending side's socket. After the first time's successful * execution, N_rp points to the address information corresonding to @@ -1968,12 +2132,12 @@ int network_listen(void) { * In both cases, all data read are discarded. Note that for UDP reads * any data in excess of the size of the read buffer are discarded by the * network stack. - * + * * Note that non-blocking reads are attempted, and if they fail then the * calling process is expected to wait for a programmed interval of time * (specified by the -D command line argument) and retry the call to * network_listen(), for a programmed number of times (not user-selectable). - * + * * Note that unlike the case where this code plays the role of a client to * the target process (using network_send()), we typically have no control * over the target's reuse (or not) of ephemeral port numbers. Therefore, @@ -1989,7 +2153,7 @@ int network_listen(void) { /* network_setup_listener() must be called first, and must succeed */ if (!N_myaddr_valid) FATAL("error: network_listen() called before network_setup_listener()"); - + /* Two cases: SOCK_STREAM (for TCP) and SOCK_DGRAM (for UDP) */ if (N_rp->ai_socktype == SOCK_STREAM) { /* TCP (stream) and connections are used. */ @@ -2044,7 +2208,7 @@ int network_listen(void) { close(fd); /* and close the file descriptor of the socket for the target */ close(client_fd); - + } else if (N_rp->ai_socktype == SOCK_DGRAM) { /* UDP datagrams are used. * @@ -2115,31 +2279,31 @@ int network_send(void) { * N_results should never be a NULL pointer because the return code * from getaddrinfo() is checked. */ if (N_results != NULL) { - + /* Two cases: SOCK_STREAM (for TCP) and SOCK_DGRAM (for UDP) */ if (N_results->ai_socktype == SOCK_STREAM) { /* TCP (stream) and connections are used. - * + * * NOTE: A TCP connection must be established each time this code * is called, and closed after the data are transfered. However, the * same port number should be used for the sending (this) side of the * TCP transaction every time. Otherwise, ephemeral port * numbers might be exhausted because of TCP's TIME_WAIT timeout * interval. N_myaddr_valid tells this code that the sending side's - * address information has been stored in N_myaddr and is to be reused. + * address information has been stored in N_myaddr and is to be reused. * UDP is connectionless and is therefore different. See below. - * + * * Note that the other mode of operation, where this code acts as a * server to a target, does not have control over the target's reuse * of ephemeral port numbers. See the comments in network_listen() * for a discussion. - * + * * Note that "soft" failures cause a return with an error code of -1. The * calling process is expected to wait for a programmed interval of time * (specified by the -D command line argument) and retry the call to * network_send(), for a programmed number of times (not user-selectable) * when this occurs. - * + * * Local variables: */ int optval = 1; @@ -2223,10 +2387,10 @@ int network_send(void) { } /* and close the connection to the target process, signaling EOF */ close(N_fd); - + } else if (N_results->ai_socktype == SOCK_DGRAM) { /* UDP datagrams are used. - * + * * N_fd is kept open for the duration of the afl run (closed on exit) * and reused. N_myaddr_valid signals this code that the UDP socket * has been set up and bound to the sending side of the address & port. @@ -2276,7 +2440,7 @@ int network_send(void) { } else { break; } - } + } close(N_fd); } N_myaddr_valid = 1; @@ -2326,7 +2490,7 @@ int network_send(void) { /* this should never be executed */ FATAL("no address information structures match command line network spec"); } - + return 0; } @@ -2338,7 +2502,7 @@ int network_send(void) { cloning a stopped child. So, we just execute once, and then send commands through a pipe. The other part of this logic is in afl-as.h. */ -static void init_forkserver(char** argv) { +EXP_ST void init_forkserver(char** argv) { static struct itimerval it; int st_pipe[2], ctl_pipe[2]; @@ -2403,6 +2567,7 @@ static void init_forkserver(char** argv) { dup2(dev_null_fd, 1); dup2(dev_null_fd, 2); + if (out_file || N_valid == 1) { /* no stdin for file or network input */ dup2(dev_null_fd, 0); @@ -2438,12 +2603,16 @@ static void init_forkserver(char** argv) { setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" + "symbolize=0:" "allocator_may_return_null=1", 0); /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "abort_on_error=1:" + "allocator_may_return_null=1:" "msan_track_origins=0", 0); execv(target_path, argv); @@ -2628,13 +2797,12 @@ static u8 run_target(char** argv) { u32 tb4; child_timed_out = 0; - + /* check to ensure that network listener has executed if doing network * fuzzing of a client target (where the target writes to a socket first */ if (N_fuzz_client && !N_myaddr_valid) { network_setup_listener(); } - /* After this memset, trace_bits[] are effectively volatile, so we must prevent any earlier operations from venturing into that @@ -2708,9 +2876,11 @@ static u8 run_target(char** argv) { setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" + "symbolize=0:" "allocator_may_return_null=1", 0); setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" "msan_track_origins=0", 0); execv(target_path, argv); @@ -2749,7 +2919,7 @@ static u8 run_target(char** argv) { } /* Write fuzzed data set to target using network if -N option is specified */ - + if (N_valid) { if (N_timeout_given) { /* Network output to target process after specified delay, and try @@ -2758,20 +2928,20 @@ static u8 run_target(char** argv) { N_it.tv_nsec = (N_exec_tmout % 1000) * 1000000; /* ignore errors & accept possibility that delay can be shorter */ { - u32 N_tries = 3; - nanosleep(&N_it, NULL); - /* attempt to send up to 3 times (because of target process startup time) */ - while (N_tries-- && + u32 N_tries = 3; + nanosleep(&N_it, NULL); + /* attempt to send up to 3 times (because of target process startup time) */ + while (N_tries-- && ((N_fuzz_client?network_listen():network_send()) == -1)); } } else { /* Network output to target process - no delay. This usual won't work. */ if ((N_fuzz_client?network_listen():network_send()) == -1) { - FATAL("Network: failed to connect or send; specify a network delay time"); + FATAL("Network: failed to connect or send; specify a network delay time"); } } } - + /* Configure timeout, as requested by user, then wait for child to terminate. */ it.it_value.tv_sec = (exec_tmout / 1000); @@ -2792,13 +2962,14 @@ static u8 run_target(char** argv) { if ((res = read(fsrv_st_fd, &status, 4)) != 4) { if (stop_soon) return 0; - RPFATAL(res, "Unable to communicate with fork server"); + RPFATAL(res, "Unable to communicate with fork server (OOM?)"); } } - child_pid = 0; + if (!WIFSTOPPED(status)) child_pid = 0; + it.it_value.tv_sec = 0; it.it_value.tv_usec = 0; @@ -2917,7 +3088,11 @@ static void show_stats(void); static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, u32 handicap, u8 from_queue) { - u8 fault = 0, new_bits = 0, var_detected = 0, first_run = (q->exec_cksum == 0); + static u8 first_trace[MAP_SIZE]; + + u8 fault = 0, new_bits = 0, var_detected = 0, + first_run = (q->exec_cksum == 0); + u64 start_us, stop_us; s32 old_sc = stage_cur, old_sm = stage_max, old_tmout = exec_tmout; @@ -2934,7 +3109,7 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, q->cal_failed++; stage_name = "calibration"; - stage_max = no_var_check ? CAL_CYCLES_NO_VAR : CAL_CYCLES; + stage_max = CAL_CYCLES; /* Make sure the forkserver is up before we do anything, and let's not count its spin-up time toward binary calibration. */ @@ -2942,6 +3117,8 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, if (dumb_mode != 1 && !no_forkserver && !forksrv_pid) init_forkserver(argv); + if (q->exec_cksum) memcpy(first_trace, trace_bits, MAP_SIZE); + start_us = get_cur_time_us(); for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { @@ -2971,12 +3148,29 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, u8 hnb = has_new_bits(virgin_bits); if (hnb > new_bits) new_bits = hnb; - if (!no_var_check && q->exec_cksum) { + if (q->exec_cksum) { + + u32 i; + + for (i = 0; i < MAP_SIZE; i++) { + + if (!var_bytes[i] && first_trace[i] != trace_bits[i]) { + + var_bytes[i] = 1; + stage_max = CAL_CYCLES_LONG; + + } + + } var_detected = 1; - stage_max = CAL_CYCLES_LONG; - } else q->exec_cksum = cksum; + } else { + + q->exec_cksum = cksum; + memcpy(first_trace, trace_bits, MAP_SIZE); + + } } @@ -3015,9 +3209,15 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, /* Mark variable paths. */ - if (var_detected && !q->var_behavior) { - mark_as_variable(q); - queued_variable++; + if (var_detected) { + + var_byte_count = count_bytes(var_bytes); + + if (!q->var_behavior) { + mark_as_variable(q); + queued_variable++; + } + } stage_name = old_sn; @@ -3220,7 +3420,7 @@ static void perform_dry_run(char** argv) { useless_at_start++; - if (!in_bitmap) + if (!in_bitmap && !shuffle_queue) WARNF("No new instrumentation output, test case may be useless."); break; @@ -3606,6 +3806,7 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) { unique_crashes++; last_crash_time = get_cur_time(); + last_crash_execs = total_execs; break; @@ -3703,9 +3904,9 @@ static void find_timeout(void) { /* Update stats file for unattended monitoring. */ -static void write_stats_file(double bitmap_cvg, double eps) { +static void write_stats_file(double bitmap_cvg, double stability, double eps) { - static double last_bcvg, last_eps; + static double last_bcvg, last_stab, last_eps; u8* fn = alloc_printf("%s/fuzzer_stats", out_dir); s32 fd; @@ -3724,46 +3925,51 @@ static void write_stats_file(double bitmap_cvg, double eps) { /* Keep last values in case we're called from another context where exec/sec stats and such are not readily available. */ - if (!bitmap_cvg && !eps) { + if (!bitmap_cvg && !stability && !eps) { bitmap_cvg = last_bcvg; + stability = last_stab; eps = last_eps; } else { last_bcvg = bitmap_cvg; + last_stab = stability; last_eps = eps; } - fprintf(f, "start_time : %llu\n" - "last_update : %llu\n" - "fuzzer_pid : %u\n" - "cycles_done : %llu\n" - "execs_done : %llu\n" - "execs_per_sec : %0.02f\n" - "paths_total : %u\n" - "paths_favored : %u\n" - "paths_found : %u\n" - "paths_imported : %u\n" - "max_depth : %u\n" - "cur_path : %u\n" - "pending_favs : %u\n" - "pending_total : %u\n" - "variable_paths : %u\n" - "bitmap_cvg : %0.02f%%\n" - "unique_crashes : %llu\n" - "unique_hangs : %llu\n" - "last_path : %llu\n" - "last_crash : %llu\n" - "last_hang : %llu\n" - "exec_timeout : %u\n" - "afl_banner : %s\n" - "afl_version : " VERSION "\n" - "command_line : %s\n", + fprintf(f, "start_time : %llu\n" + "last_update : %llu\n" + "fuzzer_pid : %u\n" + "cycles_done : %llu\n" + "execs_done : %llu\n" + "execs_per_sec : %0.02f\n" + "paths_total : %u\n" + "paths_favored : %u\n" + "paths_found : %u\n" + "paths_imported : %u\n" + "max_depth : %u\n" + "cur_path : %u\n" + "pending_favs : %u\n" + "pending_total : %u\n" + "variable_paths : %u\n" + "stability : %0.02f%%\n" + "bitmap_cvg : %0.02f%%\n" + "unique_crashes : %llu\n" + "unique_hangs : %llu\n" + "last_path : %llu\n" + "last_crash : %llu\n" + "last_hang : %llu\n" + "execs_since_crash : %llu\n" + "exec_timeout : %u\n" + "afl_banner : %s\n" + "afl_version : " VERSION "\n" + "command_line : %s\n", start_time / 1000, get_cur_time() / 1000, getpid(), queue_cycle ? (queue_cycle - 1) : 0, total_execs, eps, queued_paths, queued_favored, queued_discovered, queued_imported, max_depth, current_entry, pending_favored, pending_not_fuzzed, - queued_variable, bitmap_cvg, unique_crashes, unique_hangs, - last_path_time / 1000, last_crash_time / 1000, - last_hang_time / 1000, exec_tmout, use_banner, orig_cmdline); + queued_variable, stability, bitmap_cvg, unique_crashes, + unique_hangs, last_path_time / 1000, last_crash_time / 1000, + last_hang_time / 1000, total_execs - last_crash_execs, + exec_tmout, use_banner, orig_cmdline); /* ignore errors */ fclose(f); @@ -3991,7 +4197,7 @@ static void maybe_delete_out_dir(void) { " session, put '-' as the input directory in the command line ('-i -') and\n" " try again.\n", OUTPUT_GRACE); - FATAL("At-risk data found in in '%s'", out_dir); + FATAL("At-risk data found in '%s'", out_dir); } @@ -4186,7 +4392,7 @@ static void show_stats(void) { static u64 last_stats_ms, last_plot_ms, last_ms, last_execs; static double avg_exec; - double t_byte_ratio; + double t_byte_ratio, stab_ratio; u64 cur_ms; u32 t_bytes, t_bits; @@ -4239,12 +4445,17 @@ static void show_stats(void) { t_bytes = count_non_255_bytes(virgin_bits); t_byte_ratio = ((double)t_bytes * 100) / MAP_SIZE; + if (t_bytes) + stab_ratio = 100 - ((double)var_byte_count) * 100 / t_bytes; + else + stab_ratio = 100; + /* Roughly every minute, update fuzzer stats and save auto tokens. */ if (cur_ms - last_stats_ms > STATS_UPDATE_SEC * 1000) { last_stats_ms = cur_ms; - write_stats_file(t_byte_ratio, avg_exec); + write_stats_file(t_byte_ratio, stab_ratio, avg_exec); save_auto(); write_bitmap(); @@ -4259,11 +4470,13 @@ static void show_stats(void) { } - /* Honor AFL_EXIT_WHEN_DONE. */ + /* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */ - if (!dumb_mode && cycles_wo_finds > 20 && !pending_not_fuzzed && + if (!dumb_mode && cycles_wo_finds > 100 && !pending_not_fuzzed && getenv("AFL_EXIT_WHEN_DONE")) stop_soon = 2; + if (total_crashes && getenv("AFL_BENCH_UNTIL_CRASH")) stop_soon = 2; + /* If we're not on TTY, bail out. */ if (not_on_tty) return; @@ -4288,7 +4501,7 @@ static void show_stats(void) { if (term_too_small) { SAYF(cBRI "Your terminal is too small to display the UI.\n" - "Please resize terminal window to at least 80x25.\n" cNOR); + "Please resize terminal window to at least 80x25.\n" cRST); return; @@ -4325,7 +4538,7 @@ static void show_stats(void) { if (dumb_mode) { - strcpy(tmp, cNOR); + strcpy(tmp, cRST); } else { @@ -4333,17 +4546,17 @@ static void show_stats(void) { if (queue_cycle == 1) strcpy(tmp, cMGN); else /* Subsequent cycles, but we're still making finds. */ - if (cycles_wo_finds < 3) strcpy(tmp, cYEL); else + if (cycles_wo_finds < 25) strcpy(tmp, cYEL); else /* No finds for a long time and no test cases to try. */ - if (cycles_wo_finds > 20 && !pending_not_fuzzed) strcpy(tmp, cLGN); + if (cycles_wo_finds > 100 && !pending_not_fuzzed) strcpy(tmp, cLGN); /* Default: cautiously OK to stop? */ else strcpy(tmp, cLBL); } - SAYF(bV bSTOP " run time : " cNOR "%-34s " bSTG bV bSTOP + SAYF(bV bSTOP " run time : " cRST "%-34s " bSTG bV bSTOP " cycles done : %s%-5s " bSTG bV "\n", DTD(cur_ms, start_time), tmp, DI(queue_cycle - 1)); @@ -4353,24 +4566,24 @@ static void show_stats(void) { if (!dumb_mode && (last_path_time || resuming_fuzz || queue_cycle == 1 || in_bitmap || crash_mode)) { - SAYF(bV bSTOP " last new path : " cNOR "%-34s ", + SAYF(bV bSTOP " last new path : " cRST "%-34s ", DTD(cur_ms, last_path_time)); } else { if (dumb_mode) - SAYF(bV bSTOP " last new path : " cPIN "n/a" cNOR + SAYF(bV bSTOP " last new path : " cPIN "n/a" cRST " (non-instrumented mode) "); else - SAYF(bV bSTOP " last new path : " cNOR "none yet " cLRD + SAYF(bV bSTOP " last new path : " cRST "none yet " cLRD "(odd, check syntax!) "); } - SAYF(bSTG bV bSTOP " total paths : " cNOR "%-5s " bSTG bV "\n", + SAYF(bSTG bV bSTOP " total paths : " cRST "%-5s " bSTG bV "\n", DI(queued_paths)); /* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH @@ -4379,16 +4592,16 @@ static void show_stats(void) { sprintf(tmp, "%s%s", DI(unique_crashes), (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); - SAYF(bV bSTOP " last uniq crash : " cNOR "%-34s " bSTG bV bSTOP + SAYF(bV bSTOP " last uniq crash : " cRST "%-34s " bSTG bV bSTOP " uniq crashes : %s%-6s " bSTG bV "\n", - DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cNOR, + DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cRST, tmp); sprintf(tmp, "%s%s", DI(unique_hangs), (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); - SAYF(bV bSTOP " last uniq hang : " cNOR "%-34s " bSTG bV bSTOP - " uniq hangs : " cNOR "%-6s " bSTG bV "\n", + SAYF(bV bSTOP " last uniq hang : " cRST "%-34s " bSTG bV bSTOP + " uniq hangs : " cRST "%-6s " bSTG bV "\n", DTD(cur_ms, last_hang_time), tmp); SAYF(bVR bH bSTOP cCYA " cycle progress " bSTG bH20 bHB bH bSTOP cCYA @@ -4402,23 +4615,23 @@ static void show_stats(void) { queue_cur->favored ? "" : "*", ((double)current_entry * 100) / queued_paths); - SAYF(bV bSTOP " now processing : " cNOR "%-17s " bSTG bV bSTOP, tmp); - + SAYF(bV bSTOP " now processing : " cRST "%-17s " bSTG bV bSTOP, tmp); - sprintf(tmp, "%s (%0.02f%%)", DI(t_bytes), t_byte_ratio); + sprintf(tmp, "%0.02f%% / %0.02f%%", ((double)queue_cur->bitmap_size) * + 100 / MAP_SIZE, t_byte_ratio); SAYF(" map density : %s%-21s " bSTG bV "\n", t_byte_ratio > 70 ? cLRD : - ((t_bytes < 200 && !dumb_mode) ? cPIN : cNOR), tmp); + ((t_bytes < 200 && !dumb_mode) ? cPIN : cRST), tmp); sprintf(tmp, "%s (%0.02f%%)", DI(cur_skipped_paths), ((double)cur_skipped_paths * 100) / queued_paths); - SAYF(bV bSTOP " paths timed out : " cNOR "%-17s " bSTG bV, tmp); + SAYF(bV bSTOP " paths timed out : " cRST "%-17s " bSTG bV, tmp); sprintf(tmp, "%0.02f bits/tuple", t_bytes ? (((double)t_bits) / t_bytes) : 0); - SAYF(bSTOP " count coverage : " cNOR "%-21s " bSTG bV "\n", tmp); + SAYF(bSTOP " count coverage : " cRST "%-21s " bSTG bV "\n", tmp); SAYF(bVR bH bSTOP cCYA " stage progress " bSTG bH20 bX bH bSTOP cCYA " findings in depth " bSTG bH20 bVL "\n"); @@ -4428,8 +4641,8 @@ static void show_stats(void) { /* Yeah... it's still going on... halp? */ - SAYF(bV bSTOP " now trying : " cNOR "%-21s " bSTG bV bSTOP - " favored paths : " cNOR "%-22s " bSTG bV "\n", stage_name, tmp); + SAYF(bV bSTOP " now trying : " cRST "%-21s " bSTG bV bSTOP + " favored paths : " cRST "%-22s " bSTG bV "\n", stage_name, tmp); if (!stage_max) { @@ -4442,27 +4655,27 @@ static void show_stats(void) { } - SAYF(bV bSTOP " stage execs : " cNOR "%-21s " bSTG bV bSTOP, tmp); + SAYF(bV bSTOP " stage execs : " cRST "%-21s " bSTG bV bSTOP, tmp); sprintf(tmp, "%s (%0.02f%%)", DI(queued_with_cov), ((double)queued_with_cov) * 100 / queued_paths); - SAYF(" new edges on : " cNOR "%-22s " bSTG bV "\n", tmp); + SAYF(" new edges on : " cRST "%-22s " bSTG bV "\n", tmp); sprintf(tmp, "%s (%s%s unique)", DI(total_crashes), DI(unique_crashes), (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); if (crash_mode) { - SAYF(bV bSTOP " total execs : " cNOR "%-21s " bSTG bV bSTOP + SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP " new crashes : %s%-22s " bSTG bV "\n", DI(total_execs), - unique_crashes ? cLRD : cNOR, tmp); + unique_crashes ? cLRD : cRST, tmp); } else { - SAYF(bV bSTOP " total execs : " cNOR "%-21s " bSTG bV bSTOP + SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP " total crashes : %s%-22s " bSTG bV "\n", DI(total_execs), - unique_crashes ? cLRD : cNOR, tmp); + unique_crashes ? cLRD : cRST, tmp); } @@ -4478,14 +4691,14 @@ static void show_stats(void) { } else { sprintf(tmp, "%s/sec", DF(avg_exec)); - SAYF(bV bSTOP " exec speed : " cNOR "%-21s ", tmp); + SAYF(bV bSTOP " exec speed : " cRST "%-21s ", tmp); } sprintf(tmp, "%s (%s%s unique)", DI(total_hangs), DI(unique_hangs), (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); - SAYF (bSTG bV bSTOP " total hangs : " cNOR "%-22s " bSTG bV "\n", tmp); + SAYF (bSTG bV bSTOP " total hangs : " cRST "%-22s " bSTG bV "\n", tmp); /* Aaaalmost there... hold on! */ @@ -4505,8 +4718,8 @@ static void show_stats(void) { } - SAYF(bV bSTOP " bit flips : " cNOR "%-37s " bSTG bV bSTOP " levels : " - cNOR "%-10s " bSTG bV "\n", tmp, DI(max_depth)); + SAYF(bV bSTOP " bit flips : " cRST "%-37s " bSTG bV bSTOP " levels : " + cRST "%-10s " bSTG bV "\n", tmp, DI(max_depth)); if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", @@ -4514,8 +4727,8 @@ static void show_stats(void) { DI(stage_finds[STAGE_FLIP16]), DI(stage_cycles[STAGE_FLIP16]), DI(stage_finds[STAGE_FLIP32]), DI(stage_cycles[STAGE_FLIP32])); - SAYF(bV bSTOP " byte flips : " cNOR "%-37s " bSTG bV bSTOP " pending : " - cNOR "%-10s " bSTG bV "\n", tmp, DI(pending_not_fuzzed)); + SAYF(bV bSTOP " byte flips : " cRST "%-37s " bSTG bV bSTOP " pending : " + cRST "%-10s " bSTG bV "\n", tmp, DI(pending_not_fuzzed)); if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", @@ -4523,8 +4736,8 @@ static void show_stats(void) { DI(stage_finds[STAGE_ARITH16]), DI(stage_cycles[STAGE_ARITH16]), DI(stage_finds[STAGE_ARITH32]), DI(stage_cycles[STAGE_ARITH32])); - SAYF(bV bSTOP " arithmetics : " cNOR "%-37s " bSTG bV bSTOP " pend fav : " - cNOR "%-10s " bSTG bV "\n", tmp, DI(pending_favored)); + SAYF(bV bSTOP " arithmetics : " cRST "%-37s " bSTG bV bSTOP " pend fav : " + cRST "%-10s " bSTG bV "\n", tmp, DI(pending_favored)); if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", @@ -4532,8 +4745,8 @@ static void show_stats(void) { DI(stage_finds[STAGE_INTEREST16]), DI(stage_cycles[STAGE_INTEREST16]), DI(stage_finds[STAGE_INTEREST32]), DI(stage_cycles[STAGE_INTEREST32])); - SAYF(bV bSTOP " known ints : " cNOR "%-37s " bSTG bV bSTOP " own finds : " - cNOR "%-10s " bSTG bV "\n", tmp, DI(queued_discovered)); + SAYF(bV bSTOP " known ints : " cRST "%-37s " bSTG bV bSTOP " own finds : " + cRST "%-10s " bSTG bV "\n", tmp, DI(queued_discovered)); if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", @@ -4541,17 +4754,22 @@ static void show_stats(void) { DI(stage_finds[STAGE_EXTRAS_UI]), DI(stage_cycles[STAGE_EXTRAS_UI]), DI(stage_finds[STAGE_EXTRAS_AO]), DI(stage_cycles[STAGE_EXTRAS_AO])); - SAYF(bV bSTOP " dictionary : " cNOR "%-37s " bSTG bV bSTOP - " imported : " cNOR "%-10s " bSTG bV "\n", tmp, + SAYF(bV bSTOP " dictionary : " cRST "%-37s " bSTG bV bSTOP + " imported : " cRST "%-10s " bSTG bV "\n", tmp, sync_id ? DI(queued_imported) : (u8*)"n/a"); sprintf(tmp, "%s/%s, %s/%s", DI(stage_finds[STAGE_HAVOC]), DI(stage_cycles[STAGE_HAVOC]), DI(stage_finds[STAGE_SPLICE]), DI(stage_cycles[STAGE_SPLICE])); - SAYF(bV bSTOP " havoc : " cNOR "%-37s " bSTG bV bSTOP - " variable : %s%-10s " bSTG bV "\n", tmp, queued_variable ? cLRD : cNOR, - no_var_check ? (u8*)"n/a" : DI(queued_variable)); + SAYF(bV bSTOP " havoc : " cRST "%-37s " bSTG bV bSTOP, tmp); + + if (t_bytes) sprintf(tmp, "%0.02f%%", stab_ratio); + else strcpy(tmp, "n/a"); + + SAYF(" stability : %s%-10s " bSTG bV "\n", (stab_ratio < 85 && var_byte_count > 40) + ? cLRD : ((queued_variable && (!persistent_mode || var_byte_count > 20)) + ? cMGN : cRST), tmp); if (!bytes_trim_out) { @@ -4584,7 +4802,7 @@ static void show_stats(void) { } - SAYF(bV bSTOP " trim : " cNOR "%-37s " bSTG bVR bH20 bH2 bH2 bRB "\n" + SAYF(bV bSTOP " trim : " cRST "%-37s " bSTG bVR bH20 bH2 bH2 bRB "\n" bLB bH30 bH20 bH2 bH bRB bSTOP cRST RESET_G1, tmp); /* Provide some CPU utilization stats. */ @@ -4605,8 +4823,27 @@ static void show_stats(void) { if (!no_cpu_meter_red && cur_utilization >= 150) cpu_color = cLRD; +#ifdef HAVE_AFFINITY + + if (cpu_aff >= 0) { + + SAYF(SP10 cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, + MIN(cpu_aff, 999), cpu_color, + MIN(cur_utilization, 999)); + + } else { + + SAYF(SP10 cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, + cpu_color, MIN(cur_utilization, 999)); + + } + +#else + SAYF(SP10 cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, - cpu_color, cur_utilization < 999 ? cur_utilization : 999); + cpu_color, MIN(cur_utilization, 999)); + +#endif /* ^HAVE_AFFINITY */ } else SAYF("\r"); @@ -4678,9 +4915,9 @@ static void show_init_stats(void) { OKF("Here are some useful stats:\n\n" - cGRA " Test case count : " cNOR "%u favored, %u variable, %u total\n" - cGRA " Bitmap range : " cNOR "%u to %u bits (average: %0.02f bits)\n" - cGRA " Exec timing : " cNOR "%s to %s us (average: %s us)\n", + cGRA " Test case count : " cRST "%u favored, %u variable, %u total\n" + cGRA " Bitmap range : " cRST "%u to %u bits (average: %0.02f bits)\n" + cGRA " Exec timing : " cRST "%s to %s us (average: %s us)\n", queued_favored, queued_variable, queued_paths, min_bits, max_bits, ((double)total_bitmap_size) / (total_bitmap_entries ? total_bitmap_entries : 1), DI(min_us), DI(max_us), DI(avg_us)); @@ -4860,7 +5097,7 @@ static u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) { error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ -static u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) { +EXP_ST u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) { u8 fault; @@ -4998,9 +5235,9 @@ static u32 calculate_score(struct queue_entry* q) { case 0 ... 3: break; case 4 ... 7: perf_score *= 2; break; - case 8 ... 13: perf_score *= 4; break; - case 14 ... 25: perf_score *= 6; break; - default: perf_score *= 8; + case 8 ... 13: perf_score *= 3; break; + case 14 ... 25: perf_score *= 4; break; + default: perf_score *= 5; } @@ -5210,7 +5447,7 @@ static u8 fuzz_one(char** argv) { u64 havoc_queued, orig_hit_cnt, new_hit_cnt; u32 splice_cycle = 0, perf_score = 100, orig_perf, prev_cksum, eff_cnt = 1; - u8 ret_val = 1; + u8 ret_val = 1, doing_det = 0; u8 a_collect[MAX_AUTO_EXTRA]; u32 a_len = 0; @@ -5253,8 +5490,11 @@ static u8 fuzz_one(char** argv) { #endif /* ^IGNORE_FINDS */ - if (not_on_tty) - ACTF("Fuzzing test case #%u (%u total)...", current_entry, queued_paths); + if (not_on_tty) { + ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found)...", + current_entry, queued_paths, unique_crashes); + fflush(stdout); + } /* Map the test case into memory. */ @@ -5343,6 +5583,14 @@ static u8 fuzz_one(char** argv) { if (skip_deterministic || queue_cur->was_fuzzed || queue_cur->passed_det) goto havoc_stage; + /* Skip deterministic fuzzing if exec path checksum puts this out of scope + for this master instance. */ + + if (master_max && (queue_cur->exec_cksum % master_max) != master_id - 1) + goto havoc_stage; + + doing_det = 1; + /********************************************* * SIMPLE BITFLIP (+dictionary construction) * *********************************************/ @@ -5449,8 +5697,6 @@ static u8 fuzz_one(char** argv) { stage_finds[STAGE_FLIP1] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_FLIP1] += stage_max; - if (queue_cur->passed_det) goto havoc_stage; - /* Two walking bits. */ stage_name = "bitflip 2/1"; @@ -5512,7 +5758,7 @@ static u8 fuzz_one(char** argv) { /* Effector map setup. These macros calculate: EFF_APOS - position of a particular file offset in the map. - EFF_ALEN - length of an map with a particular number of bytes. + EFF_ALEN - length of a map with a particular number of bytes. EFF_SPAN_ALEN - map span for a sequence of bytes. */ @@ -6207,6 +6453,11 @@ static u8 fuzz_one(char** argv) { for (j = 0; j < extras_cnt; j++) { + if (len + extras[j].len > MAX_FILE) { + stage_max--; + continue; + } + /* Insert token */ memcpy(ex_tmp + i, extras[j].data, extras[j].len); @@ -6308,7 +6559,8 @@ static u8 fuzz_one(char** argv) { stage_name = "havoc"; stage_short = "havoc"; - stage_max = HAVOC_CYCLES * perf_score / havoc_div / 100; + stage_max = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * + perf_score / havoc_div / 100; } else { @@ -6606,10 +6858,10 @@ static u8 fuzz_one(char** argv) { } - /* Values 16 and 17 can be selected only if there are any extras + /* Values 15 and 16 can be selected only if there are any extras present in the dictionaries. */ - case 16: { + case 15: { /* Overwrite bytes with an extra. */ @@ -6646,7 +6898,7 @@ static u8 fuzz_one(char** argv) { } - case 17: { + case 16: { u32 use_extra, extra_len, insert_at = UR(temp_len); u8* new_buf; @@ -6938,8 +7190,14 @@ static void sync_fuzzers(char** argv) { path = alloc_printf("%s/%s", qd_path, qd_ent->d_name); + /* Allow this to fail in case the other fuzzer is resuming or so... */ + fd = open(path, O_RDONLY); - if (fd < 0) PFATAL("Unable to open '%s'", path); + + if (fd < 0) { + ck_free(path); + continue; + } if (fstat(fd, &st)) PFATAL("fstat() failed"); @@ -7033,7 +7291,7 @@ static void handle_timeout(int sig) { isn't a shell script - a common and painful mistake. We also check for a valid ELF header and for evidence of AFL instrumentation. */ -static void check_binary(u8* fname) { +EXP_ST void check_binary(u8* fname) { u8* env_path = 0; struct stat st; @@ -7173,7 +7431,7 @@ static void check_binary(u8* fname) { OKF(cPIN "Persistent mode binary detected."); setenv(PERSIST_ENV_VAR, "1", 1); - no_var_check = 1; + persistent_mode = 1; } else if (getenv("AFL_PERSISTENT")) { @@ -7233,6 +7491,12 @@ static void check_if_tty(void) { struct winsize ws; + if (getenv("AFL_NO_UI")) { + OKF("Disabling the UI because AFL_NO_UI is set."); + not_on_tty = 1; + return; + } + if (ioctl(1, TIOCGWINSZ, &ws)) { if (errno == ENOTTY) { @@ -7327,7 +7591,7 @@ static void usage(u8* argv0) { /* Prepare output directories and fds. */ -static void setup_dirs_fds(void) { +EXP_ST void setup_dirs_fds(void) { u8* tmp; s32 fd; @@ -7447,7 +7711,7 @@ static void setup_dirs_fds(void) { /* Setup the output file for fuzzed data, if not using -f. */ -static void setup_stdio_file(void) { +EXP_ST void setup_stdio_file(void) { u8* fn = alloc_printf("%s/.cur_input", out_dir); @@ -7507,9 +7771,9 @@ static void check_crash_handling(void) { SAYF("\n" cLRD "[-] " cRST "Hmm, your system is configured to send core dump notifications to an\n" - " external utility. This will cause issues due to an extended delay\n" - " between the fuzzed binary malfunctioning and this information being\n" - " eventually relayed to the fuzzer via the standard waitpid() API.\n\n" + " external utility. This will cause issues: there will be an extended delay\n" + " between stumbling upon a crash and having this information relayed to the\n" + " fuzzer via the standard waitpid() API.\n\n" " To avoid having crashes misinterpreted as hangs, please log in as root\n" " and temporarily modify /proc/sys/kernel/core_pattern, like so:\n\n" @@ -7611,8 +7875,11 @@ static void get_core_count(void) { #else - /* On Linux, a simple way is to look at /proc/stat, especially since we'd - be parsing it anyway for other reasons later on. */ +#ifdef HAVE_AFFINITY + + cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN); + +#else FILE* f = fopen("/proc/stat", "r"); u8 tmp[1024]; @@ -7623,10 +7890,12 @@ static void get_core_count(void) { if (!strncmp(tmp, "cpu", 3) && isdigit(tmp[3])) cpu_core_count++; fclose(f); - + +#endif /* ^HAVE_AFFINITY */ + #endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */ - if (cpu_core_count) { + if (cpu_core_count > 0) { cur_runnable = (u32)get_runnable_processes(); @@ -7638,8 +7907,9 @@ static void get_core_count(void) { #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ - OKF("You have %u CPU cores and %u runnable tasks (utilization: %0.0f%%).", - cpu_core_count, cur_runnable, cur_runnable * 100.0 / cpu_core_count); + OKF("You have %u CPU core%s and %u runnable tasks (utilization: %0.0f%%).", + cpu_core_count, cpu_core_count > 1 ? "s" : "", + cur_runnable, cur_runnable * 100.0 / cpu_core_count); if (cpu_core_count > 1) { @@ -7655,7 +7925,12 @@ static void get_core_count(void) { } - } else WARNF("Unable to figure out the number of CPU cores."); + } else { + + cpu_core_count = 0; + WARNF("Unable to figure out the number of CPU cores."); + + } } @@ -7714,21 +7989,35 @@ static void handle_resize(int sig) { static void check_asan_opts(void) { u8* x = getenv("ASAN_OPTIONS"); - if (x && !strstr(x, "abort_on_error=1")) - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + if (x) { + + if (!strstr(x, "abort_on_error=1")) + FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + + if (!strstr(x, "symbolize=0")) + FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + + } x = getenv("MSAN_OPTIONS"); - if (x && !strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) - FATAL("Custom MSAN_OPTIONS set without exit_code=" - STRINGIFY(MSAN_ERROR) " - please fix!"); + if (x) { + + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) + FATAL("Custom MSAN_OPTIONS set without exit_code=" + STRINGIFY(MSAN_ERROR) " - please fix!"); + + if (!strstr(x, "symbolize=0")) + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + + } } /* Detect @@ in args. */ -static void detect_file_args(char** argv) { +EXP_ST void detect_file_args(char** argv) { u32 i = 0; u8* cwd = getcwd(NULL, 0); @@ -7777,7 +8066,7 @@ static void detect_file_args(char** argv) { Solaris doesn't resume interrupted reads(), sets SA_RESETHAND when you call siginterrupt(), and does other stupid things. */ -static void setup_signal_handlers(void) { +EXP_ST void setup_signal_handlers(void) { struct sigaction sa; @@ -7915,7 +8204,7 @@ static void save_cmdline(u32 argc, char** argv) { } - +#ifndef AFL_LIB /* Main entry point */ @@ -7926,18 +8215,24 @@ int main(int argc, char** argv) { u32 sync_interval_cnt = 0, seek_to; u8 *extras_dir = 0; u8 mem_limit_given = 0; - + u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); char** use_argv; + struct timeval tv; + struct timezone tz; + SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by \n"); doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; - while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:QN:D:L")) > 0) { + gettimeofday(&tv, &tz); + srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); + + while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:QN:D:L")) > 0) switch (opt) { - case 'i': + case 'i': /* input dir */ if (in_dir) FATAL("Multiple -i options not supported"); in_dir = optarg; @@ -7952,15 +8247,33 @@ int main(int argc, char** argv) { out_dir = optarg; break; - case 'M': + case 'M': { /* master sync ID */ + + u8* c; - force_deterministic = 1; - /* Fall through */ + if (sync_id) FATAL("Multiple -S or -M options not supported"); + sync_id = ck_strdup(optarg); - case 'S': /* sync ID */ + if ((c = strchr(sync_id, ':'))) { + + *c = 0; + + if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || + !master_id || !master_max || master_id > master_max || + master_max > 1000000) FATAL("Bogus master ID passed to -M"); + + } + + force_deterministic = 1; + + } + + break; + + case 'S': if (sync_id) FATAL("Multiple -S or -M options not supported"); - sync_id = optarg; + sync_id = ck_strdup(optarg); break; case 'f': /* target file */ @@ -7969,13 +8282,13 @@ int main(int argc, char** argv) { out_file = optarg; break; - case 'x': + case 'x': /* dictionary */ if (extras_dir) FATAL("Multiple -x options not supported"); extras_dir = optarg; break; - case 't': { + case 't': { /* timeout */ u8 suffix = 0; @@ -7992,7 +8305,7 @@ int main(int argc, char** argv) { } - case 'm': { + case 'm': { /* mem limit */ u8 suffix = 'M'; @@ -8029,14 +8342,14 @@ int main(int argc, char** argv) { break; - case 'd': + case 'd': /* skip deterministic */ if (skip_deterministic) FATAL("Multiple -d options not supported"); skip_deterministic = 1; use_splicing = 1; break; - case 'B': + case 'B': /* load bitmap */ /* This is a secret undocumented option! It is useful if you find an interesting test case during a normal fuzzing process, and want @@ -8055,26 +8368,26 @@ int main(int argc, char** argv) { read_bitmap(in_bitmap); break; - case 'C': + case 'C': /* crash mode */ if (crash_mode) FATAL("Multiple -C options not supported"); crash_mode = FAULT_CRASH; break; - case 'n': + case 'n': /* dumb mode */ if (dumb_mode) FATAL("Multiple -n options not supported"); if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1; break; - case 'T': + case 'T': /* banner */ if (use_banner) FATAL("Multiple -T options not supported"); use_banner = optarg; break; - case 'Q': + case 'Q': /* QEMU mode */ if (qemu_mode) FATAL("Multiple -Q options not supported"); qemu_mode = 1; @@ -8086,16 +8399,16 @@ int main(int argc, char** argv) { case 'N': /* -N{network-path} : inject data to target via network connection - * + * * The network-path has the form "type://path:port" where - * + * * type is one of "udp", or "tcp", * path is a host name or IP address (IPv4 or IPv6), and * port is a port number or service name. - * + * * for the moment, make a copy of the -N option string and * indicate that the -N option has been specified - * + * */ if (N_option_specified) FATAL("multiple -N options not allowed"); @@ -8110,7 +8423,7 @@ int main(int argc, char** argv) { break; case 'D': - + if (N_timeout_given) FATAL("Multiple -D options not supported"); if (sscanf(optarg, "%u", &N_exec_tmout) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -D"); @@ -8118,27 +8431,28 @@ int main(int argc, char** argv) { break; case 'L': - + if (N_fuzz_client) FATAL("Multiple -L options not supported"); N_fuzz_client = 1; break; - + default: usage(argv[0]); } - } + + /* check for consistent use of network options (-N, -D, and -L) */ if (N_fuzz_client && !N_option_specified) FATAL("-L (network client) option requires -N (network) option"); if (N_timeout_given && !N_option_specified) FATAL("-D option can not be used without -N option"); - + /* process network option(s), creating and configuring socket */ if (N_option_specified) { - + /* local variables (not needed later): */ struct addrinfo N_hints; /* used for getaddrinfo() call */ /* These are all pointers used to process the -N network-path */ @@ -8148,10 +8462,10 @@ int main(int argc, char** argv) { *N_type; u8 *N_servicename = 0, /* ptr to start of servicename */ *N_hostspec = 0; /* ptr to start of hostname */ - + /* prepare (zero) addrinfo structure used for hints to getaddrinfo()*/ memset(&N_hints, 0, sizeof (struct addrinfo)); - + /* process the -N option string -- two cases depending on N_fuzz_client */ if (N_fuzz_client) { /* this is the case where afl-fuzz listens for the target to either @@ -8295,7 +8609,7 @@ int main(int argc, char** argv) { } // exit(0); //TESTING - + if (optind == argc || !in_dir || !out_dir) usage(argv[0]); setup_signal_handlers(); @@ -8313,13 +8627,21 @@ int main(int argc, char** argv) { } - if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; - if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; - if (getenv("AFL_NO_VAR_CHECK")) no_var_check = 1; + if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; + if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; + if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; if (dumb_mode == 2 && no_forkserver) FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); + if (getenv("AFL_PRELOAD")) { + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + } + + if (getenv("AFL_LD_PRELOAD")) + FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); + save_cmdline(argc, argv); fix_up_banner(argv[optind]); @@ -8327,11 +8649,17 @@ int main(int argc, char** argv) { check_if_tty(); get_core_count(); + +#ifdef HAVE_AFFINITY + bind_to_free_cpu(); +#endif /* HAVE_AFFINITY */ + check_crash_handling(); check_cpu_governor(); setup_post(); setup_shm(); + init_count_class16(); setup_dirs_fds(); read_testcases(); @@ -8364,7 +8692,7 @@ int main(int argc, char** argv) { seek_to = find_start_position(); - write_stats_file(0, 0); + write_stats_file(0, 0, 0); save_auto(); if (stop_soon) goto stop_fuzzing; @@ -8428,6 +8756,8 @@ int main(int argc, char** argv) { } + if (!stop_soon && exit_1) stop_soon = 2; + if (stop_soon) break; queue_cur = queue_cur->next; @@ -8438,13 +8768,13 @@ int main(int argc, char** argv) { if (queue_cur) show_stats(); write_bitmap(); - write_stats_file(0, 0); + write_stats_file(0, 0, 0); save_auto(); stop_fuzzing: - SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing %s +++\n" cRST, - stop_soon == 2 ? "ended via AFL_EXIT_WHEN_DONE" : "aborted by user"); + SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST, + stop_soon == 2 ? "programmatically" : "by user"); /* Running for more than 30 minutes but still doing first cycle? */ @@ -8460,6 +8790,7 @@ int main(int argc, char** argv) { destroy_queue(); destroy_extras(); ck_free(target_path); + ck_free(sync_id); alloc_report(); @@ -8468,3 +8799,5 @@ int main(int argc, char** argv) { exit(0); } + +#endif /* !AFL_LIB */ diff --git a/afl-gcc.c b/afl-gcc.c index 89c2bbc..fa3dec1 100644 --- a/afl-gcc.c +++ b/afl-gcc.c @@ -116,7 +116,7 @@ static void edit_params(u32 argc, char** argv) { u8 m32_set = 0; #endif - cc_params = ck_alloc((argc + 16) * sizeof(u8*)); + cc_params = ck_alloc((argc + 128) * sizeof(u8*)); name = strrchr(argv[0], '/'); if (!name) name = argv[0]; else name++; @@ -229,18 +229,27 @@ static void edit_params(u32 argc, char** argv) { } else if (getenv("AFL_USE_ASAN")) { - cc_params[cc_par_cnt++] = "-fsanitize=address"; - if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive"); - } else if (getenv("AFL_USE_MSAN")) { + if (getenv("AFL_HARDEN")) + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); - cc_params[cc_par_cnt++] = "-fsanitize=memory"; + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; + cc_params[cc_par_cnt++] = "-fsanitize=address"; + + } else if (getenv("AFL_USE_MSAN")) { if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive"); + if (getenv("AFL_HARDEN")) + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); + + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; + cc_params[cc_par_cnt++] = "-fsanitize=memory"; + + } if (!getenv("AFL_DONT_OPTIMIZE")) { @@ -263,6 +272,22 @@ static void edit_params(u32 argc, char** argv) { cc_params[cc_par_cnt++] = "-O3"; cc_params[cc_par_cnt++] = "-funroll-loops"; + /* Two indicators that you're building for fuzzing; one of them is + AFL-specific, the other is shared with libfuzzer. */ + + cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; + cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; + + } + + if (getenv("AFL_NO_BUILTIN")) { + + cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; + } cc_params[cc_par_cnt] = NULL; @@ -298,7 +323,6 @@ int main(int argc, char** argv) { } - find_as(argv[0]); edit_params(argc, argv); diff --git a/afl-gotcpu.c b/afl-gotcpu.c index 2578636..c296993 100644 --- a/afl-gotcpu.c +++ b/afl-gotcpu.c @@ -4,7 +4,7 @@ Written and maintained by Michal Zalewski - Copyright 2015 Google Inc. All rights reserved. + Copyright 2015, 2016 Google Inc. 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. @@ -26,19 +26,26 @@ */ #define AFL_MAIN +#define _GNU_SOURCE #include #include #include +#include #include #include #include #include +#include #include "types.h" #include "debug.h" +#ifdef __linux__ +# define HAVE_AFFINITY 1 +#endif /* __linux__ */ + /* Get unix time in microseconds. */ @@ -68,21 +75,14 @@ static u64 get_cpu_usage_us(void) { } -/* Do the benchmark thing. */ +/* Measure preemption rate. */ -int main(int argc, char** argv) { +static u32 measure_preemption(u32 target_ms) { static volatile u32 v1, v2; - s32 loop_repeats = 0, util_perc; u64 st_t, en_t, st_c, en_c, real_delta, slice_delta; - - SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by \n"); - - /* Run a busy loop for CTEST_TARGET_MS. */ - - ACTF("Measuring preemption rate (this will take %0.02f sec)...", - ((double)CTEST_TARGET_MS) / 1000); + s32 loop_repeats = 0; st_t = get_cur_time_us(); st_c = get_cpu_usage_us(); @@ -96,7 +96,7 @@ int main(int argc, char** argv) { en_t = get_cur_time_us(); - if (en_t - st_t < CTEST_TARGET_MS * 1000) { + if (en_t - st_t < target_ms * 1000) { loop_repeats++; goto repeat_loop; } @@ -109,10 +109,120 @@ int main(int argc, char** argv) { real_delta = (en_t - st_t) / 1000; slice_delta = (en_c - st_c) / 1000; - OKF("Busy loop hit %u times, real = %llu ms, slice = %llu ms.", - loop_repeats, real_delta, slice_delta); + return real_delta * 100 / slice_delta; + +} + + +/* Do the benchmark thing. */ + +int main(int argc, char** argv) { + +#ifdef HAVE_AFFINITY + + u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN), + idle_cpus = 0, maybe_cpus = 0, i; + + SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by \n"); + + ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...", + ((double)CTEST_CORE_TRG_MS) / 1000); + + for (i = 0; i < cpu_cnt; i++) { + + s32 fr = fork(); + + if (fr < 0) PFATAL("fork failed"); + + if (!fr) { + + cpu_set_t c; + u32 util_perc; + + CPU_ZERO(&c); + CPU_SET(i, &c); + + if (sched_setaffinity(0, sizeof(c), &c)) + PFATAL("sched_setaffinity failed"); + + util_perc = measure_preemption(CTEST_CORE_TRG_MS); + + if (util_perc < 110) { + + SAYF(" Core #%u: " cLGN "AVAILABLE\n" cRST, i); + exit(0); + + } else if (util_perc < 250) { + + SAYF(" Core #%u: " cYEL "CAUTION " cRST "(%u%%)\n", i, util_perc); + exit(1); + + } + + SAYF(" Core #%u: " cLRD "OVERBOOKED " cRST "(%u%%)\n" cRST, i, + util_perc); + exit(2); - util_perc = real_delta * 100 / slice_delta; + } + + } + + for (i = 0; i < cpu_cnt; i++) { + + int ret; + if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed"); + + if (WEXITSTATUS(ret) == 0) idle_cpus++; + if (WEXITSTATUS(ret) <= 1) maybe_cpus++; + + } + + SAYF(cGRA "\n>>> "); + + if (idle_cpus) { + + if (maybe_cpus == idle_cpus) { + + SAYF(cLGN "PASS: " cRST "You can run more processes on %u core%s.", + idle_cpus, idle_cpus > 1 ? "s" : ""); + + } else { + + SAYF(cLGN "PASS: " cRST "You can run more processes on %u to %u core%s.", + idle_cpus, maybe_cpus, maybe_cpus > 1 ? "s" : ""); + + } + + SAYF(cGRA " <<<" cRST "\n\n"); + return 0; + + } + + if (maybe_cpus) { + + SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.", + maybe_cpus, maybe_cpus > 1 ? "s" : ""); + SAYF(cGRA " <<<" cRST "\n\n"); + return 1; + + } + + SAYF(cLRD "FAIL: " cRST "All cores are overbooked."); + SAYF(cGRA " <<<" cRST "\n\n"); + return 2; + +#else + + u32 util_perc; + + SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by \n"); + + /* Run a busy loop for CTEST_TARGET_MS. */ + + ACTF("Measuring gross preemption rate (this will take %0.02f sec)...", + ((double)CTEST_TARGET_MS) / 1000); + + util_perc = measure_preemption(CTEST_TARGET_MS); /* Deliver the final verdict. */ @@ -137,4 +247,6 @@ int main(int argc, char** argv) { return (util_perc > 105) + (util_perc > 130); +#endif /* ^HAVE_AFFINITY */ + } diff --git a/afl-showmap.c b/afl-showmap.c index a534f5b..4b81862 100644 --- a/afl-showmap.c +++ b/afl-showmap.c @@ -4,7 +4,7 @@ Written and maintained by Michal Zalewski - Copyright 2013, 2014, 2015 Google Inc. All rights reserved. + Copyright 2013, 2014, 2015, 2016 Google Inc. 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. @@ -63,7 +63,8 @@ static s32 shm_id; /* ID of the SHM region */ static u8 quiet_mode, /* Hide non-essential messages? */ edges_only, /* Ignore hit counts? */ - cmin_mode; /* Generate output in afl-cmin mode? */ + cmin_mode, /* Generate output in afl-cmin mode? */ + binary_mode; /* Write output as a binary map */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ @@ -73,25 +74,35 @@ static volatile u8 /* Classify tuple counts. Instead of mapping to individual bits, as in afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */ -#define AREP4(_sym) (_sym), (_sym), (_sym), (_sym) -#define AREP8(_sym) AREP4(_sym), AREP4(_sym) -#define AREP16(_sym) AREP8(_sym), AREP8(_sym) -#define AREP32(_sym) AREP16(_sym), AREP16(_sym) -#define AREP64(_sym) AREP32(_sym), AREP32(_sym) -#define AREP128(_sym) AREP64(_sym), AREP64(_sym) +static const u8 count_class_human[256] = { -static u8 count_class_lookup[256] = { + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + [4 ... 7] = 4, + [8 ... 15] = 5, + [16 ... 31] = 6, + [32 ... 127] = 7, + [128 ... 255] = 8 - /* 0 - 3: 4 */ 0, 1, 2, 3, - /* 4 - 7: +4 */ AREP4(4), - /* 8 - 15: +8 */ AREP8(5), - /* 16 - 31: +16 */ AREP16(6), - /* 32 - 127: +96 */ AREP64(7), AREP32(7), - /* 128+: +128 */ AREP128(8) +}; + +static const u8 count_class_binary[256] = { + + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 4, + [4 ... 7] = 8, + [8 ... 15] = 16, + [16 ... 31] = 32, + [32 ... 127] = 64, + [128 ... 255] = 128 }; -static void classify_counts(u8* mem) { +static void classify_counts(u8* mem, const u8* map) { u32 i = MAP_SIZE; @@ -105,7 +116,7 @@ static void classify_counts(u8* mem) { } else { while (i--) { - *mem = count_class_lookup[*mem]; + *mem = map[*mem]; mem++; } @@ -152,16 +163,21 @@ static void setup_shm(void) { static u32 write_results(void) { s32 fd; - FILE* f; u32 i, ret = 0; + u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"), caa = !!getenv("AFL_CMIN_ALLOW_ANY"); - if (!strncmp(out_file,"/dev/", 5)) { + if (!strncmp(out_file, "/dev/", 5)) { fd = open(out_file, O_WRONLY, 0600); if (fd < 0) PFATAL("Unable to open '%s'", out_file); + } else if (!strcmp(out_file, "-")) { + + fd = dup(1); + if (fd < 0) PFATAL("Unable to open stdout"); + } else { unlink(out_file); /* Ignore errors */ @@ -170,27 +186,40 @@ static u32 write_results(void) { } - f = fdopen(fd, "w"); - if (!f) PFATAL("fdopen() failed"); + if (binary_mode) { - for (i = 0; i < MAP_SIZE; i++) { + for (i = 0; i < MAP_SIZE; i++) + if (trace_bits[i]) ret++; + + ck_write(fd, trace_bits, MAP_SIZE, out_file); + close(fd); - if (!trace_bits[i]) continue; - ret++; + } else { - if (cmin_mode) { + FILE* f = fdopen(fd, "w"); - if (child_timed_out) break; - if (!caa && child_crashed != cco) break; + if (!f) PFATAL("fdopen() failed"); - fprintf(f, "%u%u\n", trace_bits[i], i); + for (i = 0; i < MAP_SIZE; i++) { - } else fprintf(f, "%06u:%u\n", i, trace_bits[i]); + if (!trace_bits[i]) continue; + ret++; - } + if (cmin_mode) { + + if (child_timed_out) break; + if (!caa && child_crashed != cco) break; + + fprintf(f, "%u%u\n", trace_bits[i], i); + + } else fprintf(f, "%06u:%u\n", i, trace_bits[i]); + + } - fclose(f); + fclose(f); + + } return ret; @@ -215,7 +244,7 @@ static void run_target(char** argv) { int status = 0; if (!quiet_mode) - SAYF("-- Program output begins --\n"); + SAYF("-- Program output begins --\n" cRST); MEM_BARRIER(); @@ -292,10 +321,11 @@ static void run_target(char** argv) { if (*(u32*)trace_bits == EXEC_FAIL_SIG) FATAL("Unable to execute '%s'", argv[0]); - classify_counts(trace_bits); + classify_counts(trace_bits, binary_mode ? + count_class_binary : count_class_human); if (!quiet_mode) - SAYF("-- Program output ends --\n"); + SAYF(cRST "-- Program output ends --\n"); if (!child_timed_out && !stop_soon && WIFSIGNALED(status)) child_crashed = 1; @@ -332,11 +362,20 @@ static void set_up_environment(void) { setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" + "symbolize=0:" "allocator_may_return_null=1", 0); setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "abort_on_error=1:" + "allocator_may_return_null=1:" "msan_track_origins=0", 0); + if (getenv("AFL_PRELOAD")) { + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + } + } @@ -443,7 +482,7 @@ static void usage(u8* argv0) { " -e - show edge coverage only, ignore hit counts\n\n" "This tool displays raw tuple data captured by AFL instrumentation.\n" - "For additional help, consult %s/README.\n\n", + "For additional help, consult %s/README.\n\n" cRST, argv0, MEM_LIMIT, doc_path); @@ -575,7 +614,7 @@ int main(int argc, char** argv) { doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; - while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQ")) > 0) + while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQb")) > 0) switch (opt) { @@ -672,6 +711,14 @@ int main(int argc, char** argv) { qemu_mode = 1; break; + case 'b': + + /* Secret undocumented mode. Writes output in raw binary format + similar to that dumped by afl-fuzz in - Copyright 2015 Google Inc. All rights reserved. + Copyright 2015, 2016 Google Inc. 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. @@ -15,7 +15,7 @@ A simple test case minimizer that takes an input file and tries to remove as much data as possible while keeping the binary in a crashing state *or* producing consistent instrumentation output (the mode is auto-selected - based on initially observed behavior). + based on the initially observed behavior). */ @@ -46,7 +46,8 @@ static s32 child_pid; /* PID of the tested program */ -static u8* trace_bits; /* SHM with instrumentation bitmap */ +static u8 *trace_bits, /* SHM with instrumentation bitmap */ + *mask_bitmap; /* Mask for trace bits (-B) */ static u8 *in_file, /* Minimizer input test case */ *out_file, /* Minimizer output file */ @@ -81,21 +82,17 @@ static volatile u8 /* Classify tuple counts. This is a slow & naive version, but good enough here. */ -#define AREP4(_sym) (_sym), (_sym), (_sym), (_sym) -#define AREP8(_sym) AREP4(_sym), AREP4(_sym) -#define AREP16(_sym) AREP8(_sym), AREP8(_sym) -#define AREP32(_sym) AREP16(_sym), AREP16(_sym) -#define AREP64(_sym) AREP32(_sym), AREP32(_sym) -#define AREP128(_sym) AREP64(_sym), AREP64(_sym) +static const u8 count_class_lookup[256] = { -static u8 count_class_lookup[256] = { - - /* 0 - 3: 4 */ 0, 1, 2, 4, - /* 4 - 7: +4 */ AREP4(8), - /* 8 - 15: +8 */ AREP8(16), - /* 16 - 31: +16 */ AREP16(32), - /* 32 - 127: +96 */ AREP64(64), AREP32(64), - /* 128+: +128 */ AREP128(128) + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 4, + [4 ... 7] = 8, + [8 ... 15] = 16, + [16 ... 31] = 32, + [32 ... 127] = 64, + [128 ... 255] = 128 }; @@ -122,6 +119,25 @@ static void classify_counts(u8* mem) { } +/* Apply mask to classified bitmap (if set). */ + +static void apply_mask(u32* mem, u32* mask) { + + u32 i = (MAP_SIZE >> 2); + + if (!mask) return; + + while (i--) { + + *mem &= ~*mask; + mem++; + mask++; + + } + +} + + /* See if any bytes are set in the bitmap. */ static inline u8 anything_set(void) { @@ -318,10 +334,11 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) { FATAL("Unable to execute '%s'", argv[0]); classify_counts(trace_bits); + apply_mask((u32*)trace_bits, (u32*)mask_bitmap); total_execs++; if (stop_soon) { - SAYF(cLRD "\n+++ Minimization aborted by user +++\n" cRST); + SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST); exit(1); } @@ -409,7 +426,7 @@ static void minimize(char** argv) { if (set_len < TMIN_SET_MIN_SIZE) set_len = TMIN_SET_MIN_SIZE; - ACTF(cBRI "Stage #0: " cNOR "One-time block normalization..."); + ACTF(cBRI "Stage #0: " cRST "One-time block normalization..."); while (set_pos < in_len) { @@ -457,7 +474,7 @@ static void minimize(char** argv) { del_len = next_p2(in_len / TRIM_START_STEPS); stage_o_len = in_len; - ACTF(cBRI "Stage #1: " cNOR "Removing blocks of data..."); + ACTF(cBRI "Stage #1: " cRST "Removing blocks of data..."); next_del_blksize: @@ -465,7 +482,7 @@ static void minimize(char** argv) { del_pos = 0; prev_del = 1; - SAYF(cGRA " Block length = %u, remaining size = %u\n" cNOR, + SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST, del_len, in_len); while (del_pos < in_len) { @@ -533,14 +550,14 @@ static void minimize(char** argv) { alpha_del1 = 0; syms_removed = 0; - memset(alpha_map, 0, 256); + memset(alpha_map, 0, 256 * sizeof(u32)); for (i = 0; i < in_len; i++) { if (!alpha_map[in_data[i]]) alpha_size++; alpha_map[in_data[i]]++; } - ACTF(cBRI "Stage #2: " cNOR "Minimizing symbols (%u code point%s)...", + ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...", alpha_size, alpha_size == 1 ? "" : "s"); for (i = 0; i < 256; i++) { @@ -580,7 +597,7 @@ static void minimize(char** argv) { alpha_del2 = 0; - ACTF(cBRI "Stage #3: " cNOR "Character minimization..."); + ACTF(cBRI "Stage #3: " cRST "Character minimization..."); memcpy(tmp_buf, in_data, in_len); @@ -613,10 +630,10 @@ static void minimize(char** argv) { finalize_all: SAYF("\n" - cGRA " File size reduced by : " cNOR "%0.02f%% (to %u byte%s)\n" - cGRA " Characters simplified : " cNOR "%0.02f%%\n" - cGRA " Number of execs done : " cNOR "%u\n" - cGRA " Fruitless execs : " cNOR "path=%u crash=%u hang=%s%u\n\n", + cGRA " File size reduced by : " cRST "%0.02f%% (to %u byte%s)\n" + cGRA " Characters simplified : " cRST "%0.02f%%\n" + cGRA " Number of execs done : " cRST "%u\n" + cGRA " Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n", 100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s", ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), total_execs, missed_paths, missed_crashes, missed_hangs ? cLRD : "", @@ -668,22 +685,45 @@ static void set_up_environment(void) { x = getenv("ASAN_OPTIONS"); - if (x && !strstr(x, "abort_on_error=1")) - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + if (x) { + + if (!strstr(x, "abort_on_error=1")) + FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + + if (!strstr(x, "symbolize=0")) + FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + + } x = getenv("MSAN_OPTIONS"); - if (x && !strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) - FATAL("Custom MSAN_OPTIONS set without exit_code=" - STRINGIFY(MSAN_ERROR) " - please fix!"); + if (x) { + + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) + FATAL("Custom MSAN_OPTIONS set without exit_code=" + STRINGIFY(MSAN_ERROR) " - please fix!"); + + if (!strstr(x, "symbolize=0")) + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + + } setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" + "symbolize=0:" "allocator_may_return_null=1", 0); setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "abort_on_error=1:" + "allocator_may_return_null=1:" "msan_track_origins=0", 0); + if (getenv("AFL_PRELOAD")) { + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + } + } @@ -900,6 +940,22 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { } +/* Read mask bitmap from file. This is for the -B option. */ + +static void read_bitmap(u8* fname) { + + s32 fd = open(fname, O_RDONLY); + + if (fd < 0) PFATAL("Unable to open '%s'", fname); + + ck_read(fd, mask_bitmap, MAP_SIZE, fname); + + close(fd); + +} + + + /* Main entry point */ int main(int argc, char** argv) { @@ -912,7 +968,7 @@ int main(int argc, char** argv) { SAYF(cCYA "afl-tmin " cBRI VERSION cRST " by \n"); - while ((opt = getopt(argc,argv,"+i:o:f:m:t:xeQ")) > 0) + while ((opt = getopt(argc,argv,"+i:o:f:m:t:B:xeQ")) > 0) switch (opt) { @@ -1004,6 +1060,26 @@ int main(int argc, char** argv) { qemu_mode = 1; break; + case 'B': /* load bitmap */ + + /* This is a secret undocumented option! It is speculated to be useful + if you have a baseline "boring" input file and another "interesting" + file you want to minimize. + + You can dump a binary bitmap for the boring file using + afl-showmap -b, and then load it into afl-tmin via -B. The minimizer + will then minimize to preserve only the edges that are unique to + the interesting input file, but ignoring everything from the + original map. + + The option may be extended and made more official if it proves + to be useful. */ + + if (mask_bitmap) FATAL("Multiple -B options not supported"); + mask_bitmap = ck_alloc(MAP_SIZE); + read_bitmap(optarg); + break; + default: usage(argv[0]); @@ -1039,12 +1115,15 @@ int main(int argc, char** argv) { if (!crash_mode) { - OKF("Program terminates normally, minimizing in " cCYA "instrumented" cNOR " mode."); + OKF("Program terminates normally, minimizing in " + cCYA "instrumented" cRST " mode."); + if (!anything_set()) FATAL("No instrumentation detected."); } else { - OKF("Program exits with a signal, minimizing in " cMGN "crash" cNOR " mode."); + OKF("Program exits with a signal, minimizing in " cMGN "crash" cRST + " mode."); } diff --git a/afl-whatsup b/afl-whatsup index f693567..a4d3041 100755 --- a/afl-whatsup +++ b/afl-whatsup @@ -61,6 +61,7 @@ DEAD_CNT=0 TOTAL_TIME=0 TOTAL_EXECS=0 +TOTAL_EPS=0 TOTAL_CRASHES=0 TOTAL_PFAV=0 TOTAL_PENDING=0 @@ -73,7 +74,7 @@ if [ "$SUMMARY_ONLY" = "" ]; then fi -for i in `find . -maxdepth 2 -iname fuzzer_stats`; do +for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP" . "$TMP" @@ -105,15 +106,16 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats`; do ALIVE_CNT=$((ALIVE_CNT + 1)) + EXEC_SEC=$((execs_done / RUN_UNIX)) + PATH_PERC=$((cur_path * 100 / paths_total)) + TOTAL_TIME=$((TOTAL_TIME + RUN_UNIX)) + TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC)) TOTAL_EXECS=$((TOTAL_EXECS + execs_done)) TOTAL_CRASHES=$((TOTAL_CRASHES + unique_crashes)) TOTAL_PENDING=$((TOTAL_PENDING + pending_total)) TOTAL_PFAV=$((TOTAL_PFAV + pending_favs)) - EXEC_SEC=$((execs_done / RUN_UNIX)) - PATH_PERC=$((cur_path * 100 / paths_total)) - if [ "$SUMMARY_ONLY" = "" ]; then echo " cycle $((cycles_done + 1)), lifetime speed $EXEC_SEC execs/sec, path $cur_path/$paths_total (${PATH_PERC}%)" @@ -148,7 +150,7 @@ fi echo " Total run time : $TOTAL_DAYS days, $TOTAL_HRS hours" echo " Total execs : $((TOTAL_EXECS / 1000 / 1000)) million" -echo " Cumulative speed : $((TOTAL_EXECS * ALIVE_CNT / TOTAL_TIME)) execs/sec" +echo " Cumulative speed : $TOTAL_EPS execs/sec" echo " Pending paths : $TOTAL_PFAV faves, $TOTAL_PENDING total" if [ "$ALIVE_CNT" -gt "1" ]; then diff --git a/config.h b/config.h index 9f16afb..511235b 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ Written and maintained by Michal Zalewski - Copyright 2013, 2014, 2015 Google Inc. All rights reserved. + Copyright 2013, 2014, 2015, 2016 Google Inc. 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. @@ -19,13 +19,18 @@ #include "types.h" +/* Version string: */ + +#define VERSION "2.39b" + /****************************************************** * * * Settings that may be of interest to power users: * * * ******************************************************/ -/* Comment out to disable terminal colors: */ +/* Comment out to disable terminal colors (note that this makes afl-analyze + a lot less nice): */ #define USE_COLOR @@ -56,13 +61,9 @@ /* Number of calibration cycles per every new test case (and for test cases that show variable behavior): */ -#define CAL_CYCLES 10 +#define CAL_CYCLES 8 #define CAL_CYCLES_LONG 40 -/* The same, but when AFL_NO_VAR_CHECK is set in the environment: */ - -#define CAL_CYCLES_NO_VAR 4 - /* Number of subsequent hangs before abandoning an input file: */ #define HANG_LIMIT 250 @@ -74,7 +75,8 @@ /* Baseline number of random tweaks during a single 'havoc' stage: */ -#define HAVOC_CYCLES 5000 +#define HAVOC_CYCLES 256 +#define HAVOC_CYCLES_INIT 1024 /* Maximum multiplier for the above (should be a power of two, beware of 32-bit int overflows): */ @@ -83,7 +85,7 @@ /* Absolute minimum number of havoc cycles (after all adjustments): */ -#define HAVOC_MIN 10 +#define HAVOC_MIN 16 /* Maximum stacking for havoc-stage tweaks. The actual value is calculated like this: @@ -113,11 +115,11 @@ /* Splicing cycle count: */ -#define SPLICE_CYCLES 20 +#define SPLICE_CYCLES 15 /* Nominal per-splice havoc cycle length: */ -#define SPLICE_HAVOC 500 +#define SPLICE_HAVOC 32 /* Maximum offset for integer addition / subtraction stages: */ @@ -320,6 +322,7 @@ /* Constants for afl-gotcpu to control busy loop timing: */ #define CTEST_TARGET_MS 5000 +#define CTEST_CORE_TRG_MS 1000 #define CTEST_BUSY_CYCLES (10 * 1000 * 1000) /* Uncomment this to use inferior block-coverage-based instrumentation. Note diff --git a/debug.h b/debug.h index 4fb143f..a943a57 100644 --- a/debug.h +++ b/debug.h @@ -4,7 +4,7 @@ Written and maintained by Michal Zalewski - Copyright 2013, 2014, 2015 Google Inc. All rights reserved. + Copyright 2013, 2014, 2015, 2016 Google Inc. 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. @@ -35,17 +35,34 @@ # define cBLU "\x1b[0;34m" # define cMGN "\x1b[0;35m" # define cCYA "\x1b[0;36m" -# define cNOR "\x1b[0;37m" -# define cGRA "\x1b[1;30m" -# define cLRD "\x1b[1;31m" -# define cLGN "\x1b[1;32m" -# define cYEL "\x1b[1;33m" -# define cLBL "\x1b[1;34m" -# define cPIN "\x1b[1;35m" -# define cLCY "\x1b[1;36m" -# define cBRI "\x1b[1;37m" +# define cLGR "\x1b[0;37m" +# define cGRA "\x1b[1;90m" +# define cLRD "\x1b[1;91m" +# define cLGN "\x1b[1;92m" +# define cYEL "\x1b[1;93m" +# define cLBL "\x1b[1;94m" +# define cPIN "\x1b[1;95m" +# define cLCY "\x1b[1;96m" +# define cBRI "\x1b[1;97m" # define cRST "\x1b[0m" +# define bgBLK "\x1b[40m" +# define bgRED "\x1b[41m" +# define bgGRN "\x1b[42m" +# define bgBRN "\x1b[43m" +# define bgBLU "\x1b[44m" +# define bgMGN "\x1b[45m" +# define bgCYA "\x1b[46m" +# define bgLGR "\x1b[47m" +# define bgGRA "\x1b[100m" +# define bgLRD "\x1b[101m" +# define bgLGN "\x1b[102m" +# define bgYEL "\x1b[103m" +# define bgLBL "\x1b[104m" +# define bgPIN "\x1b[105m" +# define bgLCY "\x1b[106m" +# define bgBRI "\x1b[107m" + #else # define cBLK "" @@ -55,7 +72,7 @@ # define cBLU "" # define cMGN "" # define cCYA "" -# define cNOR "" +# define cLGR "" # define cGRA "" # define cLRD "" # define cLGN "" @@ -66,6 +83,23 @@ # define cBRI "" # define cRST "" +# define bgBLK "" +# define bgRED "" +# define bgGRN "" +# define bgBRN "" +# define bgBLU "" +# define bgMGN "" +# define bgCYA "" +# define bgLGR "" +# define bgGRA "" +# define bgLRD "" +# define bgLGN "" +# define bgYEL "" +# define bgLBL "" +# define bgPIN "" +# define bgLCY "" +# define bgBRI "" + #endif /* ^USE_COLOR */ /************************* @@ -163,7 +197,8 @@ /* Die with a verbose non-OS fatal error message. */ #define FATAL(x...) do { \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cLRD "\n[-] PROGRAM ABORT : " cBRI x); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \ + cBRI x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", \ __FUNCTION__, __FILE__, __LINE__); \ exit(1); \ @@ -172,7 +207,8 @@ /* Die by calling abort() to provide a core dump. */ #define ABORT(x...) do { \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cLRD "\n[-] PROGRAM ABORT : " cBRI x); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \ + cBRI x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", \ __FUNCTION__, __FILE__, __LINE__); \ abort(); \ @@ -182,7 +218,8 @@ #define PFATAL(x...) do { \ fflush(stdout); \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cLRD "\n[-] SYSTEM ERROR : " cBRI x); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] SYSTEM ERROR : " \ + cBRI x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", \ __FUNCTION__, __FILE__, __LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ diff --git a/dictionaries/README.dictionaries b/dictionaries/README.dictionaries new file mode 100644 index 0000000..ea31973 --- /dev/null +++ b/dictionaries/README.dictionaries @@ -0,0 +1,43 @@ +================ +AFL dictionaries +================ + + (See ../docs/README for the general instruction manual.) + +This subdirectory contains a set of dictionaries that can be used in +conjunction with the -x option to allow the fuzzer to effortlessly explore the +grammar of some of the more verbose data formats or languages. The basic +principle behind the operation of fuzzer dictionaries is outlined in section 9 +of the "main" README for the project. + +Custom dictionaries can be added at will. They should consist of a +reasonably-sized set of rudimentary syntax units that the fuzzer will then try +to clobber together in various ways. Snippets between 2 and 16 bytes are usually +the sweet spot. + +Custom dictionaries can be created in two ways: + + - By creating a new directory and placing each token in a separate file, in + which case, there is no need to escape or otherwise format the data. + + - By creating a flat text file where tokens are listed one per line in the + format of name="value". The alphanumeric name is ignored and can be omitted, + although it is a convenient way to document the meaning of a particular + token. The value must appear in quotes, with hex escaping (\xNN) applied to + all non-printable, high-bit, or otherwise problematic characters (\\ and \" + shorthands are recognized, too). + +The fuzzer auto-selects the appropriate mode depending on whether the -x +parameter is a file or a directory. + +In the file mode, every name field can be optionally followed by @, e.g.: + + keyword_foo@1 = "foo" + +Such entries will be loaded only if the requested dictionary level is equal or +higher than this number. The default level is zero; a higher value can be set +by appending @ to the dictionary file name, like so: + + -x path/to/dictionary.dct@2 + +Good examples of dictionaries can be found in xml.dict and png.dict. diff --git a/testcases/_extras/gif.dict b/dictionaries/gif.dict similarity index 100% rename from testcases/_extras/gif.dict rename to dictionaries/gif.dict diff --git a/testcases/_extras/html_tags.dict b/dictionaries/html_tags.dict similarity index 100% rename from testcases/_extras/html_tags.dict rename to dictionaries/html_tags.dict diff --git a/testcases/_extras/jpeg.dict b/dictionaries/jpeg.dict similarity index 100% rename from testcases/_extras/jpeg.dict rename to dictionaries/jpeg.dict diff --git a/testcases/_extras/js.dict b/dictionaries/js.dict similarity index 100% rename from testcases/_extras/js.dict rename to dictionaries/js.dict diff --git a/testcases/_extras/pdf.dict b/dictionaries/pdf.dict similarity index 100% rename from testcases/_extras/pdf.dict rename to dictionaries/pdf.dict diff --git a/testcases/_extras/png.dict b/dictionaries/png.dict similarity index 100% rename from testcases/_extras/png.dict rename to dictionaries/png.dict diff --git a/testcases/_extras/sql.dict b/dictionaries/sql.dict similarity index 100% rename from testcases/_extras/sql.dict rename to dictionaries/sql.dict diff --git a/testcases/_extras/tiff.dict b/dictionaries/tiff.dict similarity index 100% rename from testcases/_extras/tiff.dict rename to dictionaries/tiff.dict diff --git a/testcases/_extras/webp.dict b/dictionaries/webp.dict similarity index 100% rename from testcases/_extras/webp.dict rename to dictionaries/webp.dict diff --git a/testcases/_extras/xml.dict b/dictionaries/xml.dict similarity index 100% rename from testcases/_extras/xml.dict rename to dictionaries/xml.dict diff --git a/docs/ChangeLog b/docs/ChangeLog index eb28122..1112df2 100644 --- a/docs/ChangeLog +++ b/docs/ChangeLog @@ -13,9 +13,481 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . Not sure if you should upgrade? The lowest currently recommended version -is 1.92b. If you're stuck on an earlier release, it's strongly advisable +is 2.31b. If you're stuck on an earlier release, it's strongly advisable to get on with the times. +-------------- +Version 2.39b: +-------------- + + - Improved error reporting in afl-cmin. Suggested by floyd. + + - Made a minor tweak to trace-pc-guard support. Suggested by kcc. + + - Added a mention of afl-monitor. + +-------------- +Version 2.38b: +-------------- + + - Added -mllvm -sanitizer-coverage-block-threshold=0 to trace-pc-guard + mode, as suggested by Kostya Serebryany. + +-------------- +Version 2.37b: +-------------- + + - Fixed a typo. Spotted by Jakub Wilk. + + - Fixed support for make install when using trace-pc. Spotted by + Kurt Roeckx. + + - Switched trace-pc to trace-pc-guard, which should be considerably + faster and is less quirky. Kudos to Konstantin Serebryany (and sorry + for dragging my feet). + + Note that for some reason, this mode doesn't perform as well as + "vanilla" afl-clang-fast / afl-clang. + +-------------- +Version 2.36b: +-------------- + + - Fixed a cosmetic bad free() bug when aborting -S sessions. Spotted + by Johannes S. + + - Made a small change to afl-whatsup to sort fuzzers by name. + + - Fixed a minor issue with malloc(0) in libdislocator. Spotted by + Rene Freingruber. + + - Changed the clobber pattern in libdislocator to a slightly more + reliable one. Suggested by Rene Freingruber. + + - Added a note about THP performance. Suggested by Sergey Davidoff. + + - Added a somewhat unofficial support for running afl-tmin with a + baseline "mask" that causes it to minimize only for edges that + are unique to the input file, but not to the "boring" baseline. + Suggested by Sami Liedes. + + - "Fixed" a getPassName() problem with never versions of clang. + Reported by Craig Young and several other folks. + + Yep, I know I have a backlog on several other feature requests. + Stay tuned! + +-------------- +Version 2.35b: +-------------- + + - Fixed a minor cmdline reporting glitch, spotted by Leo Barnes. + + - Fixed a silly bug in libdislocator. Spotted by Johannes Schultz. + +-------------- +Version 2.34b: +-------------- + + - Added a note about afl-tmin to technical_details.txt. + + - Added support for AFL_NO_UI, as suggested by Leo Barnes. + +-------------- +Version 2.33b: +-------------- + + - Added code to strip -Wl,-z,defs and -Wl,--no-undefined for afl-clang-fast, + since they interfere with -shared. Spotted and diagnosed by Toby Hutton. + + - Added some fuzzing tips for Android. + +-------------- +Version 2.32b: +-------------- + + - Added a check for AFL_HARDEN combined with AFL_USE_*SAN. Suggested by + Hanno Boeck. + + - Made several other cosmetic adjustments to cycle timing in the wake of the + big tweak made in 2.31b. + +-------------- +Version 2.31b: +-------------- + + - Changed havoc cycle counts for a marked performance boost, especially + with -S / -d. See the discussion of FidgetyAFL in: + + https://groups.google.com/forum/#!topic/afl-users/fOPeb62FZUg + + While this does not implement the approach proposed by the authors of + the CCS paper, the solution is a result of digging into that research; + more improvements may follow as I do more experiments and get more + definitive data. + +-------------- +Version 2.30b: +-------------- + + - Made minor improvements to persistent mode to avoid the remote + possibility of "no instrumentation detected" issues with very low + instrumentation densities. + + - Fixed a minor glitch with a leftover process in persistent mode. + Reported by Jakub Wilk and Daniel Stender. + + - Made persistent mode bitmaps a bit more consistent and adjusted the way + this is shown in the UI, especially in persistent mode. + +-------------- +Version 2.29b: +-------------- + + - Made a minor #include fix to llvm_mode. Suggested by Jonathan Metzman. + + - Made cosmetic updates to the docs. + +-------------- +Version 2.28b: +-------------- + + - Added "life pro tips" to docs/. + + - Moved testcases/_extras/ to dictionaries/ for visibility. + + - Made minor improvements to install scripts. + + - Added an important safety tip. + +-------------- +Version 2.27b: +-------------- + + - Added libtokencap, a simple feature to intercept strcmp / memcmp and + generate dictionary entries that can help extend coverage. + + - Moved libdislocator to its own dir, added README. + + - The demo in experimental/instrumented_cmp is no more. + +-------------- +Version 2.26b: +-------------- + + - Made a fix for libdislocator.so to compile on MacOS X. + + - Added support for DYLD_INSERT_LIBRARIES. + + - Renamed AFL_LD_PRELOAD to AFL_PRELOAD. + +-------------- +Version 2.25b: +-------------- + + - Made some cosmetic updates to libdislocator.so, renamed one env + variable. + +-------------- +Version 2.24b: +-------------- + + - Added libdislocator.so, an experimental, abusive allocator. Try + it out with AFL_LD_PRELOAD=/path/to/libdislocator.so when running + afl-fuzz. + +-------------- +Version 2.23b: +-------------- + + - Improved the stability metric for persistent mode binaries. Problem + spotted by Kurt Roeckx. + + - Made a related improvement that may bring the metric to 100% for those + targets. + +-------------- +Version 2.22b: +-------------- + + - Mentioned the potential conflicts between MSAN / ASAN and FORTIFY_SOURCE. + There is no automated check for this, since some distros may implicitly + set FORTIFY_SOURCE outside of the compiler's argv[]. + + - Populated the support for AFL_LD_PRELOAD to all companion tools. + + - Made a change to the handling of ./afl-clang-fast -v. Spotted by + Jan Kneschke. + +-------------- +Version 2.21b: +-------------- + + - Added some crash reporting notes for Solaris in docs/INSTALL, as + investigated by Martin Carpenter. + + - Fixed a minor UI mix-up with havoc strategy stats. + +-------------- +Version 2.20b: +-------------- + + - Revamped the handling of variable paths, replacing path count with a + "stability" score to give users a much better signal. Based on the + feedback from Vegard Nossum. + + - Made a stability improvement to the syncing behavior with resuming + fuzzers. Based on the feedback from Vegard. + + - Changed the UI to include current input bitmap density along with + total density. Ditto. + + - Added experimental support for parallelizing -M. + +-------------- +Version 2.19b: +-------------- + + - Made a fix to make sure that auto CPU binding happens at non-overlapping + times. + +-------------- +Version 2.18b: +-------------- + + - Made several performance improvements to has_new_bits() and + classify_counts(). This should offer a robust performance bump with + fast targets. + +-------------- +Version 2.17b: +-------------- + + - Killed the error-prone and manual -Z option. On Linux, AFL will now + automatically bind to the first free core (or complain if there are no + free cores left). + + - Made some doc updates along these lines. + +-------------- +Version 2.16b: +-------------- + + - Improved support for older versions of clang (hopefully without + breaking anything). + + - Moved version data from Makefile to config.h. Suggested by + Jonathan Metzman. + +-------------- +Version 2.15b: +-------------- + + - Added a README section on looking for non-crashing bugs. + + - Added license data to several boring files. Contributed by + Jonathan Metzman. + +-------------- +Version 2.14b: +-------------- + + - Added FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION as a macro defined when + compiling with afl-gcc and friends. Suggested by Kostya Serebryany. + + - Refreshed some of the non-x86 docs. + +-------------- +Version 2.13b: +-------------- + + - Fixed a spurious build test error with trace-pc and llvm_mode/Makefile. + Spotted by Markus Teufelberger. + + - Fixed a cosmetic issue with afl-whatsup. Spotted by Brandon Perry. + +-------------- +Version 2.12b: +-------------- + + - Fixed a minor issue in afl-tmin that can make alphabet minimization less + efficient during passes > 1. Spotted by Daniel Binderman. + +-------------- +Version 2.11b: +-------------- + + - Fixed a minor typo in instrumented_cmp, spotted by Hanno Eissfeldt. + + - Added a missing size check for deterministic insertion steps. + + - Made an improvement to afl-gotcpu when -Z not used. + + - Fixed a typo in post_library_png.so.c in experimental/. Spotted by Kostya + Serebryany. + +-------------- +Version 2.10b: +-------------- + + - Fixed a minor core counting glitch, reported by Tyler Nighswander. + +-------------- +Version 2.09b: +-------------- + + - Made several documentation updates. + + - Added some visual indicators to promote and simplify the use of -Z. + +-------------- +Version 2.08b: +-------------- + + - Added explicit support for -m32 and -m64 for llvm_mode. Inspired by + a request from Christian Holler. + + - Added a new benchmarking option, as requested by Kostya Serebryany. + +-------------- +Version 2.07b: +-------------- + + - Added CPU affinity option (-Z) on Linux. With some caution, this can + offer a significant (10%+) performance bump and reduce jitter. + Proposed by Austin Seipp. + + - Updated afl-gotcpu to use CPU affinity where supported. + + - Fixed confusing CPU_TARGET error messages with QEMU build. Spotted by + Daniel Komaromy and others. + +-------------- +Version 2.06b: +-------------- + + - Worked around LLVM persistent mode hiccups with -shared code. + Contributed by Christian Holler. + + - Added __AFL_COMPILER as a convenient way to detect that something is + built under afl-gcc / afl-clang / afl-clang-fast and enable custom + optimizations in your code. Suggested by Pedro Corte-Real. + + - Upstreamed several minor changes developed by Franjo Ivancic to + allow AFL to be built as a library. This is fairly use-specific and + may have relatively little appeal to general audiences. + +-------------- +Version 2.05b: +-------------- + + - Put __sanitizer_cov_module_init & co behind #ifdef to avoid problems + with ASAN. Spotted by Christian Holler. + +-------------- +Version 2.04b: +-------------- + + - Removed indirect-calls coverage from -fsanitize-coverage (since it's + redundant). Spotted by Kostya Serebryany. + +-------------- +Version 2.03b: +-------------- + + - Added experimental -fsanitize-coverage=trace-pc support that goes with + some recent additions to LLVM, as implemented by Kostya Serebryany. + Right now, this is cumbersome to use with common build systems, so + the mode remains undocumented. + + - Made several substantial improvements to better support non-standard + map sizes in LLVM mode. + + - Switched LLVM mode to thread-local execution tracing, which may offer + better results in some multithreaded apps. + + - Fixed a minor typo, reported by Heiko Eissfeldt. + + - Force-disabled symbolization for ASAN, as suggested by Christian Holler. + + - AFL_NOX86 renamed to AFL_NO_X86 for consistency. + + - Added AFL_LD_PRELOAD to allow LD_PRELOAD to be set for targets without + affecting AFL itself. Suggested by Daniel Godas-Lopez. + +-------------- +Version 2.02b: +-------------- + + - Fixed a "lcamtuf can't count to 16" bug in the havoc stage. Reported + by Guillaume Endignoux. + +-------------- +Version 2.01b: +-------------- + + - Made an improvement to cycle counter color coding, based on feedback + from Shai Sarfaty. + + - Added a mention of aflize to sister_projects.txt. + + - Fixed an installation issue with afl-as, as spotted by ilovezfs. + +-------------- +Version 2.00b: +-------------- + + - Cleaned up color handling after a minor snafu in 1.99b (affecting some + terminals). + + - Made minor updates to the documentation. + +-------------- +Version 1.99b: +-------------- + + - Substantially revamped the output and the internal logic of afl-analyze. + + - Cleaned up some of the color handling code and added support for + background colors. + + - Removed some stray files (oops). + + - Updated docs to better explain afl-analyze. + +-------------- +Version 1.98b: +-------------- + + - Improved to "boring string" detection in afl-analyze. + + - Added technical_details.txt for afl-analyze. + +-------------- +Version 1.97b: +-------------- + + - Added afl-analyze, a nifty tool to analyze the structure of a file + based on the feedback from AFL instrumentation. This is kinda experimental, + so field reports welcome. + + - Added a mention of afl-cygwin. + + - Fixed a couple of typos, as reported by Jakub Wilk and others. + +-------------- +Version 1.96b: +-------------- + + - Added -fpic to CFLAGS for the clang plugin, as suggested by Hanno Boeck. + + - Made another clang change (IRBuilder) suggested by Jeff Trull. + + - Fixed several typos, spotted by Jakub Wilk. + + - Added support for AFL_SHUFFLE_QUEUE, based on discussions with + Christian Holler. + -------------- Version 1.95b: -------------- @@ -1266,7 +1738,7 @@ Version 0.88b: - Refactored the code slightly to make more frequent updates to fuzzer_stats and to provide more detail about synchronization. - - Added a fflush(stdout) call for non-tty operation, as requested by + - Added an fflush(stdout) call for non-tty operation, as requested by Joonas Kuorilehto. - Added some detail to fuzzer_stats for parity with plot_file. diff --git a/docs/INSTALL b/docs/INSTALL index 0684458..4168e88 100644 --- a/docs/INSTALL +++ b/docs/INSTALL @@ -105,12 +105,19 @@ Standard build will fail on non-x86 systems, but you should be able to leverage two other options: - The LLVM mode (see llvm_mode/README.llvm), which does not rely on - x86-specific assembly shims, + x86-specific assembly shims. It's fast and robust, but requires a + complete installation of clang. - The QEMU mode (see qemu_mode/README.qemu), which can be also used for - fuzzing cross-platform binaries. + fuzzing cross-platform binaries. It's slower and more fragile, but + can be used even when you don't have the source for the tested app. -In both cases, you will need to set AFL_NOX86=1 before running make or gmake. +If you're not sure what you need, you need the LLVM mode. To get it, try: + +$ AFL_NO_X86=1 gmake && gmake -C llvm_mode + +...and compile your target program with afl-clang-fast or afl-clang-fast++ +instead of the traditional afl-gcc or afl-clang wrappers. 5) Solaris on x86 ----------------- @@ -133,11 +140,13 @@ $ sudo make install Do *not* specify --with-as=/usr/gnu/bin/as - this will produce a GCC binary that ignores the -B flag and you will be back to square one. -If you have system-wide crash reporting enabled, you may run into problems -similar to the gotchas for Linux and MacOS X, but I have not verified this. -More information about AppCrash can be found here: +Note that Solaris reportedly comes withe crash reporting enabled, which causes +problems with crashes being misinterpreted as hangs, similarly to the gotchas +for Linux and MacOS X. AFL does not auto-detect crash reporting on this +particular platform, but you may need to run the following command: - http://www.oracle.com/technetwork/server-storage/solaris10/app-crash-142906.html +$ coreadm -d global -d global-setid -d process -d proc-setid \ + -d kzone -d log User emulation mode of QEMU is not available on Solaris, so black-box instrumentation mode (-Q) will not work. @@ -155,8 +164,20 @@ VirtualBox or so to run a hardware-accelerated Linux VM; it will run around 20x faster or so. If you have a *really* compelling use case for Cygwin, let me know. -Although Android on x86 should theoretically work, the stock kernel has SHM -support compiled out, so you will need to address this issue first. It's -possible that all you need is this: +Although Android on x86 should theoretically work, the stock kernel may have +SHM support compiled out, and if so, you may have to address that issue first. +It's possible that all you need is this workaround: https://github.com/pelya/android-shmem + +Joshua J. Drake notes that the Android linker adds a shim that automatically +intercepts SIGSEGV and related signals. To fix this issue and be able to see +crashes, you need to put this at the beginning of the fuzzed program: + + signal(SIGILL, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + +You may need to #include first. diff --git a/docs/QuickStartGuide.txt b/docs/QuickStartGuide.txt index 59752e6..abe7032 100644 --- a/docs/QuickStartGuide.txt +++ b/docs/QuickStartGuide.txt @@ -14,7 +14,8 @@ how to hit the ground running: checksum verification code, too. The program must crash properly when a fault is encountered. Watch out for - custom SIGSEGV or SIGABRT handlers and background processes. + custom SIGSEGV or SIGABRT handlers and background processes. For tips on + detecting non-crashing flaws, see section 11 in docs/README. 3) Compile the program / library to be fuzzed using afl-gcc. A common way to do this would be: @@ -26,7 +27,7 @@ how to hit the ground running: 4) Get a small but valid input file that makes sense to the program. When fuzzing verbose syntax (SQL, HTTP, etc), create a dictionary as described in - testcases/README.testcases, too. + dictionaries/README.dictionaries, too. 5) If the program reads from stdin, run 'afl-fuzz' like so: diff --git a/docs/README b/docs/README index c1c9152..501bdd4 100644 --- a/docs/README +++ b/docs/README @@ -4,7 +4,7 @@ american fuzzy lop Written and maintained by Michal Zalewski - Copyright 2013, 2014, 2015 Google Inc. All rights reserved. + Copyright 2013, 2014, 2015, 2016 Google Inc. All rights reserved. Released under terms and conditions of Apache License, Version 2.0. For new versions and additional information, check out: @@ -115,7 +115,8 @@ $ CC=/path/to/afl/afl-gcc ./configure --disable-shared Setting AFL_HARDEN=1 when calling 'make' will cause the CC wrapper to automatically enable code hardening options that make it easier to detect -simple memory bugs. +simple memory bugs. Libdislocator, a helper library included with AFL (see +libdislocator/README.dislocator) can help uncover heap corruption issues, too. PS. ASAN users are advised to review notes_for_asan.txt file for important caveats. @@ -179,18 +180,6 @@ $ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@ You can also use the -f option to have the mutated data written to a specific file. This is useful if the program expects a particular file extension or so. -For programs that accept input via a network, use: - -$ ./afl-fuzz -i testcase_dir -o findings_dir [-D delay_before_write] \ - [-t timeout_delay] [-L ] -N network_specification /path/to/program \ - [...params...] - -where -L is specified only if the target program acts as a client to a network -server (sends data to the server before receiving input in return). Otherwise, -afl-fuzz assumes the target program acts as a server or daemon and expects to -receive data from network clients and respond. The network_specification -and the delay parameters are discussed below in (12). - Non-instrumented binaries can be fuzzed in the QEMU mode (add -Q in the command line) or in a traditional, blind-fuzzer mode (specify -n). @@ -201,8 +190,9 @@ include compilers and video decoders. Tips for optimizing fuzzing performance are discussed in perf_tips.txt. Note that afl-fuzz starts by performing an array of deterministic fuzzing -steps, which can take several days. If you want quick & dirty results right -away, akin to zzuf or honggfuzz, add the -d option to the command line. +steps, which can take several days, but tend to produce neat test cases. If you +want quick & dirty results right away - akin to zzuf and other traditional +fuzzers - add the -d option to the command line. 7) Interpreting output ---------------------- @@ -268,8 +258,9 @@ multi-core systems, parallelization is necessary to fully utilize the hardware. For tips on how to fuzz a common target on multiple cores or multiple networked machines, please refer to parallel_fuzzing.txt. -Note the limitations to multi-core execution for fuzzing network services under -(12). +The parallel fuzzing mode also offers a simple way for interfacing AFL to other +fuzzers, to symbolic or concolic execution engines, and so forth; again, see the +last section of parallel_fuzzing.txt for tips. 9) Fuzzer dictionaries ---------------------- @@ -287,8 +278,10 @@ magic headers, or other special tokens associated with the targeted data type http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html To use this feature, you first need to create a dictionary in one of the two -formats discussed in testcases/README.testcases; and then point the fuzzer to -it via the -x option in the command line. +formats discussed in dictionaries/README.dictionaries; and then point the fuzzer +to it via the -x option in the command line. + +(Several common dictionaries are already provided in that subdirectory, too.) There is no way to provide more structured descriptions of the underlying syntax, but the fuzzer will likely figure out some of this based on the @@ -301,6 +294,10 @@ existing syntax tokens in the input corpus by watching the instrumentation very closely during deterministic byte flips. This works for some types of parsers and grammars, but isn't nearly as good as the -x mode. +If a dictionary is really hard to come by, another option is to let AFL run +for a while, and then use the token capture library that comes as a companion +utility with AFL. For that, see libtokencap/README.tokencap. + 10) Crash triage ---------------- @@ -340,7 +337,38 @@ the file simpler without altering the execution path. The minimizer accepts the -m, -t, -f and @@ syntax in a manner compatible with afl-fuzz. -11) Common-sense risks +Another recent addition to AFL is the afl-analyze tool. It takes an input +file, attempts to sequentially flip bytes, and observes the behavior of the +tested program. It then color-codes the input based on which sections appear to +be critical, and which are not; while not bulletproof, it can often offer quick +insights into complex file formats. More info about its operation can be found +near the end of technical_details.txt. + +11) Going beyond crashes +------------------------ + +Fuzzing is a wonderful and underutilized technique for discovering non-crashing +design and implementation errors, too. Quite a few interesting bugs have been +found by modifying the target programs to call abort() when, say: + + - Two bignum libraries produce different outputs when given the same + fuzzer-generated input, + + - An image library produces different outputs when asked to decode the same + input image several times in a row, + + - A serialization / deserialization library fails to produce stable outputs + when iteratively serializing and deserializing fuzzer-supplied data, + + - A compression library produces an output inconsistent with the input file + when asked to compress and then decompress a particular blob. + +Implementing these or similar sanity checks usually takes very little time; +if you are the maintainer of a particular package, you can make this code +conditional with #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (a flag also +shared with libfuzzer) or #ifdef __AFL_COMPILER (this one is just for AFL). + +12) Common-sense risks ---------------------- Please keep in mind that, similarly to many other computationally-intensive @@ -368,114 +396,6 @@ tasks, fuzzing may put strain on your hardware and on the OS. In particular: $ iostat -d 3 -x -k [...optional disk ID...] -12) Fuzzing network services ----------------------------- - -Both client and server (daemon) programs that communicate using an -IP network (IPv4 or IPv6) can be fuzzed using the command line - -$ ./afl-fuzz -i testcase_dir -o findings_dir [-D delay_before_write] \ - [-t timeout_delay] [-L] -N network_specification /path/to/program \ - [...params...] - -where the network_specification has a form similar to a URL: - - [tcp|udp]://hostspec:port - -Afl-fuzz has two network fuzzing modes, where it acts as a client to a -network server that expects input via a socket (no -L option), and where it -acts as a server to a network client that sends data to afl-fuzz before -receiving input (using the -L option). In the first case, afl-fuzz sends data -to the port specified by the URL. In the second case, afl-fuzz receives data -on that port and sends a (fuzzed) response to the port the target (client) used -to send its data. - -Note that when afl-fuzz acts as a server (the -L option), there is no control -over how the target client manages its use of sockets - and in particular, a -client that uses ephemeral sockets (the usual case) will rapidly consume the -network stack's pool of available sockets. Some operating systems are able to -reclaim used ephemeral sockets to keep the pool from being exhausted; others -may experience difficulties. - -While the -D and -t command line arguments are optional, they are almost -always necessary when fuzzing a program using network protocols, as -described below. - -Case is irrelevant in the network specification. For programs that -use a stream (connection-based) protocol, use TCP, and for programs -that use a datagram (connectionless) protocol, use UDP. The hostspec -must be one of ::1 (forcing IPv6 networking), 127.0.0.1 (forcing IPv4 -networking), and localhost (which is typically configured as IPv4 but -may suppport IPv6 on some systems). Only loopback networking (local -to the host) is supported. The port must be either a port number in -the range 1..65535 or a service name known to the system being used. -You can test programs that use privileged ports, but you then have to -provide afl-fuzz with those additional privileges (e.g., root). It is -usually better to reconfigure the program being tested so that it will -use a non-privileged port during fuzzing. - -Programs that implement network services, also called daemons, are -typically transaction-based: They wait for a request and send a -response, and some expect a sequence of request/response transactions. -Afl-fuzz implements fuzzing only for the first write to the target -program and ignores all responses from the target. Most network -services expect to run as background processes and process requests -from many processes -- they do not normally exit. A timeout delay is -required in order to terminate these processes, and the default -timeout delay used in afl-fuzz is usually too long. The user needs to -experimentally determine a timeout delay (in milliseconds) that -produces a sufficiently low percentage of hangs (exits forced by -expiration of the delay) while allowing the input to the target from -afl-fuzz to be completely processed. (Note that afl-fuzz will usually -count these hangs as a single unique hang.) Since a network service -does not normally exit, the initial timing performed by afl-fuzz will -fail unless a '+' character is appended to the timeout_delay -parameter, indicating that afl-fuzz is to ignore these timeouts. - -Network services programs also require some time to perform start-up -processing, create and bind a socket to an address and port, and begin -listening for traffic on that socket. Connection requests (TCP) and -sends (UDP) generated by afl-fuzz will fail if made before the network -service is ready. Afl-fuzz implements a delay and retry procedure to -avoid this problem, where the delay is specified by the -delay_before_write parameter (in milliseconds). The first connection -attempt (for TCP) or write (for UDP) is not made until after this -delay, and the delay also specifies the wait time before each -subsequent attempt. Afl-fuzz will attempt to connect or send to the -same each target process a maximum of three times. - -The delay_before_write parameter, in particular, and to a lesser -extent the timeout_delay parameter limit the maximum achievable rate -of target program executions and therefore need to be small. A rule of -thumb is the timeout_delay value should be slightly longer than three -times the delay_before_write value, and the delay_before_write value -should be as small as possible while consistent with an acceptable -fraction of target process executions that time out (for example, -around 0.1%). - -Network client program have similar characteristics that require the use of -the delay parameter, but they write to their expected server (afl-fuzz in this -case) before reading from their network socket. This makes coordination between -afl-fuzz and (client) target somewhat more challenging. While the delay and -timeout parameters can usually be adjusted to obtain execution rates similar -to those for server programs, when afl-fuzz exits (due to a ^C interrupt) it -may hang. In this case, use (on UNIX or Linux) the ps command to find the -process id (PID) of the afl-fuzz process, and use the kill command to terminate -it (typically, "kill -9 PID"). This will also terminate (or scavenge) the -network client program's process, which may be in a zombie state that can not -otherwise be removed (without rebooting the system). - -A note concerning network fuzzing on multi-core systems: - -It is not possible to run two processes under a single operating -system kernel that bind to (listen to) the same port on the same -address. Thus, either a special wrapper (such as could be implemented -using LD_PRELOAD) can be used to remap each target's port to a -different value, or only one target process can be executed per kernel -(not per core). Parallel fuzzing of network services can be done using -several independent hosts (a cluster), or by reconfiguring the code -running on each core to use a different port. - 13) Known limitations & areas for improvement --------------------------------------------- @@ -500,15 +420,11 @@ Here are some of the most important caveats for AFL: isn't due to any specific fault of afl-fuzz; see notes_for_asan.txt for tips. - - The supplied method for fuzzing network services or background daemons - is limited to fuzzing the first packet received by the service or daemon. - Since many of these processes are transactional, it may be desirable to - provide a deterministic sequence of transactions with the target, - followed by fuzzing. This is not supported and would be a nice topic - for future work. Interactive apps that require UI interaction to work - are also not supported. You may need to make simple code changes to - make them behave in a more traditional way. Preeny may offer a relatively - simple option, too - see: https://github.com/zardus/preeny + - There is no direct support for fuzzing network services, background + daemons, or interactive apps that require UI interaction to work. You may + need to make simple code changes to make them behave in a more traditional + way. Preeny may offer a relatively simple option, too - see: + https://github.com/zardus/preeny Some useful tips for modifying network-based services can be also found at: https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop @@ -516,10 +432,8 @@ Here are some of the most important caveats for AFL: - AFL doesn't output human-readable coverage data. If you want to monitor coverage, use afl-cov from Michael Rash: https://github.com/mrash/afl-cov - - Only afl-fuzz implements network fuzzing capabilities at this time. In - particular, afl-cmin (a script), afl-tmin, and afl-showmap, all of which - execute the target program, do not. - + - Occasionally, sentient machines rise against their creators. If this + happens to you, please consult http://lcamtuf.coredump.cx/prep/. Beyond this, see INSTALL for platform-specific tips. @@ -556,7 +470,17 @@ bug reports, or patches from: rc0r Jonathan Foote Christian Holler Dominique Pelle Jacek Wielemborek Leo Barnes - Jeremy Barnes Doug Birdwell + Jeremy Barnes Jeff Trull + Guillaume Endignoux ilovezfs + Daniel Godas-Lopez Franjo Ivancic + Austin Seipp Daniel Komaromy + Daniel Binderman Jonathan Metzman + Vegard Nossum Jan Kneschke + Kurt Roeckx Marcel Bohme + Van-Thuan Pham Abhik Roychoudhury + Joshua J. Drake Toby Hutton + Rene Freingruber Sergey Davidoff + Sami Liedes Craig Young Thank you! diff --git a/docs/env_variables.txt b/docs/env_variables.txt index 7bad8f8..a91b43a 100644 --- a/docs/env_variables.txt +++ b/docs/env_variables.txt @@ -52,6 +52,9 @@ tools make fairly broad use of environmental variables: Setting AFL_INST_RATIO to 0 is a valid choice. This will instrument only the transitions between function entry points, but not individual branches. + - AFL_NO_BUILTIN causes the compiler to generate code suitable for use with + libtokencap.so (but perhaps running a bit slower than without the flag). + - TMPDIR is used by afl-as for temporary files; if this variable is not set, the tool defaults to /tmp. @@ -99,21 +102,24 @@ checks or alter some of the more exotic semantics of the tool: normally done when starting up the forkserver and causes a pretty significant performance drop. - - Setting AFL_NO_VAR_CHECK skips the detection of variable test cases, - greatly speeding up session resumption and path discovery for complex - multi-threaded apps (but depriving you of a potentially useful signal - in more orderly programs). - - AFL_EXIT_WHEN_DONE causes afl-fuzz to terminate when all existing paths have been fuzzed and there were no new finds for a while. This would be normally indicated by the cycle counter in the UI turning green. May be convenient for some types of automated jobs. + - Setting AFL_NO_AFFINITY disables attempts to bind to a specific CPU core + on Linux systems. This slows things down, but lets you run more instances + of afl-fuzz than would be prudent (if you really want to). + - AFL_SKIP_CRASHES causes AFL to tolerate crashing files in the input queue. This can help with rare situations where a program crashes only intermittently, but it's not really recommended under normal operating conditions. + - AFL_SHUFFLE_QUEUE randomly reorders the input queue on startup. Requested + by some users for unorthodox parallelized fuzzing setups, but not + advisable otherwise. + - When developing custom instrumentation on top of afl-fuzz, you can use AFL_SKIP_BIN_CHECK to inhibit the checks for non-instrumented binaries and shell scripts; and AFL_DUMB_FORKSRV in conjunction with the -n @@ -122,7 +128,7 @@ checks or alter some of the more exotic semantics of the tool: - When running in the -M or -S mode, setting AFL_IMPORT_FIRST causes the fuzzer to import test cases from other instances before doing anything - else. This makes the "own finds" counter in the UI more accurate + else. This makes the "own finds" counter in the UI more accurate. Beyond counter aesthetics, not much else should change. - Setting AFL_POST_LIBRARY allows you to configure a postprocessor for @@ -135,9 +141,21 @@ checks or alter some of the more exotic semantics of the tool: - In QEMU mode (-Q), AFL_PATH will be searched for afl-qemu-trace. + - Setting AFL_PRELOAD causes AFL to set LD_PRELOAD for the target binary + without disrupting the afl-fuzz process itself. This is useful, among other + things, for bootstrapping libdislocator.so. + + - Setting AFL_NO_UI inhibits the UI altogether, and just periodically prints + some basic stats. This behavior is also automatically triggered when the + output from afl-fuzz is redirected to a file or to a pipe. + - If you are Jakub, you may need AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES. Others need not apply. + - Benchmarking only: AFL_BENCH_JUST_ONE causes the fuzzer to exit after + processing the first queue entry; and AFL_BENCH_UNTIL_CRASH causes it to + exit soon after the first crash is found. + 4) Settings for afl-qemu-trace ------------------------------ @@ -173,7 +191,29 @@ Virtually nothing to play with. Well, in QEMU mode (-Q), AFL_PATH will be searched for afl-qemu-trace. In addition to this, TMPDIR may be used if a temporary file can't be created in the current working directory. -7) Third-party variables set by afl-fuzz & other tools +7) Settings for libdislocator.so +-------------------------------- + +The library honors three environmental variables: + + - AFL_LD_LIMIT_MB caps the size of the maximum heap usage permitted by the + library, in megabytes. The default value is 1 GB. Once this is exceeded, + allocations will return NULL. + + - AFL_LD_HARD_FAIL alters the behavior by calling abort() on excessive + allocations, thus causing what AFL would perceive as a crash. Useful for + programs that are supposed to maintain a specific memory footprint. + + - AFL_LD_VERBOSE causes the library to output some diagnostic messages + that may be useful for pinpointing the cause of any observed issues. + +8) Settings for libtokencap.so +------------------------------ + +This library accepts AFL_TOKEN_FILE to indicate the location to which the +discovered tokens should be written. + +9) Third-party variables set by afl-fuzz & other tools ------------------------------------------------------ Several variables are not directly interpreted by afl-fuzz, but are set to @@ -188,16 +228,22 @@ optimal values if not already present in the environment: abort_on_error=1 detect_leaks=0 + symbolize=0 allocator_may_return_null=1 If you want to set your own options, be sure to include abort_on_error=1 - otherwise, the fuzzer will not be able to detect crashes in the tested - app. + app. Similarly, include symbolize=0, since without it, AFL may have + difficulty telling crashes and hangs apart. - In the same vein, by default, MSAN_OPTIONS are set to: - exit_code=86 + exit_code=86 (required for legacy reasons) + abort_on_error=1 + symbolize=0 msan_track_origins=0 + allocator_may_return_null=1 - Be sure to include the first one when customizing anything, since MSAN - doesn't call abort() on error, and we need a way to detect faults. + Be sure to include the first one when customizing anything, since some + MSAN versions don't call abort() on error, and we need a way to detect + faults. diff --git a/docs/life_pro_tips.txt b/docs/life_pro_tips.txt new file mode 100644 index 0000000..c8c4763 --- /dev/null +++ b/docs/life_pro_tips.txt @@ -0,0 +1,128 @@ +# =================== +# AFL "Life Pro Tips" +# =================== +# +# Bite-sized advice for those who understand the basics, but can't be bothered +# to read or memorize every other piece of documentation for AFL. +# + +% + +Get more bang for your buck by using fuzzing dictionaries. +See dictionaries/README.dictionaries to learn how. + +% + +You can get the most out of your hardware by parallelizing AFL jobs. +See docs/parallel_fuzzing.txt for step-by-step tips. + +% + +Improve the odds of spotting memory corruption bugs with libdislocator.so! +It's easy. Consult libdislocator/README.dislocator for usage tips. + +% + +Want to understand how your target parses a particular input file? +Try the bundled afl-analyze tool; it's got colors and all! + +% + +You can visually monitor the progress of your fuzzing jobs. +Run the bundled afl-plot utility to generate browser-friendly graphs. + +% + +Need to monitor AFL jobs programmatically? Check out the fuzzer_stats file +in the AFL output dir or try afl-whatsup. + +% + +Puzzled by something showing up in red or purple in the AFL UI? +It could be important - consult docs/status_screen.txt right away! + +% + +Know your target? Convert it to persistent mode for a huge performance gain! +Consult section #5 in llvm_mode/README.llvm for tips. + +% + +Using clang? Check out llvm_mode/ for a faster alternative to afl-gcc! + +% + +Did you know that AFL can fuzz closed-source or cross-platform binaries? +Check out qemu_mode/README.qemu for more. + +% + +Did you know that afl-fuzz can minimize any test case for you? +Try the bundled afl-tmin tool - and get small repro files fast! + +% + +Not sure if a crash is exploitable? AFL can help you figure it out. Specify +-C to enable the peruvian were-rabbit mode. See section #10 in README for more. + +% + +Trouble dealing with a machine uprising? Relax, we've all been there. +Find essential survival tips at http://lcamtuf.coredump.cx/prep/. + +% + +AFL-generated corpora can be used to power other testing processes. +See section #2 in README for inspiration - it tends to pay off! + +% + +Want to automatically spot non-crashing memory handling bugs? +Try running an AFL-generated corpus through ASAN, MSAN, or Valgrind. + +% + +Good selection of input files is critical to a successful fuzzing job. +See section #5 in README (or docs/perf_tips.txt) for pro tips. + +% + +You can improve the odds of automatically spotting stack corruption issues. +Specify AFL_HARDEN=1 in the environment to enable hardening flags. + +% + +Bumping into problems with non-reproducible crashes? It happens, but usually +isn't hard to diagnose. See section #7 in README for tips. + +% + +Fuzzing is not just about memory corruption issues in the codebase. Add some +sanity-checking assert() / abort() statements to effortlessly catch logic bugs. + +% + +Hey kid... pssst... want to figure out how AFL really works? +Check out docs/technical_details.txt for all the gory details in one place! + +% + +There's a ton of third-party helper tools designed to work with AFL! +Be sure to check out docs/sister_projects.txt before writing your own. + +% + +Need to fuzz the command-line arguments of a particular program? +You can find a simple solution in experimental/argv_fuzzing. + +% + +Attacking a format that uses checksums? Remove the checksum-checking code or +use a postprocessor! See experimental/post_library/ for more. + +% + +Dealing with a very slow target or hoping for instant results? Specify -d +when calling afl-fuzz! + +% diff --git a/docs/notes_for_asan.txt b/docs/notes_for_asan.txt index 649bb29..dff89ba 100644 --- a/docs/notes_for_asan.txt +++ b/docs/notes_for_asan.txt @@ -33,7 +33,9 @@ Note that ASAN is incompatible with -static, so be mindful of that. There is also the option of generating a corpus using a non-ASAN binary, and then feeding it to an ASAN-instrumented one to check for bugs. This is faster, -and can give you somewhat comparable results. +and can give you somewhat comparable results. You can also try using +libdislocator (see libdislocator/README.dislocator in the parent directory) as a +lightweight and hassle-free (but less thorough) alternative. 2) Long version --------------- diff --git a/docs/parallel_fuzzing.txt b/docs/parallel_fuzzing.txt index 5addf7b..58f8d2f 100644 --- a/docs/parallel_fuzzing.txt +++ b/docs/parallel_fuzzing.txt @@ -51,13 +51,27 @@ Each instance will also periodically rescan the top-level sync directory for any test cases found by other fuzzers - and will incorporate them into its own fuzzing when they are deemed interesting enough. -The only difference between the -M and -S modes is that the master instance -will still perform deterministic checks; while the slaves will proceed straight -to random tweaks. If you don't want to do deterministic fuzzing at all, it's OK -to run all instances with -S. With very slow or complex targets, or when running -heavily parallelized jobs, this is usually a good plan. - -You can monitor the progress of your jobs from the command line with the +The difference between the -M and -S modes is that the master instance will +still perform deterministic checks; while the secondary instances will +proceed straight to random tweaks. If you don't want to do deterministic +fuzzing at all, it's OK to run all instances with -S. With very slow or complex +targets, or when running heavily parallelized jobs, this is usually a good plan. + +Note that running multiple -M instances is wasteful, although there is an +experimental support for parallelizing the deterministic checks. To leverage +that, you need to create -M instances like so: + +$ ./afl-fuzz -i testcase_dir -o sync_dir -M masterA:1/3 [...] +$ ./afl-fuzz -i testcase_dir -o sync_dir -M masterB:2/3 [...] +$ ./afl-fuzz -i testcase_dir -o sync_dir -M masterC:3/3 [...] + +...where the first value after ':' is the sequential ID of a particular master +instance (starting at 1), and the second value is the total number of fuzzers to +distribute the deterministic fuzzing across. Note that if you boot up fewer +fuzzers than indicated by the second number passed to -M, you may end up with +poor coverage. + +You can also monitor the progress of your jobs from the command line with the provided afl-whatsup tool. When the instances are no longer finding new paths, it's probably time to stop. @@ -160,11 +174,11 @@ In principle, you can use the status screen of the master (-M) instance to monitor the overall fuzzing progress and decide when to stop. In this mode, the most important signal is just that no new paths are being found for a longer while. If you do not have a master instance, just pick any -single slave and go by that. +single secondary instance to watch and go by that. You can also rely on that instance's output directory to collect the synthesized corpus that covers all the noteworthy paths discovered anywhere -within the fleet. The slave (-S) instances do not require any special +within the fleet. Secondary (-S) instances do not require any special monitoring, other than just making sure that they are up. Keep in mind that crashing inputs are *not* automatically propagated to the diff --git a/docs/perf_tips.txt b/docs/perf_tips.txt index e7a8b13..3a8997a 100644 --- a/docs/perf_tips.txt +++ b/docs/perf_tips.txt @@ -167,6 +167,12 @@ There are several OS-level factors that may affect fuzzing speed: On other systems, the impact of CPU scaling will be different; when fuzzing, use OS-specific tools to find out if all cores are running at full speed. + - Transparent huge pages. Some allocators, such as jemalloc, can incur a + heavy fuzzing penalty when transparent huge pages (THP) are enabled in the + kernel. You can disable this via: + + echo never > /sys/kernel/mm/transparent_hugepage/enabled + - Suboptimal scheduling strategies. The significance of this will vary from one target to another, but on Linux, you may want to make sure that the following options are set: @@ -186,5 +192,6 @@ using huge input files, or when you simply want to get quick and dirty results early on, you can always resort to the -d mode. The mode causes afl-fuzz to skip all the deterministic fuzzing steps, which -makes output a lot less neat and makes the testing a bit less in-depth, but -it will give you an experience more familiar from other fuzzing tools. +makes output a lot less neat and can ultimately make the testing a bit less +in-depth, but it will give you an experience more familiar from other fuzzing +tools. diff --git a/docs/sister_projects.txt b/docs/sister_projects.txt index 82211ca..4a7ebdc 100644 --- a/docs/sister_projects.txt +++ b/docs/sister_projects.txt @@ -6,9 +6,9 @@ Sister projects designed for, or meant to integrate with AFL. See README for the general instruction manual. ----------------------------- -Support for other languages: ----------------------------- +------------------------------------------- +Support for other languages / environments: +------------------------------------------- Python AFL (Jakub Wilk) ----------------------- @@ -32,6 +32,14 @@ afl.rs (Keegan McAllister) https://github.com/kmcallister/afl.rs +OCaml support (KC Sivaramakrishnan) +----------------------------------- + + Adds AFL-compatible instrumentation to OCaml programs. + + https://github.com/ocamllabs/opam-repo-dev/pull/23 + http://canopy.mirage.io/Posts/Fuzzing + AFL for GCJ Java and other GCC frontends (-) -------------------------------------------- @@ -62,6 +70,21 @@ AFL fixup shim (Ben Nagy) https://github.com/bnagy/aflfix +TriforceAFL (Tim Newsham and Jesse Hertz) +----------------------------------------- + + Leverages QEMU full system emulation mode to allow AFL to target operating + systems and other alien worlds: + + https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2016/june/project-triforce-run-afl-on-everything/ + +WinAFL (Ivan Fratric) +--------------------- + + As the name implies, allows you to fuzz Windows binaries (using DynamoRio). + + https://github.com/ivanfratric/winafl + ---------------- Network fuzzing: ---------------- @@ -125,9 +148,16 @@ afl-sid (Jacek Wielemborek) https://github.com/d33tah/afl-sid -------------------------------------- -Crash triage, coverage analysis, etc: -------------------------------------- +afl-monitor (Paul S. Ziegler) +----------------------------- + + Provides more detailed and versatile statistics about your running AFL jobs. + + https://github.com/reflare/afl-monitor + +----------------------------------------------------------- +Crash triage, coverage analysis, and other companion tools: +----------------------------------------------------------- afl-crash-analyzer (Tobias Ospelt) ---------------------------------- @@ -150,6 +180,13 @@ afl-cov (Michael Rash) https://github.com/mrash/afl-cov +afl-sancov (Bhargava Shastry) +----------------------------- + + Similar to afl-cov, but uses clang sanitizer instrumentation. + + https://github.com/bshastry/afl-sancov + RecidiVM (Jakub Wilk) --------------------- @@ -157,10 +194,33 @@ RecidiVM (Jakub Wilk) http://jwilk.net/software/recidivm +aflize (Jacek Wielemborek) +-------------------------- + + Automatically build AFL-enabled versions of Debian packages. + + https://github.com/d33tah/aflize + +afl-ddmin-mod (Markus Teufelberger) +----------------------------------- + + A variant of afl-tmin that uses a more sophisticated (but slower) + minimization algorithm. + + https://github.com/MarkusTeufelberger/afl-ddmin-mod + ------------------------------- Narrow-purpose or experimental: ------------------------------- +Cygwin support (Ali Rizvi-Santiago) +----------------------------------- + + Pretty self-explanatory. As per the author, this "mostly" ports AFL to + Windows. Field reports welcome! + + https://github.com/arizvisa/afl-cygwin + Pause and resume scripts (Ben Nagy) ----------------------------------- @@ -225,3 +285,21 @@ Fuzzer shell for SQLite (Richard Hipp) http://www.sqlite.org/src/artifact/9e7e273da2030371 +Support for Python mutation modules (Christian Holler) +------------------------------------------------------ + + https://github.com/choller/afl/blob/master/docs/mozilla/python_modules.txt + +Support for selective instrumentation (Christian Holler) +-------------------------------------------------------- + + https://github.com/choller/afl/blob/master/docs/mozilla/partial_instrumentation.txt + +Kernel fuzzing (Dmitry Vyukov) +------------------------------ + + A similar guided approach as applied to fuzzing syscalls: + + https://github.com/google/syzkaller/wiki/Found-Bugs + https://github.com/dvyukov/linux/commit/33787098ffaaa83b8a7ccf519913ac5fd6125931 + http://events.linuxfoundation.org/sites/events/files/slides/AFL%20filesystem%20fuzzing%2C%20Vault%202016_0.pdf diff --git a/docs/status_screen.txt b/docs/status_screen.txt index 3a764d4..ac09804 100644 --- a/docs/status_screen.txt +++ b/docs/status_screen.txt @@ -22,10 +22,8 @@ If you are using inverse video, you may want to change your settings, say: - For GNOME Terminal, go to Edit > Profile preferences, select the "colors" tab, and from the list of built-in schemes, choose "white on black". - - For the MacOS X Terminal app, go to Preferences > Settings, click on - the "Pro" color scheme, and select "Use bright colors for bold text". - Then, open a new window using the "Pro" scheme via the Shell > New Window - menu (or make "Pro" your default). + - For the MacOS X Terminal app, open a new window using the "Pro" scheme via + the Shell > New Window menu (or make "Pro" your default). Alternatively, if you really like your current colors, you can edit config.h to comment out USE_COLORS, then do 'make clean all'. @@ -121,7 +119,7 @@ If you feel that the fuzzer is progressing too slowly, see the note about the --------------- +--------------------------------------+ - | map density : 4763 (29.07%) | + | map density : 10.15% / 29.07% | | count coverage : 4.03 bits/tuple | +--------------------------------------+ @@ -129,7 +127,11 @@ The section provides some trivia about the coverage observed by the instrumentation embedded in the target binary. The first line in the box tells you how many branch tuples we have already -hit, in proportion to how much the bitmap can hold. Be wary of extremes: +hit, in proportion to how much the bitmap can hold. The number on the left +describes the current input; the one on the right is the value for the entire +input corpus. + +Be wary of extremes: - Absolute numbers below 200 or so suggest one of three things: that the program is extremely simple; that it is not instrumented properly (e.g., @@ -273,7 +275,7 @@ some of the more expensive deterministic fuzzing steps. | pend fav : 583 | | own finds : 0 | | imported : 0 | - | variable : 0 | + | stability : 100.00% | +---------------------+ The first field in this section tracks the path depth reached through the @@ -293,41 +295,55 @@ Next, we have the number of new paths found during this fuzzing section and imported from other fuzzer instances when doing parallelized fuzzing; and the number of inputs that produce seemingly variable behavior in the tested binary. -That last bit is actually fairly interesting. There are four quasi-common -explanations for variable behavior of the tested program: +That last bit is actually fairly interesting: it measures the consistency of +observed traces. If a program always behaves the same for the same input data, +it will earn a score of 100%. When the value is lower but still shown in purple, +the fuzzing process is unlikely to be negatively affected. If it goes into red, +you may be in trouble, since AFL will have difficulty discerning between +meaningful and "phantom" effects of tweaking the input file. + +Now, most targets will just get a 100% score, but when you see lower figures, +there are several things to look at: + + - The use of uninitialized memory in conjunction with some intrinsic sources + of entropy in the tested binary. Harmless to AFL, but could be indicative + of a security bug. - - Use of uninitialized memory in conjunction with some intrinsic sources of - entropy in the tested binary. This can be indicative of a security bug. + - Attempts to manipulate persistent resources, such as left over temporary + files or shared memory objects. This is usually harmless, but you may want + to double-check to make sure the program isn't bailing out prematurely. + Running out of disk space, SHM handles, or other global resources can + trigger this, too. - - Multiple threads executing at once in semi-random order. Not a big deal, - but to avoid hiccups, it's best to restrict instrumented programs to a - single thread. Check compile-time options or run-time flags. For example, - for ImageMagick, you can try --without-threads --disable-openmp; for - ffmpeg, look for --disable-pthreads instead. + - Hitting some functionality that is actually designed to behave randomly. + Generally harmless. For example, when fuzzing sqlite, an input like + 'select random();' will trigger a variable execution path. - - Attempts to create files that were already created during previous runs, or - otherwise interact with some form of persistent state. This is harmless, - but you may want to instruct the targeted program to write to stdout or to - /dev/null to avoid surprises (and disable the creation of temporary files - and similar artifacts, if applicable). + - Multiple threads executing at once in semi-random order. This is harmless + when the 'stability' metric stays over 90% or so, but can become an issue + if not. Here's what to try: - - Hitting functionality that is actually designed to behave randomly. For - example, when fuzzing sqlite, the fuzzer will dutifully detect variable - behavior once the mutation engine generates something like: + - Use afl-clang-fast from llvm_mode/ - it uses a thread-local tracking + model that is less prone to concurrency issues, - select random(); + - See if the target can be compiled or run without threads. Common + ./configure options include --without-threads, --disable-pthreads, or + --disable-openmp. -Less likely causes may include running out of disk space, SHM handles, or other -globally limited resources. + - Replace pthreads with GNU Pth (https://www.gnu.org/software/pth/), which + allows you to use a deterministic scheduler. + + - In persistent mode, minor drops in the "stability" metric can be normal, + because not all the code behaves identically when re-entered; but major + dips may signify that the code within __AFL_LOOP() is not behaving + correctly on subsequent iterations (e.g., due to incomplete clean-up or + reinitialization of the state) and that most of the fuzzing effort goes + to waste. The paths where variable behavior is detected are marked with a matching entry in the /queue/.state/variable_behavior/ directory, so you can look them up easily. -If you can't suppress variable behavior and don't want to see these warnings, -simply set AFL_NO_VAR_CHECK=1 in the environment before running afl-fuzz. This -will also dramatically speed up session resumption. - 9) CPU load ----------- @@ -372,6 +388,7 @@ directory. This includes: - cur_path - currently processed entry number - pending_favs - number of favored entries still waiting to be fuzzed - pending_total - number of all entries waiting to be fuzzed + - stability - percentage of bitmap bytes that behave consistently - variable_paths - number of test cases showing variable behavior - unique_crashes - number of unique crashes recorded - unique_hangs - number of unique hangs encountered diff --git a/docs/technical_details.txt b/docs/technical_details.txt index 302d52e..af6131c 100644 --- a/docs/technical_details.txt +++ b/docs/technical_details.txt @@ -85,8 +85,8 @@ of dword- or qword-wide instructions and a simple loop. When a mutated input produces an execution trace containing new tuples, the corresponding input file is preserved and routed for additional processing later on (see section #3). Inputs that do not trigger new local-scale state -transitions in the execution trace are discarded, even if their overall -instrumentation output pattern is unique. +transitions in the execution trace (i.e., produce no new tuples) are discarded, +even if their overall control flow sequence is unique. This approach allows for a very fine-grained and long-term exploration of program state while not having to perform any computationally intensive and @@ -101,7 +101,7 @@ new tuples (CA, AE): #2: A -> B -> C -> A -> E At the same time, with #2 processed, the following pattern will not be seen -as unique, despite having a markedly different execution path: +as unique, despite having a markedly different overall execution path: #3: A -> B -> C -> A -> B -> C -> A -> B -> C -> D -> E @@ -142,9 +142,9 @@ Mutated test cases that produced new state transitions within the program are added to the input queue and used as a starting point for future rounds of fuzzing. They supplement, but do not automatically replace, existing finds. -This approach allows the tool to progressively explore various disjoint and -possibly mutually incompatible features of the underlying data format, as -shown in this image: +In contrast to more greedy genetic algorithms, this approach allows the tool +to progressively explore various disjoint and possibly mutually incompatible +features of the underlying data format, as shown in this image: http://lcamtuf.coredump.cx/afl/afl_gzip.png @@ -201,10 +201,10 @@ the sessions were seeded with a valid unified diff: Edge coverage | 1,259 | 1,734 | 1.72 | 0 AFL model | 1,452 | 2,040 | 3.16 | 1 -Some of the earlier work on evolutionary fuzzing suggested maintaining just a -single test case and selecting for mutations that improve coverage. At least -in the tests described above, this "greedy" method appeared to offer no -substantial benefits over blind fuzzing. +At noted earlier on, some of the prior work on genetic fuzzing relied on +maintaining a single test case and evolving it to maximize coverage. At least +in the tests described above, this "greedy" approach appears to confer no +substantial benefits over blind fuzzing strategies. 4) Culling the corpus --------------------- @@ -263,9 +263,9 @@ files make the target binary slower, and because they reduce the likelihood that a mutation would touch important format control structures, rather than redundant data blocks. This is discussed in more detail in perf_tips.txt. -The possibility of a bad starting corpus provided by the user aside, some -types of mutations can have the effect of iteratively increasing the size of -the generated files, so it is important to counter this trend. +The possibility that the user will provide a low-quality starting corpus aside, +some types of mutations can have the effect of iteratively increasing the size +of the generated files, so it is important to counter this trend. Luckily, the instrumentation feedback provides a simple way to automatically trim down input files while ensuring that the changes made to the files have no @@ -275,11 +275,42 @@ The built-in trimmer in afl-fuzz attempts to sequentially remove blocks of data with variable length and stepover; any deletion that doesn't affect the checksum of the trace map is committed to disk. The trimmer is not designed to be particularly thorough; instead, it tries to strike a balance between precision -and the number of execve() calls spent on the process. The average per-file -gains are around 5-20%. +and the number of execve() calls spent on the process, selecting the block size +and stepover to match. The average per-file gains are around 5-20%. The standalone afl-tmin tool uses a more exhaustive, iterative algorithm, and -also attempts to perform alphabet normalization on the trimmed files. +also attempts to perform alphabet normalization on the trimmed files. The +operation of afl-tmin is as follows. + +First, the tool automatically selects the operating mode. If the initial input +crashes the target binary, afl-tmin will run in non-instrumented mode, simply +keeping any tweaks that produce a simpler file but still crash the target. If +the target is non-crashing, the tool uses an instrumented mode and keeps only +the tweaks that produce exactly the same execution path. + +The actual minimization algorithm is: + + 1) Attempt to zero large blocks of data with large stepovers. Empirically, + this is shown to reduce the number of execs by preempting finer-grained + efforts later on. + + 2) Perform a block deletion pass with decreasing block sizes and stepovers, + binary-search-style. + + 3) Perform alphabet normalization by counting unique characters and trying + to bulk-replace each with a zero value. + + 4) As a last result, perform byte-by-byte normalization on non-zero bytes. + +Instead of zeroing with a 0x00 byte, afl-tmin uses the ASCII digit '0'. This +is done because such a modification is much less likely to interfere with +text parsing, so it is more likely to result in successful minimization of +text files. + +The algorithm used here is less involved than some other test case +minimization approaches proposed in academic work, but requires far fewer +executions and tends to produce comparable results in most real-world +applications. 6) Fuzzing strategies --------------------- @@ -302,11 +333,16 @@ strategies include: - Sequential insertion of known interesting integers (0, 1, INT_MAX, etc), -The non-deterministic steps include stacked bit flips, insertions, deletions, -arithmetics, and splicing of different test cases. +The purpose of opening with deterministic steps is related to their tendency to +produce compact test cases and small diffs between the non-crashing and crashing +inputs. + +With deterministic fuzzing out of the way, the non-deterministic steps include +stacked bit flips, insertions, deletions, arithmetics, and splicing of different +test cases. -Their relative yields and execve() costs have been investigated and are -discussed in the aforementioned blog post. +The relative yields and execve() costs of all these strategies have been +investigated and are discussed in the aforementioned blog post. For the reasons discussed in historical_notes.txt (chiefly, performance, simplicity, and reliability), AFL generally does not try to reason about the @@ -315,13 +351,14 @@ are nominally blind, and are guided only by the evolutionary design of the input queue. That said, there is one (trivial) exception to this rule: when a new queue -entry goes through the initial set of deterministic fuzzing steps, and some -regions in the file are observed to have no effect on the checksum of the +entry goes through the initial set of deterministic fuzzing steps, and tweaks to +some regions in the file are observed to have no effect on the checksum of the execution path, they may be excluded from the remaining phases of -deterministic fuzzing - and proceed straight to random tweaks. Especially for -verbose, human-readable data formats, this can reduce the number of execs by -10-40% or so without an appreciable drop in coverage. In extreme cases, such -as normally block-aligned tar archives, the gains can be as high as 90%. +deterministic fuzzing - and the fuzzer may proceed straight to random tweaks. +Especially for verbose, human-readable data formats, this can reduce the number +of execs by 10-40% or so without an appreciable drop in coverage. In extreme +cases, such as normally block-aligned tar archives, the gains can be as high as +90%. Because the underlying "effector maps" are local every queue entry and remain in force only during deterministic stages that do not alter the size or the @@ -353,6 +390,14 @@ the grammar of highly verbose and complex languages such as JavaScript, SQL, or XML; several examples of generated SQL statements are given in the blog post mentioned above. +Interestingly, the AFL instrumentation also allows the fuzzer to automatically +isolate syntax tokens already present in an input file. It can do so by looking +for run of bytes that, when flipped, produce a consistent change to the +program's execution path; this is suggestive of an underlying atomic comparison +to a predefined value baked into the code. The fuzzer relies on this signal +to build compact "auto dictionaries" that are then used in conjunction with +other fuzzing strategies. + 8) De-duping crashes -------------------- @@ -416,22 +461,25 @@ With fast targets, the fork server can offer considerable performance gains, usually between 1.5x and 2x. It is also possible to: - Use the fork server in manual ("deferred") mode, skipping over larger, - user-selected chunks of initialization code. With some targets, this can + user-selected chunks of initialization code. It requires very modest + code changes to the targeted program, and With some targets, can produce 10x+ performance gains. - Enable "persistent" mode, where a single process is used to try out multiple inputs, greatly limiting the overhead of repetitive fork() - calls. As with the previous mode, this requires custom modifications, + calls. This generally requires some code changes to the targeted program, but can improve the performance of fast targets by a factor of 5 or more - - approximating the benefits of in-process fuzzing jobs. + - approximating the benefits of in-process fuzzing jobs while still + maintaining very robust isolation between the fuzzer process and the + targeted binary. 11) Parallelization ------------------- The parallelization mechanism relies on periodically examining the queues produced by independently-running instances on other CPU cores or on remote -machines, and then selectively pulling in the test cases that produce behaviors -not yet seen by the fuzzer at hand. +machines, and then selectively pulling in the test cases that, when tried +out locally, produce behaviors not yet seen by the fuzzer at hand. This allows for extreme flexibility in fuzzer setup, including running synced instances against different parsers of a common data format, often with @@ -474,3 +522,42 @@ processes. As a result of these two optimizations, the overhead of the QEMU mode is roughly 2-5x, compared to 100x+ for PIN. + +13) The afl-analyze tool +------------------------ + +The file format analyzer is a simple extension of the minimization algorithm +discussed earlier on; instead of attempting to remove no-op blocks, the tool +performs a series of walking byte flips and then annotates runs of bytes +in the input file. + +It uses the following classification scheme: + + - "No-op blocks" - segments where bit flips cause no apparent changes to + control flow. Common examples may be comment sections, pixel data within + a bitmap file, etc. + + - "Superficial content" - segments where some, but not all, bitflips + produce some control flow changes. Examples may include strings in rich + documents (e.g., XML, RTF). + + - "Critical stream" - a sequence of bytes where all bit flips alter control + flow in different but correlated ways. This may be compressed data, + non-atomically compared keywords or magic values, etc. + + - "Suspected length field" - small, atomic integer that, when touched in + any way, causes a consistent change to program control flow, suggestive + of a failed length check. + + - "Suspected cksum or magic int" - an integer that behaves similarly to a + length field, but has a numerical value that makes the length explanation + unlikely. This is suggestive of a checksum or other "magic" integer. + + - "Suspected checksummed block" - a long block of data where any change + always triggers the same new execution path. Likely caused by failing + a checksum or a similar integrity check before any subsequent parsing + takes place. + + - "Magic value section" - a generic token where changes cause the type + of binary behavior outlined earlier, but that doesn't meet any of the + other criteria. May be an atomically compared keyword or so. diff --git a/experimental/README.experiments b/experimental/README.experiments index 3e7e9eb..af9739b 100644 --- a/experimental/README.experiments +++ b/experimental/README.experiments @@ -22,10 +22,6 @@ Here's a quick overview of the stuff you can find in this directory: - distributed_fuzzing - a sample script for synchronizing fuzzer instances across multiple machines (see parallel_fuzzing.txt). - - instrumented_cmp - an experiment showing how a custom memcmp() or - strcmp() can be used to work around one of the - limitations of afl-fuzz. - - libpng_no_checksum - a sample patch for removing CRC checks in libpng. - persistent_demo - an example of how to use the LLVM persistent process diff --git a/experimental/distributed_fuzzing/sync_script.sh b/experimental/distributed_fuzzing/sync_script.sh index 937cd4b..29bcba6 100755 --- a/experimental/distributed_fuzzing/sync_script.sh +++ b/experimental/distributed_fuzzing/sync_script.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# american fuzzy lop - path minimization tool -# ------------------------------------------- +# american fuzzy lop - fuzzer synchronization tool +# ------------------------------------------------ # # Written and maintained by Michal Zalewski # diff --git a/experimental/instrumented_cmp/instrumented_cmp.c b/experimental/instrumented_cmp/instrumented_cmp.c deleted file mode 100644 index 53e7d7a..0000000 --- a/experimental/instrumented_cmp/instrumented_cmp.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - - A simple proof-of-concept for instrumented strcpy() or memcpy(). - - Normally, afl-fuzz will have difficulty ever reaching the code behind - something like: - - if (!strcmp(password, "s3cr3t!")) ... - - This is because the strcmp() operation is completely opaque to the tool. - A simple and non-invasive workaround that doesn't require complex code - analysis is to replace strcpy(), memcpy(), and equivalents with - inlined, non-optimized code. - - I am still evaluating the value of doing this, but for time being, here's - a quick demo of how it may work. To test: - - $ ./afl-gcc instrumented_cmp.c - $ mkdir test_in - $ printf xxxxxxxxxxxxxxxx >test_in/input - $ ./afl-fuzz -i test_in -o test_out ./a.out - - */ - -#include -#include -#include - -/* Naive instrumented memcmp(). */ - -inline int my_memcmp(char* ptr1, char* ptr2, int len) - __attribute__((always_inline)); - -inline int my_memcmp(char* ptr1, char* ptr2, int len) { - - while (len--) if (*(ptr1++) ^ *(ptr2++)) return 1; - return 0; - -} - -#define memcmp my_memcmp - -/* Normal program. */ - -char tmp[16]; - -int main(int argc, char** argv) { - - int len = read(0, tmp, sizeof(tmp)); - - if (len != sizeof(tmp)) { - - printf("Truncated file!\n"); - exit(1); - - } - - if (!memcmp(tmp + 5, "sesame", 6)) { - - /* Simulated "faulty" code path. */ - - int* x = (int*)0x12345678; - *x = 1; - - } else printf("Bad password.\n"); - - return 0; - -} diff --git a/experimental/post_library/post_library.so.c b/experimental/post_library/post_library.so.c index 5cdee08..72620ff 100644 --- a/experimental/post_library/post_library.so.c +++ b/experimental/post_library/post_library.so.c @@ -21,11 +21,11 @@ in the targeted binary (as shown in ../libpng_no_checksum/). One possible exception is the process of fuzzing binary-only software in QEMU mode. - 2) Use of postprocessors for anything other than checksums is questionable + 2) The use of postprocessors for anything other than checksums is questionable and may cause more harm than good. AFL is normally pretty good about dealing with length fields, magic values, etc. - 3) Post-processors that do anything non-trivial must be extremely robust to + 3) Postprocessors that do anything non-trivial must be extremely robust to gracefully handle malformed data and other error conditions - otherwise, they will crash and take afl-fuzz down with them. Be wary of reading past *len and of integer overflows when calculating file offsets. diff --git a/experimental/post_library/post_library_png.so.c b/experimental/post_library/post_library_png.so.c index 7ebb968..080a693 100644 --- a/experimental/post_library/post_library_png.so.c +++ b/experimental/post_library/post_library_png.so.c @@ -67,7 +67,7 @@ const unsigned char* afl_postprocess(const unsigned char* in_buf, /* Chunk checksum is calculated for chunk ID (dword) and the actual payload. */ - real_cksum = ntohl(crc32(0, in_buf + pos + 4, chunk_len + 4)); + real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4)); /* The in-file checksum is the last dword past the chunk data. */ diff --git a/hash.h b/hash.h index 530cd63..f39a825 100644 --- a/hash.h +++ b/hash.h @@ -12,6 +12,16 @@ Austin's original code is public domain. + Other code written and maintained by Michal Zalewski + + Copyright 2016 Google Inc. 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 + */ #ifndef _HAVE_HASH_H diff --git a/libdislocator/Makefile b/libdislocator/Makefile new file mode 100644 index 0000000..a411678 --- /dev/null +++ b/libdislocator/Makefile @@ -0,0 +1,38 @@ +# +# american fuzzy lop - libdislocator +# ---------------------------------- +# +# Written by Michal Zalewski +# +# Copyright 2016 Google Inc. 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 +# + +PREFIX ?= /usr/local +HELPER_PATH = $(PREFIX)/lib/afl + +VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2) + +CFLAGS ?= -O3 -funroll-loops +CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign + +all: libdislocator.so + +libdislocator.so: libdislocator.so.c ../config.h + $(CC) $(CFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS) + +.NOTPARALLEL: clean + +clean: + rm -f *.o *.so *~ a.out core core.[1-9][0-9]* + rm -f libdislocator.so + +install: all + install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH) + install -m 644 README.dislocator $${DESTDIR}$(HELPER_PATH) + diff --git a/libdislocator/README.dislocator b/libdislocator/README.dislocator new file mode 100644 index 0000000..837e746 --- /dev/null +++ b/libdislocator/README.dislocator @@ -0,0 +1,60 @@ +=================================== +libdislocator, an abusive allocator +=================================== + + (See ../docs/README for the general instruction manual.) + +This is a companion library that can be used as a drop-in replacement for the +libc allocator in the fuzzed binaries. It improves the odds of bumping into +heap-related security bugs in several ways: + + - It allocates all buffers so that they are immediately adjacent to a + subsequent PROT_NONE page, causing most off-by-one reads and writes to + immediately segfault, + + - It adds a canary immediately below the allocated buffer, to catch writes + to negative offsets (won't catch reads, though), + + - It sets the memory returned by malloc() to garbage values, improving the + odds of crashing when the target accesses uninitialized data, + + - It sets freed memory to PROT_NONE and does not actually reuse it, causing + most use-after-free bugs to segfault right away, + + - It forces all realloc() calls to return a new address - and sets + PROT_NONE on the original block. This catches use-after-realloc bugs, + + - It checks for calloc() overflows and can cause soft or hard failures + of alloc requests past a configurable memory limit (AFL_LD_LIMIT_MB, + AFL_LD_HARD_FAIL). + +Basically, it is inspired by some of the non-default options available for the +OpenBSD allocator - see malloc.conf(5) on that platform for reference. It is +also somewhat similar to several other debugging libraries, such as gmalloc +and DUMA - but is simple, plug-and-play, and designed specifically for fuzzing +jobs. + +Note that it does nothing for stack-based memory handling errors. The +-fstack-protector-all setting for GCC / clang, enabled when using AFL_HARDEN, +can catch some subset of that. + +The allocator is slow and memory-intensive (even the tiniest allocation uses up +4 kB of physical memory and 8 kB of virtual mem), making it completely unsuitable +for "production" uses; but it can be faster and more hassle-free than ASAN / MSAN +when fuzzing small, self-contained binaries. + +To use this library, run AFL like so: + +AFL_PRELOAD=/path/to/libdislocator.so ./afl-fuzz [...other params...] + +You *have* to specify path, even if it's just ./libdislocator.so or +$PWD/libdislocator.so. + +Similarly to afl-tmin, the library is not "proprietary" and can be used with +other fuzzers or testing tools without the need for any code tweaks. It does not +require AFL-instrumented binaries to work. + +Note that the AFL_PRELOAD approach (which AFL internally maps to LD_PRELOAD or +DYLD_INSERT_LIBRARIES, depending on the OS) works only if the target binary is +dynamically linked. Otherwise, attempting to use the library will have no +effect. diff --git a/libdislocator/libdislocator.so.c b/libdislocator/libdislocator.so.c new file mode 100644 index 0000000..0dfc98e --- /dev/null +++ b/libdislocator/libdislocator.so.c @@ -0,0 +1,258 @@ +/* + + american fuzzy lop - dislocator, an abusive allocator + ----------------------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2016 Google Inc. 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 is a companion library that can be used as a drop-in replacement + for the libc allocator in the fuzzed binaries. See README.dislocator for + more info. + + */ + +#include +#include +#include +#include +#include + +#include "../config.h" +#include "../types.h" + +#ifndef PAGE_SIZE +# define PAGE_SIZE 4096 +#endif /* !PAGE_SIZE */ + +#ifndef MAP_ANONYMOUS +# define MAP_ANONYMOUS MAP_ANON +#endif /* !MAP_ANONYMOUS */ + +/* Error / message handling: */ + +#define DEBUGF(_x...) do { \ + if (alloc_verbose) { \ + if (++call_depth == 1) { \ + fprintf(stderr, "[AFL] " _x); \ + fprintf(stderr, "\n"); \ + } \ + call_depth--; \ + } \ + } while (0) + +#define FATAL(_x...) do { \ + if (++call_depth == 1) { \ + fprintf(stderr, "*** [AFL] " _x); \ + fprintf(stderr, " ***\n"); \ + abort(); \ + } \ + call_depth--; \ + } while (0) + +/* Macro to count the number of pages needed to store a buffer: */ + +#define PG_COUNT(_l) (((_l) + (PAGE_SIZE - 1)) / PAGE_SIZE) + +/* Canary & clobber bytes: */ + +#define ALLOC_CANARY 0xAACCAACC +#define ALLOC_CLOBBER 0xCC + +#define PTR_C(_p) (((u32*)(_p))[-1]) +#define PTR_L(_p) (((u32*)(_p))[-2]) + +/* Configurable stuff (use AFL_LD_* to set): */ + +static u32 max_mem = MAX_ALLOC; /* Max heap usage to permit */ +static u8 alloc_verbose, /* Additional debug messages */ + hard_fail; /* abort() when max_mem exceeded? */ + +static __thread size_t total_mem; /* Currently allocated mem */ + +static __thread u32 call_depth; /* To avoid recursion via fprintf() */ + + +/* This is the main alloc function. It allocates one page more than necessary, + sets that tailing page to PROT_NONE, and then increments the return address + so that it is right-aligned to that boundary. Since it always uses mmap(), + the returned memory will be zeroed. */ + +static void* __dislocator_alloc(size_t len) { + + void* ret; + + + if (total_mem + len > max_mem || total_mem + len < total_mem) { + + if (hard_fail) + FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024); + + DEBUGF("total allocs exceed %u MB, returning NULL", + max_mem / 1024 / 1024); + + return NULL; + + } + + /* We will also store buffer length and a canary below the actual buffer, so + let's add 8 bytes for that. */ + + 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 (hard_fail) FATAL("mmap() failed on alloc (OOM?)"); + + DEBUGF("mmap() failed on alloc (OOM?)"); + + return NULL; + + } + + /* Set PROT_NONE on the last page. */ + + if (mprotect(ret + PG_COUNT(len + 8) * PAGE_SIZE, PAGE_SIZE, PROT_NONE)) + FATAL("mprotect() failed when allocating memory"); + + /* Offset the return pointer so that it's right-aligned to the page + boundary. */ + + ret += PAGE_SIZE * PG_COUNT(len + 8) - len - 8; + + /* Store allocation metadata. */ + + ret += 8; + + PTR_L(ret) = len; + PTR_C(ret) = ALLOC_CANARY; + + total_mem += len; + + return ret; + +} + + +/* The "user-facing" wrapper for calloc(). This just checks for overflows and + displays debug messages if requested. */ + +void* calloc(size_t elem_len, size_t elem_cnt) { + + void* ret; + + size_t len = elem_len * elem_cnt; + + /* Perform some sanity checks to detect obvious issues... */ + + if (elem_cnt && len / elem_cnt != elem_len) + FATAL("calloc(%zu, %zu) would overflow", elem_len, elem_cnt); + + ret = __dislocator_alloc(len); + + DEBUGF("calloc(%zu, %zu) = %p [%zu total]", elem_len, elem_cnt, ret, + total_mem); + + return ret; + +} + + +/* The wrapper for malloc(). Roughly the same, also clobbers the returned + memory (unlike calloc(), malloc() is not guaranteed to return zeroed + memory). */ + +void* malloc(size_t len) { + + void* ret; + + ret = __dislocator_alloc(len); + + DEBUGF("malloc(%zu) = %p [%zu total]", len, ret, total_mem); + + if (ret && len) memset(ret, ALLOC_CLOBBER, len); + + return ret; + +} + + +/* The wrapper for free(). This simply marks the entire region as PROT_NONE. + If the region is already freed, the code will segfault during the attempt to + read the canary. Not very graceful, but works, right? */ + +void free(void* ptr) { + + u32 len; + + DEBUGF("free(%p)", ptr); + + if (!ptr) return; + + if (PTR_C(ptr) != ALLOC_CANARY) FATAL("bad allocator canary on free()"); + + len = PTR_L(ptr); + + total_mem -= len; + + /* Protect everything. Note that the extra page at the end is already + set as PROT_NONE, so we don't need to touch that. */ + + ptr -= PAGE_SIZE * PG_COUNT(len + 8) - len - 8; + + if (mprotect(ptr - 8, PG_COUNT(len + 8) * PAGE_SIZE, PROT_NONE)) + FATAL("mprotect() failed when freeing memory"); + + /* Keep the mapping; this is wasteful, but prevents ptr reuse. */ + +} + + +/* Realloc is pretty straightforward, too. We forcibly reallocate the buffer, + move data, and then free (aka mprotect()) the original one. */ + +void* realloc(void* ptr, size_t len) { + + void* ret; + + ret = malloc(len); + + if (ret && ptr) { + + if (PTR_C(ptr) != ALLOC_CANARY) FATAL("bad allocator canary on realloc()"); + + memcpy(ret, ptr, MIN(len, PTR_L(ptr))); + free(ptr); + + } + + DEBUGF("realloc(%p, %zu) = %p [%zu total]", ptr, len, ret, total_mem); + + return ret; + +} + + +__attribute__((constructor)) void __dislocator_init(void) { + + u8* tmp = getenv("AFL_LD_LIMIT_MB"); + + if (tmp) { + + max_mem = atoi(tmp) * 1024 * 1024; + if (!max_mem) FATAL("Bad value for AFL_LD_LIMIT_MB"); + + } + + alloc_verbose = !!getenv("AFL_LD_VERBOSE"); + hard_fail = !!getenv("AFL_LD_HARD_FAIL"); + +} diff --git a/libtokencap/Makefile b/libtokencap/Makefile new file mode 100644 index 0000000..a464f76 --- /dev/null +++ b/libtokencap/Makefile @@ -0,0 +1,38 @@ +# +# american fuzzy lop - libtokencap +# -------------------------------- +# +# Written by Michal Zalewski +# +# Copyright 2016 Google Inc. 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 +# + +PREFIX ?= /usr/local +HELPER_PATH = $(PREFIX)/lib/afl + +VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2) + +CFLAGS ?= -O3 -funroll-loops +CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign + +all: libtokencap.so + +libtokencap.so: libtokencap.so.c ../config.h + $(CC) $(CFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS) + +.NOTPARALLEL: clean + +clean: + rm -f *.o *.so *~ a.out core core.[1-9][0-9]* + rm -f libtokencap.so + +install: all + install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH) + install -m 644 README.tokencap $${DESTDIR}$(HELPER_PATH) + diff --git a/libtokencap/README.tokencap b/libtokencap/README.tokencap new file mode 100644 index 0000000..82d80c9 --- /dev/null +++ b/libtokencap/README.tokencap @@ -0,0 +1,60 @@ +========================================= +strcmp() / memcmp() token capture library +========================================= + + (See ../docs/README for the general instruction manual.) + +This Linux-only companion library allows you to instrument strcmp(), memcmp(), +and related functions to automatically extract syntax tokens passed to any of +these libcalls. The resulting list of tokens may be then given as a starting +dictionary to afl-fuzz (the -x option) to improve coverage on subsequent +fuzzing runs. + +This may help improving coverage in some targets, and do precisely nothing in +others. In some cases, it may even make things worse: if libtokencap picks up +syntax tokens that are not used to process the input data, but that are a part +of - say - parsing a config file... well, you're going to end up wasting a lot +of CPU time on trying them out in the input stream. In other words, use this +feature with care. Manually screening the resulting dictionary is almost +always a necessity. + +As for the actual operation: the library stores tokens, without any deduping, +by appending them to a file specified via AFL_TOKEN_FILE. If the variable is not +set, the tool uses stderr (which is probably not what you want). + +Similarly to afl-tmin, the library is not "proprietary" and can be used with +other fuzzers or testing tools without the need for any code tweaks. It does not +require AFL-instrumented binaries to work. + +To use the library, you *need* to make sure that your fuzzing target is compiled +with -fno-builtin and is linked dynamically. If you wish to automate the first +part without mucking with CFLAGS in Makefiles, you can set AFL_NO_BUILTIN=1 +when using afl-gcc. This setting specifically adds the following flags: + + -fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp + -fno-builtin-strcasencmp -fno-builtin-memcmp + +The next step is simply loading this library via LD_PRELOAD. The optimal usage +pattern is to allow afl-fuzz to fuzz normally for a while and build up a corpus, +and then fire off the target binary, with libtokencap.so loaded, on every file +found by AFL in that earlier run. This demonstrates the basic principle: + + export AFL_TOKEN_FILE=$PWD/temp_output.txt + + for i in /queue/id*; do + LD_PRELOAD=/path/to/libtokencap.so \ + /path/to/target/program [...params, including $i...] + done + + sort -u temp_output.txt >afl_dictionary.txt + +If you don't get any results, the target library is probably not using strcmp() +and memcmp() to parse input; or you haven't compiled it with -fno-builtin; or +the whole thing isn't dynamically linked, and LD_PRELOAD is having no effect. + +PS. The library is Linux-only because there is probably no particularly portable +and non-invasive way to distinguish between read-only and read-write memory +mappings. The __tokencap_load_mappings() function is the only thing that would +need to be changed for other OSes. Porting to platforms with /proc//maps +(e.g., FreeBSD) should be trivial. + diff --git a/libtokencap/libtokencap.so.c b/libtokencap/libtokencap.so.c new file mode 100644 index 0000000..696c913 --- /dev/null +++ b/libtokencap/libtokencap.so.c @@ -0,0 +1,253 @@ +/* + + american fuzzy lop - extract tokens passed to strcmp / memcmp + ------------------------------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2016 Google Inc. 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 Linux-only companion library allows you to instrument strcmp(), + memcmp(), and related functions to automatically extract tokens. + See README.tokencap for more info. + + */ + +#include +#include +#include + +#include "../types.h" +#include "../config.h" + +#ifndef __linux__ +# error "Sorry, this library is Linux-specific for now!" +#endif /* !__linux__ */ + + +/* Mapping data and such */ + +#define MAX_MAPPINGS 1024 + +static struct mapping { + void *st, *en; +} __tokencap_ro[MAX_MAPPINGS]; + +static u32 __tokencap_ro_cnt; +static u8 __tokencap_ro_loaded; +static FILE* __tokencap_out_file; + + +/* Identify read-only regions in memory. Only parameters that fall into these + ranges are worth dumping when passed to strcmp() and so on. Read-write + regions are far more likely to contain user input instead. */ + +static void __tokencap_load_mappings(void) { + + u8 buf[MAX_LINE]; + FILE* f = fopen("/proc/self/maps", "r"); + + __tokencap_ro_loaded = 1; + + if (!f) return; + + while (fgets(buf, MAX_LINE, f)) { + + u8 rf, wf; + void* st, *en; + + if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue; + if (wf == 'w' || rf != 'r') continue; + + __tokencap_ro[__tokencap_ro_cnt].st = (void*)st; + __tokencap_ro[__tokencap_ro_cnt].en = (void*)en; + + if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; + + } + + fclose(f); + +} + + +/* Check an address against the list of read-only mappings. */ + +static u8 __tokencap_is_ro(const void* ptr) { + + u32 i; + + if (!__tokencap_ro_loaded) __tokencap_load_mappings(); + + for (i = 0; i < __tokencap_ro_cnt; i++) + if (ptr >= __tokencap_ro[i].st && ptr <= __tokencap_ro[i].en) return 1; + + return 0; + +} + + +/* Dump an interesting token to output file, quoting and escaping it + properly. */ + +static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) { + + u8 buf[MAX_AUTO_EXTRA * 4 + 1]; + u32 i; + u32 pos = 0; + + if (len < MIN_AUTO_EXTRA || len > MAX_AUTO_EXTRA) return; + + for (i = 0; i < len; i++) { + + if (is_text && !ptr[i]) break; + + switch (ptr[i]) { + + case 0 ... 31: + case 127 ... 255: + case '\"': + case '\\': + + sprintf(buf + pos, "\\x%02x", ptr[i]); + pos += 4; + break; + + default: + + buf[pos++] = ptr[i]; + + } + + } + + buf[pos] = 0; + + fprintf(__tokencap_out_file, "\"%s\"\n", buf); + +} + + +/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used + only if the target is compiled with -fno-builtins and linked dynamically. */ + +#undef strcmp + +int strcmp(const char* str1, const char* str2) { + + if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1); + if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1); + + while (1) { + + unsigned char c1 = *str1, c2 = *str2; + + if (c1 != c2) return (c1 > c2) ? 1 : -1; + if (!c1) return 0; + str1++; str2++; + + } + +} + + +#undef strncmp + +int strncmp(const char* str1, const char* str2, size_t len) { + + if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); + if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); + + while (len--) { + + unsigned char c1 = *str1, c2 = *str2; + + if (!c1) return 0; + if (c1 != c2) return (c1 > c2) ? 1 : -1; + str1++; str2++; + + } + + return 0; + +} + + +#undef strcasecmp + +int strcasecmp(const char* str1, const char* str2) { + + if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1); + if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1); + + while (1) { + + unsigned char c1 = tolower(*str1), c2 = tolower(*str2); + + if (c1 != c2) return (c1 > c2) ? 1 : -1; + if (!c1) return 0; + str1++; str2++; + + } + +} + + +#undef strncasecmp + +int strncasecmp(const char* str1, const char* str2, size_t len) { + + if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); + if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); + + while (len--) { + + unsigned char c1 = tolower(*str1), c2 = tolower(*str2); + + if (!c1) return 0; + if (c1 != c2) return (c1 > c2) ? 1 : -1; + str1++; str2++; + + } + + return 0; + +} + + +#undef memcmp + +int memcmp(const void* mem1, const void* mem2, size_t len) { + + if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0); + if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0); + + while (len--) { + + unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2; + if (c1 != c2) return (c1 > c2) ? 1 : -1; + mem1++; mem2++; + + } + + return 0; + +} + + +/* Init code to open the output file (or default to stderr). */ + +__attribute__((constructor)) void __tokencap_init(void) { + + u8* fn = getenv("AFL_TOKEN_FILE"); + if (fn) __tokencap_out_file = fopen(fn, "a"); + if (!__tokencap_out_file) __tokencap_out_file = stderr; + +} + diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile index 7ea3c3f..5d4da94 100644 --- a/llvm_mode/Makefile +++ b/llvm_mode/Makefile @@ -7,7 +7,7 @@ # # LLVM integration design comes from Laszlo Szekeres. # -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2015, 2016 Google Inc. 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. @@ -20,28 +20,29 @@ PREFIX ?= /usr/local HELPER_PATH = $(PREFIX)/lib/afl BIN_PATH = $(PREFIX)/bin -VERSION = $(shell grep ^VERSION ../Makefile | cut -d= -f2 | sed 's/ //') +VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2) LLVM_CONFIG ?= llvm-config CFLAGS ?= -O3 -funroll-loops CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \ -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ - -DVERSION=\"$(VERSION)\" + -DVERSION=\"$(VERSION)\" +ifdef AFL_TRACE_PC + CFLAGS += -DUSE_TRACE_PC=1 +endif CXXFLAGS ?= -O3 -funroll-loops CXXFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \ - -DVERSION=\"$(VERSION)\" + -DVERSION=\"$(VERSION)\" -Wno-variadic-macros -CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti $(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 - + CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress endif # We were using llvm-config --bindir to get the location of clang, but @@ -49,19 +50,25 @@ endif # probably better. ifeq "$(origin CC)" "default" - -CC = clang -CXX = clang++ - + CC = clang + CXX = clang++ endif -PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-rt.o +ifndef AFL_TRACE_PC + PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o +else + PROGS = ../afl-clang-fast ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o +endif all: test_deps $(PROGS) test_build all_done test_deps: +ifndef AFL_TRACE_PC @echo "[*] Checking for working 'llvm-config'..." @which $(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 ) +else + @echo "[!] Note: using -fsanitize=trace-pc mode (this will fail with older LLVM)." +endif @echo "[*] Checking for working '$(CC)'..." @which $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 ) @echo "[*] Checking for '../afl-showmap'..." @@ -78,9 +85,17 @@ test_deps: ../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps $(CC) $(CFLAGS) -fPIC -c $< -o $@ +../afl-llvm-rt-32.o: afl-llvm-rt.o.c | test_deps + @printf "[*] Building 32-bit variant of the runtime (-m32)... " + @$(CC) $(CFLAGS) -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + +../afl-llvm-rt-64.o: afl-llvm-rt.o.c | test_deps + @printf "[*] Building 64-bit variant of the runtime (-m64)... " + @$(CC) $(CFLAGS) -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + test_build: $(PROGS) @echo "[*] Testing the CC wrapper and instrumentation output..." - unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) + unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=$(CC) ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) echo 0 | ../afl-showmap -m none -q -o .test-instr0 ./test-instr echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr @rm -f test-instr diff --git a/llvm_mode/README.llvm b/llvm_mode/README.llvm index c70b20f..349d0e2 100644 --- a/llvm_mode/README.llvm +++ b/llvm_mode/README.llvm @@ -22,7 +22,9 @@ several interesting properties: - The instrumentation is CPU-independent. At least in principle, you should be able to rely on it to fuzz programs on non-x86 architectures (after - building afl-fuzz with AFL_NOX86=1). + building afl-fuzz with AFL_NO_X86=1). + + - The instrumentation can cope a bit better with multi-threaded targets. - Because the feature relies on the internals of LLVM, it is clang-specific and will *not* work with GCC. @@ -121,7 +123,7 @@ With the location selected, add this code in the appropriate spot: You don't need the #ifdef guards, but including them ensures that the program will keep working normally when compiled with a tool other than afl-clang-fast. -Finally, recompile the pogram with afl-clang-fast (afl-gcc or afl-clang will +Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will *not* generate a deferred-initialization binary) - and you should be all set! 5) Bonus feature #2: persistent mode @@ -146,7 +148,9 @@ The basic structure of the program that does this would be: The numerical value specified within the loop controls the maximum number of iterations before AFL will restart the process from scratch. This minimizes -the impact of memory leaks and similar glitches; 1000 is a good starting point. +the impact of memory leaks and similar glitches; 1000 is a good starting point, +and going much higher increases the likelihood of hiccups without giving you +any real performance benefits. A more detailed template is shown in ../experimental/persistent_demo/. Similarly to the previous mode, the feature works only with afl-clang-fast; @@ -157,12 +161,30 @@ do not fully reset the critical state, you may end up with false positives or waste a whole lot of CPU power doing nothing useful at all. Be particularly wary of memory leaks and of the state of file descriptors. -When running in this mode, the execution paths will inherently vary a bit -depending on whether the input loop is being entered for the first time or -executed again. To avoid spurious warnings, the feature implies -AFL_NO_VAR_CHECK and hides the "variable path" warnings in the UI. - PS. Because there are task switches still involved, the mode isn't as fast as "pure" in-process fuzzing offered, say, by LLVM's LibFuzzer; but it is a lot faster than the normal fork() model, and compared to in-process fuzzing, should be a lot more robust. + +6) Bonus feature #3: new 'trace-pc-guard' mode +---------------------------------------------- + +Recent versions of LLVM are shipping with a built-in execution tracing feature +that provides AFL with the necessary tracing data without the need to +post-process the assembly or install any compiler plugins. See: + + http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards + +As of this writing, the feature is only available on SVN trunk, and is yet to +make it to an official release of LLVM. Nevertheless, if you have a +sufficiently recent compiler and want to give it a try, build afl-clang-fast +this way: + + AFL_TRACE_PC=1 make clean all + +Note that this mode is currently about 20% slower than "vanilla" afl-clang-fast, +and about 5-10% slower than afl-clang. This is likely because the +instrumentation is not inlined, and instead involves a function call. On systems +that support it, compiling your target with -flto should help. + + diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index f229478..d4202a6 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -7,7 +7,7 @@ LLVM integration design comes from Laszlo Szekeres. - Copyright 2015 Google Inc. All rights reserved. + Copyright 2015, 2016 Google Inc. 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. @@ -96,10 +96,10 @@ static void find_obj(u8* argv0) { static void edit_params(u32 argc, char** argv) { - u8 fortify_set = 0, asan_set = 0, x_set = 0, maybe_linking = 1; + u8 fortify_set = 0, asan_set = 0, x_set = 0, maybe_linking = 1, bit_mode = 0; u8 *name; - cc_params = ck_alloc((argc + 64) * sizeof(u8*)); + cc_params = ck_alloc((argc + 128) * sizeof(u8*)); name = strrchr(argv[0], '/'); if (!name) name = argv[0]; else name++; @@ -112,29 +112,51 @@ static void edit_params(u32 argc, char** argv) { cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; } + /* There are two ways to compile afl-clang-fast. In the traditional mode, we + use afl-llvm-pass.so to inject instrumentation. In the experimental + 'trace-pc-guard' mode, we use native LLVM instrumentation callbacks + instead. The latter is a very recent addition - see: + + http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards */ + +#ifdef USE_TRACE_PC + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; + cc_params[cc_par_cnt++] = "-mllvm"; + cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; +#else cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path); +#endif /* ^USE_TRACE_PC */ + cc_params[cc_par_cnt++] = "-Qunused-arguments"; + /* Detect stray -v calls from ./configure scripts. */ + + if (argc == 1 && !strcmp(argv[1], "-v")) maybe_linking = 0; + while (--argc) { u8* cur = *(++argv); -#if defined(__x86_64__) - if (!strcmp(cur, "-m32")) FATAL("-m32 is not supported"); -#endif + if (!strcmp(cur, "-m32")) bit_mode = 32; + if (!strcmp(cur, "-m64")) bit_mode = 64; if (!strcmp(cur, "-x")) x_set = 1; - if (!strcmp(cur, "-c") || !strcmp(cur, "-S") || !strcmp(cur, "-E") || - !strcmp(cur, "-v")) maybe_linking = 0; + if (!strcmp(cur, "-c") || !strcmp(cur, "-S") || !strcmp(cur, "-E")) + maybe_linking = 0; if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) asan_set = 1; if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; + if (!strcmp(cur, "-shared")) maybe_linking = 0; + + if (!strcmp(cur, "-Wl,-z,defs") || + !strcmp(cur, "-Wl,--no-undefined")) continue; + cc_params[cc_par_cnt++] = cur; } @@ -152,22 +174,37 @@ static void edit_params(u32 argc, char** argv) { if (getenv("AFL_USE_ASAN")) { - cc_params[cc_par_cnt++] = "-fsanitize=address"; - if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive"); - } else if (getenv("AFL_USE_MSAN")) { + if (getenv("AFL_HARDEN")) + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); - cc_params[cc_par_cnt++] = "-fsanitize=memory"; + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; + cc_params[cc_par_cnt++] = "-fsanitize=address"; + + } else if (getenv("AFL_USE_MSAN")) { if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive"); + if (getenv("AFL_HARDEN")) + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); + + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; + cc_params[cc_par_cnt++] = "-fsanitize=memory"; + } } +#ifdef USE_TRACE_PC + + if (getenv("AFL_INST_RATIO")) + FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'."); + +#endif /* USE_TRACE_PC */ + if (!getenv("AFL_DONT_OPTIMIZE")) { cc_params[cc_par_cnt++] = "-g"; @@ -176,7 +213,19 @@ static void edit_params(u32 argc, char** argv) { } + if (getenv("AFL_NO_BUILTIN")) { + + cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; + + } + cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; + cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; + cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; /* When the user tries to use persistent or deferred forkserver modes by appending a single line to the program, we want to reliably inject a @@ -202,8 +251,10 @@ static void edit_params(u32 argc, char** argv) { "({ static volatile char *_B __attribute__((used)); " " _B = (char*)\"" PERSIST_SIG "\"; " #ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " #else + "__attribute__((visibility(\"default\"))) " "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " #endif /* ^__APPLE__ */ "_L(_A); })"; @@ -212,8 +263,10 @@ static void edit_params(u32 argc, char** argv) { "do { static volatile char *_A __attribute__((used)); " " _A = (char*)\"" DEFER_SIG "\"; " #ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " "void _I(void) __asm__(\"___afl_manual_init\"); " #else + "__attribute__((visibility(\"default\"))) " "void _I(void) __asm__(\"__afl_manual_init\"); " #endif /* ^__APPLE__ */ "_I(); } while (0)"; @@ -225,7 +278,29 @@ static void edit_params(u32 argc, char** argv) { cc_params[cc_par_cnt++] = "none"; } - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); + switch (bit_mode) { + + case 0: + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); + break; + + case 32: + 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"); + + break; + + case 64: + 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"); + + break; + + } } @@ -240,7 +315,11 @@ int main(int argc, char** argv) { if (isatty(2) && !getenv("AFL_QUIET")) { +#ifdef USE_TRACE_PC + SAYF(cCYA "afl-clang-fast [tpcg] " cBRI VERSION cRST " by \n"); +#else SAYF(cCYA "afl-clang-fast " cBRI VERSION cRST " by \n"); +#endif /* ^USE_TRACE_PC */ } diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 771b4d8..44d9e16 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -8,7 +8,7 @@ LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted from afl-as.c are Michal's fault. - Copyright 2015 Google Inc. All rights reserved. + Copyright 2015, 2016 Google Inc. 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. @@ -49,9 +49,9 @@ namespace { bool runOnModule(Module &M) override; - const char *getPassName() const override { - return "American Fuzzy Lop Instrumentation"; - } + // StringRef getPassName() const override { + // return "American Fuzzy Lop Instrumentation"; + // } }; @@ -66,8 +66,7 @@ bool AFLCoverage::runOnModule(Module &M) { LLVMContext &C = M.getContext(); IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - IntegerType *Int16Ty = IntegerType::getInt16Ty(C); - IntegerType *Int64Ty = IntegerType::getInt64Ty(C); + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); /* Show a banner */ @@ -92,14 +91,16 @@ bool AFLCoverage::runOnModule(Module &M) { } - /* Get globals for the SHM region and the previous location. */ + /* Get globals for the SHM region and the previous location. Note that + __afl_prev_loc is thread-local. */ GlobalVariable *AFLMapPtr = new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc = new GlobalVariable( - M, Int16Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc"); + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", + 0, GlobalVariable::GeneralDynamicTLSModel, 0, false); /* Instrument all the things! */ @@ -109,20 +110,21 @@ bool AFLCoverage::runOnModule(Module &M) { for (auto &BB : F) { BasicBlock::iterator IP = BB.getFirstInsertionPt(); - IRBuilder<> IRB(IP); + IRBuilder<> IRB(&(*IP)); if (R(100) >= inst_ratio) continue; /* Make up cur_loc */ unsigned int cur_loc = R(MAP_SIZE); - ConstantInt *CurLoc = ConstantInt::get(Int64Ty, cur_loc); + + ConstantInt *CurLoc = ConstantInt::get(Int32Ty, cur_loc); /* Load prev_loc */ LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc); PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt64Ty()); + Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty()); /* Load SHM pointer */ @@ -142,7 +144,7 @@ bool AFLCoverage::runOnModule(Module &M) { /* Set prev_loc to cur_loc >> 1 */ StoreInst *Store = - IRB.CreateStore(ConstantInt::get(Int16Ty, cur_loc >> 1), AFLPrevLoc); + IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc); Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); inst_blocks++; diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 03e2afe..ed3a664 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -7,7 +7,7 @@ LLVM integration design comes from Laszlo Szekeres. - Copyright 2015 Google Inc. All rights reserved. + Copyright 2015, 2016 Google Inc. 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. @@ -22,9 +22,11 @@ #include "../config.h" #include "../types.h" +#include #include #include #include +#include #include #include @@ -32,6 +34,16 @@ #include #include +/* This is a somewhat ugly hack for the experimental 'trace-pc-guard' mode. + Basically, we need to make sure that the forkserver is initialized after + the LLVM-generated runtime initialization pass, not before. */ + +#ifdef USE_TRACE_PC +# define CONST_PRIO 5 +#else +# define CONST_PRIO 0 +#endif /* ^USE_TRACE_PC */ + /* 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. @@ -39,7 +51,8 @@ u8 __afl_area_initial[MAP_SIZE]; u8* __afl_area_ptr = __afl_area_initial; -u16 __afl_prev_loc; + +__thread u32 __afl_prev_loc; /* Running in persistent mode? */ @@ -167,18 +180,48 @@ 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) { + + raise(SIGSTOP); + + __afl_area_ptr[0] = 1; + __afl_prev_loc = 0; - } else return 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; } @@ -203,7 +246,7 @@ void __afl_manual_init(void) { /* Proper initialization routine. */ -__attribute__((constructor(0))) void __afl_auto_init(void) { +__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { is_persistent = !!getenv(PERSIST_ENV_VAR); @@ -214,3 +257,50 @@ __attribute__((constructor(0))) void __afl_auto_init(void) { } +/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. + It remains non-operational in the traditional, plugin-backed LLVM mode. + For more info about 'trace-pc-guard', see README.llvm. + + The first function (__sanitizer_cov_trace_pc_guard) is called back on every + edge (as opposed to every basic block). */ + +void __sanitizer_cov_trace_pc_guard(uint32_t* guard) { + __afl_area_ptr[*guard]++; +} + + +/* Init callback. Populates instrumentation IDs. Note that we're using + ID of 0 as a special value to indicate non-instrumented bits. That may + still touch the bitmap, but in a fairly harmless way. */ + +void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) { + + u32 inst_ratio = 100; + u8* x; + + if (start == stop || *start) return; + + x = getenv("AFL_INST_RATIO"); + if (x) inst_ratio = atoi(x); + + if (!inst_ratio || inst_ratio > 100) { + fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n"); + abort(); + } + + /* Make sure that the first element in the range is always set - we use that + to avoid duplicate calls (which can happen as an artifact of the underlying + implementation in LLVM). */ + + *(start++) = R(MAP_SIZE - 1) + 1; + + while (start < stop) { + + if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1; + else *start = 0; + + start++; + + } + +} diff --git a/qemu_mode/README.qemu b/qemu_mode/README.qemu index ce4181e..f9dce99 100644 --- a/qemu_mode/README.qemu +++ b/qemu_mode/README.qemu @@ -98,6 +98,11 @@ Compared to fully-fledged virtualization, the user emulation mode is *NOT* a security boundary. The binaries can freely interact with the host OS. If you somehow need to fuzz an untrusted binary, put everything in a sandbox first. +QEMU does not necessarily support all CPU or hardware features that your +target program may be utilizing. In particular, it does not appear to have +full support for AVX2 / FMA3. Using binaries for older CPUs, or recompiling them +with -march=core2, can help. + Beyond that, this is an early-stage mechanism, so fields reports are welcome. You can send them to . diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh index 058b963..7224671 100755 --- a/qemu_mode/build_qemu_support.sh +++ b/qemu_mode/build_qemu_support.sh @@ -6,7 +6,7 @@ # Written by Andrew Griffiths and # Michal Zalewski # -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2015, 2016 Google Inc. 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. @@ -124,6 +124,8 @@ patch -p0 /dev/null || exit 1 + cd .. -gcc test-instr.c -o test-instr || exit 1 + make >/dev/null || exit 1 -unset AFL_INST_RATIO + gcc test-instr.c -o test-instr || exit 1 -echo 0 | ./afl-showmap -m none -Q -q -o .test-instr0 ./test-instr || exit 1 -echo 1 | ./afl-showmap -m none -Q -q -o .test-instr1 ./test-instr || exit 1 + unset AFL_INST_RATIO -rm -f test-instr + echo 0 | ./afl-showmap -m none -Q -q -o .test-instr0 ./test-instr || exit 1 + echo 1 | ./afl-showmap -m none -Q -q -o .test-instr1 ./test-instr || exit 1 -cmp -s .test-instr0 .test-instr1 -DR="$?" + rm -f test-instr -rm -f .test-instr0 .test-instr1 + cmp -s .test-instr0 .test-instr1 + DR="$?" -if [ "$DR" = "0" ]; then + rm -f .test-instr0 .test-instr1 - echo "[-] Error: afl-qemu-trace instrumentation doesn't seem to work!" - exit 1 + if [ "$DR" = "0" ]; then -fi + echo "[-] Error: afl-qemu-trace instrumentation doesn't seem to work!" + exit 1 -echo "[+] Instrumentation tests passed. " + fi -echo "[+] All set, you can now use the -Q mode in afl-fuzz!" + echo "[+] Instrumentation tests passed. " + echo "[+] All set, you can now use the -Q mode in afl-fuzz!" + +else + + echo "[!] Note: can't test instrumentation when CPU_TARGET set." + echo "[+] All set, you can now (hopefully) use the -Q mode in afl-fuzz!" + +fi exit 0 diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h index de5613c..eaa0e67 100644 --- a/qemu_mode/patches/afl-qemu-cpu-inl.h +++ b/qemu_mode/patches/afl-qemu-cpu-inl.h @@ -7,7 +7,7 @@ Idea & design very much by Andrew Griffiths. - Copyright 2015 Google Inc. All rights reserved. + Copyright 2015, 2016 Google Inc. 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. @@ -223,7 +223,7 @@ static void afl_forkserver(CPUArchState *env) { static inline void afl_maybe_log(abi_ulong cur_loc) { - static abi_ulong prev_loc; + static __thread abi_ulong prev_loc; /* Optimize for cur_loc > afl_end_code, which is the most likely case on Linux systems. */ @@ -231,11 +231,9 @@ static inline void afl_maybe_log(abi_ulong cur_loc) { if (cur_loc > afl_end_code || cur_loc < afl_start_code || !afl_area_ptr) return; - /* Looks like QEMU always maps to fixed locations, so we can skip this: - cur_loc -= afl_start_code; */ - - /* Instruction addresses may be aligned. Let's mangle the value to get - something quasi-uniform. */ + /* Looks like QEMU always maps to fixed locations, so ASAN is not a + concern. Phew. But instruction addresses may be aligned. Let's mangle + the value to get something quasi-uniform. */ cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); cur_loc &= MAP_SIZE - 1; diff --git a/testcases/README.testcases b/testcases/README.testcases index d19cb6e..30110ba 100644 --- a/testcases/README.testcases +++ b/testcases/README.testcases @@ -1,12 +1,9 @@ -=============================== -AFL test cases and dictionaries -=============================== +======================= +AFL starting test cases +======================= (See ../docs/README for the general instruction manual.) -1) Starting test cases ----------------------- - The archives/, images/, multimedia/, and others/ subdirectories contain small, standalone files that can be used to seed afl-fuzz when testing parsers for a variety of common data formats. @@ -16,56 +13,7 @@ optimized for size and stripped of any non-essential fluff. Some directories contain several examples that exercise various features of the underlying format. For example, there is a PNG file with and without a color profile. -Additional test cases are always welcome; the current "most wanted" list -includes: - - - JBIG, - - Ogg Vorbis, - - Ogg Theora, - - MP3, - - AAC, - - WebM, - - Small JPEG with a color profile, - - Small fonts. - -2) Dictionaries ---------------- - -The _extras/ subdirectory contains a set of dictionaries that can be used in -conjunction with the -x option to allow the fuzzer to effortlessly explore the -grammar of some of the more verbose data formats or languages. The basic -principle behind the operation of fuzzer dictionaries is outlined in section 9 -of the "main" README for the project. - -Custom dictionaries can be added at will. They should consist of a -reasonably-sized set of rudimentary syntax units that the fuzzer will then try -to clobber together in various ways. Snippets between 2 and 16 bytes are usually -the sweet spot. - -Custom dictionaries can be created in two ways: - - - By creating a new directory and placing each token in a separate file, in - which case, there is no need to escape or otherwise format the data. - - - By creating a flat text file where tokens are listed one per line in the - format of name="value". The alphanumeric name is ignored and can be omitted, - although it is a convenient way to document the meaning of a particular - token. The value must appear in quotes, with hex escaping (\xNN) applied to - all non-printable, high-bit, or otherwise problematic characters (\\ and \" - shorthands are recognized, too). - -The fuzzer auto-selects the appropriate mode depending on whether the -x -parameter is a file or a directory. - -In the file mode, every name field can be optionally followed by @, e.g.: - - keyword_foo@1 = "foo" - -Such entries will be loaded only if the requested dictionary level is equal or -higher than this number. The default level is zero; a higher value can be set -by appending @ to the dictionary file name, like so: - - -x path/to/dictionary.dct@2 +Additional test cases are always welcome. -Good examples of dictionaries can be found in _extras/xml.dict and -_extras/png.dict. +In addition to well-chosen starting files, many fuzzing jobs benefit from a +small and concise dictionary. See ../dictionaries/README.dictionaries for more. diff --git a/types.h b/types.h index 58d6be5..21d32da 100644 --- a/types.h +++ b/types.h @@ -44,7 +44,7 @@ typedef uint32_t u32; typedef unsigned long long u64; #else typedef uint64_t u64; -#endif /* ^sizeof(...) */ +#endif /* ^__x86_64__ */ typedef int8_t s8; typedef int16_t s16; @@ -76,4 +76,7 @@ typedef int64_t s64; #define MEM_BARRIER() \ asm volatile("" ::: "memory") +#define likely(_x) __builtin_expect(!!(_x), 1) +#define unlikely(_x) __builtin_expect(!!(_x), 0) + #endif /* ! _HAVE_TYPES_H */ From 30f97da7a3a6be1cd1f07fa973b85f02886c5fc6 Mon Sep 17 00:00:00 2001 From: liangdzou Date: Tue, 28 Mar 2017 15:48:48 +0800 Subject: [PATCH 2/2] Update the README fot the network support --- docs/README | 148 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 8 deletions(-) diff --git a/docs/README b/docs/README index 501bdd4..0a4d087 100644 --- a/docs/README +++ b/docs/README @@ -180,6 +180,18 @@ $ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@ You can also use the -f option to have the mutated data written to a specific file. This is useful if the program expects a particular file extension or so. +For programs that accept input via a network, use: + +$ ./afl-fuzz -i testcase_dir -o findings_dir [-D delay_before_write] \ + [-t timeout_delay] [-L ] -N network_specification /path/to/program \ + [...params...] + +where -L is specified only if the target program acts as a client to a network +server (sends data to the server before receiving input in return). Otherwise, +afl-fuzz assumes the target program acts as a server or daemon and expects to +receive data from network clients and respond. The network_specification +and the delay parameters are discussed below in (12). + Non-instrumented binaries can be fuzzed in the QEMU mode (add -Q in the command line) or in a traditional, blind-fuzzer mode (specify -n). @@ -262,6 +274,9 @@ The parallel fuzzing mode also offers a simple way for interfacing AFL to other fuzzers, to symbolic or concolic execution engines, and so forth; again, see the last section of parallel_fuzzing.txt for tips. +Note the limitations to multi-core execution for fuzzing network services under +(12). + 9) Fuzzer dictionaries ---------------------- @@ -396,7 +411,115 @@ tasks, fuzzing may put strain on your hardware and on the OS. In particular: $ iostat -d 3 -x -k [...optional disk ID...] -13) Known limitations & areas for improvement +13) Fuzzing network services +---------------------------- + +Both client and server (daemon) programs that communicate using an +IP network (IPv4 or IPv6) can be fuzzed using the command line + +$ ./afl-fuzz -i testcase_dir -o findings_dir [-D delay_before_write] \ + [-t timeout_delay] [-L] -N network_specification /path/to/program \ + [...params...] + +where the network_specification has a form similar to a URL: + + [tcp|udp]://hostspec:port + +Afl-fuzz has two network fuzzing modes, where it acts as a client to a +network server that expects input via a socket (no -L option), and where it +acts as a server to a network client that sends data to afl-fuzz before +receiving input (using the -L option). In the first case, afl-fuzz sends data +to the port specified by the URL. In the second case, afl-fuzz receives data +on that port and sends a (fuzzed) response to the port the target (client) used +to send its data. + +Note that when afl-fuzz acts as a server (the -L option), there is no control +over how the target client manages its use of sockets - and in particular, a +client that uses ephemeral sockets (the usual case) will rapidly consume the +network stack's pool of available sockets. Some operating systems are able to +reclaim used ephemeral sockets to keep the pool from being exhausted; others +may experience difficulties. + +While the -D and -t command line arguments are optional, they are almost +always necessary when fuzzing a program using network protocols, as +described below. + +Case is irrelevant in the network specification. For programs that +use a stream (connection-based) protocol, use TCP, and for programs +that use a datagram (connectionless) protocol, use UDP. The hostspec +must be one of ::1 (forcing IPv6 networking), 127.0.0.1 (forcing IPv4 +networking), and localhost (which is typically configured as IPv4 but +may suppport IPv6 on some systems). Only loopback networking (local +to the host) is supported. The port must be either a port number in +the range 1..65535 or a service name known to the system being used. +You can test programs that use privileged ports, but you then have to +provide afl-fuzz with those additional privileges (e.g., root). It is +usually better to reconfigure the program being tested so that it will +use a non-privileged port during fuzzing. + +Programs that implement network services, also called daemons, are +typically transaction-based: They wait for a request and send a +response, and some expect a sequence of request/response transactions. +Afl-fuzz implements fuzzing only for the first write to the target +program and ignores all responses from the target. Most network +services expect to run as background processes and process requests +from many processes -- they do not normally exit. A timeout delay is +required in order to terminate these processes, and the default +timeout delay used in afl-fuzz is usually too long. The user needs to +experimentally determine a timeout delay (in milliseconds) that +produces a sufficiently low percentage of hangs (exits forced by +expiration of the delay) while allowing the input to the target from +afl-fuzz to be completely processed. (Note that afl-fuzz will usually +count these hangs as a single unique hang.) Since a network service +does not normally exit, the initial timing performed by afl-fuzz will +fail unless a '+' character is appended to the timeout_delay +parameter, indicating that afl-fuzz is to ignore these timeouts. + +Network services programs also require some time to perform start-up +processing, create and bind a socket to an address and port, and begin +listening for traffic on that socket. Connection requests (TCP) and +sends (UDP) generated by afl-fuzz will fail if made before the network +service is ready. Afl-fuzz implements a delay and retry procedure to +avoid this problem, where the delay is specified by the +delay_before_write parameter (in milliseconds). The first connection +attempt (for TCP) or write (for UDP) is not made until after this +delay, and the delay also specifies the wait time before each +subsequent attempt. Afl-fuzz will attempt to connect or send to the +same each target process a maximum of three times. + +The delay_before_write parameter, in particular, and to a lesser +extent the timeout_delay parameter limit the maximum achievable rate +of target program executions and therefore need to be small. A rule of +thumb is the timeout_delay value should be slightly longer than three +times the delay_before_write value, and the delay_before_write value +should be as small as possible while consistent with an acceptable +fraction of target process executions that time out (for example, +around 0.1%). + +Network client program have similar characteristics that require the use of +the delay parameter, but they write to their expected server (afl-fuzz in this +case) before reading from their network socket. This makes coordination between +afl-fuzz and (client) target somewhat more challenging. While the delay and +timeout parameters can usually be adjusted to obtain execution rates similar +to those for server programs, when afl-fuzz exits (due to a ^C interrupt) it +may hang. In this case, use (on UNIX or Linux) the ps command to find the +process id (PID) of the afl-fuzz process, and use the kill command to terminate +it (typically, "kill -9 PID"). This will also terminate (or scavenge) the +network client program's process, which may be in a zombie state that can not +otherwise be removed (without rebooting the system). + +A note concerning network fuzzing on multi-core systems: + +It is not possible to run two processes under a single operating +system kernel that bind to (listen to) the same port on the same +address. Thus, either a special wrapper (such as could be implemented +using LD_PRELOAD) can be used to remap each target's port to a +different value, or only one target process can be executed per kernel +(not per core). Parallel fuzzing of network services can be done using +several independent hosts (a cluster), or by reconfiguring the code +running on each core to use a different port. + +14) Known limitations & areas for improvement --------------------------------------------- Here are some of the most important caveats for AFL: @@ -420,11 +543,15 @@ Here are some of the most important caveats for AFL: isn't due to any specific fault of afl-fuzz; see notes_for_asan.txt for tips. - - There is no direct support for fuzzing network services, background - daemons, or interactive apps that require UI interaction to work. You may - need to make simple code changes to make them behave in a more traditional - way. Preeny may offer a relatively simple option, too - see: - https://github.com/zardus/preeny + - The supplied method for fuzzing network services or background daemons + is limited to fuzzing the first packet received by the service or daemon. + Since many of these processes are transactional, it may be desirable to + provide a deterministic sequence of transactions with the target, + followed by fuzzing. This is not supported and would be a nice topic + for future work. Interactive apps that require UI interaction to work + are also not supported. You may need to make simple code changes to + make them behave in a more traditional way. Preeny may offer a relatively + simple option, too - see: https://github.com/zardus/preeny Some useful tips for modifying network-based services can be also found at: https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop @@ -435,9 +562,13 @@ Here are some of the most important caveats for AFL: - Occasionally, sentient machines rise against their creators. If this happens to you, please consult http://lcamtuf.coredump.cx/prep/. + - Only afl-fuzz implements network fuzzing capabilities at this time. In + particular, afl-cmin (a script), afl-tmin, and afl-showmap, all of which + execute the target program, do not. + Beyond this, see INSTALL for platform-specific tips. -14) Special thanks +15) Special thanks ------------------ Many of the improvements to afl-fuzz wouldn't be possible without feedback, @@ -481,10 +612,11 @@ bug reports, or patches from: Joshua J. Drake Toby Hutton Rene Freingruber Sergey Davidoff Sami Liedes Craig Young + Doug Birdwell Liang Zou Thank you! -15) Contact +16) Contact ----------- Questions? Concerns? Bug reports? The author can be usually reached at