This commit is contained in:
hexcoder-
2020-05-07 18:33:38 +02:00
23 changed files with 1302 additions and 167 deletions

View File

@ -52,12 +52,28 @@ endif
ifneq "$(shell uname)" "Darwin" ifneq "$(shell uname)" "Darwin"
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
CFLAGS_OPT = -march=native CFLAGS_OPT += -march=native
endif endif
# OS X does not like _FORTIFY_SOURCE=2 # OS X does not like _FORTIFY_SOURCE=2
CFLAGS_OPT += -D_FORTIFY_SOURCE=2 CFLAGS_OPT += -D_FORTIFY_SOURCE=2
endif endif
ifdef STATIC
$(info Compiling static version of binaries)
# Disable python for static compilation to simplify things
PYTHON_OK=0
PYFLAGS=
CFLAGS_OPT += -static
LDFLAGS += -lm -lpthread -lz -lutil
endif
ifdef PROFILING
$(info Compiling with profiling information, for analysis: gprof ./afl-fuzz gmon.out > prof.txt)
CFLAGS_OPT += -pg -DPROFILING=1
LDFLAGS += -pg
endif
ifneq "$(shell uname -m)" "x86_64" ifneq "$(shell uname -m)" "x86_64"
ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386" ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386"
ifneq "$(shell uname -m)" "amd64" ifneq "$(shell uname -m)" "amd64"
@ -142,23 +158,23 @@ else
endif endif
ifneq "$(filter Linux GNU%,$(shell uname))" "" ifneq "$(filter Linux GNU%,$(shell uname))" ""
LDFLAGS += -ldl LDFLAGS += -ldl
endif endif
ifneq "$(findstring FreeBSD, $(shell uname))" "" ifneq "$(findstring FreeBSD, $(shell uname))" ""
CFLAGS += -pthread CFLAGS += -pthread
LDFLAGS += -lpthread LDFLAGS += -lpthread
endif endif
ifneq "$(findstring NetBSD, $(shell uname))" "" ifneq "$(findstring NetBSD, $(shell uname))" ""
CFLAGS += -pthread CFLAGS += -pthread
LDFLAGS += -lpthread LDFLAGS += -lpthread
endif endif
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
TEST_CC = afl-gcc TEST_CC = afl-gcc
else else
TEST_CC = afl-clang TEST_CC = afl-clang
endif endif
COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h
@ -184,18 +200,8 @@ ifeq "$(shell svn proplist . 2>/dev/null && echo 1 || echo 0)" "1"
IN_REPO=1 IN_REPO=1
endif endif
ifdef STATIC
$(info Compiling static version of binaries)
# Disable python for static compilation to simplify things
PYTHON_OK=0
PYFLAGS=
CFLAGS += -static
LDFLAGS += -lm -lpthread -lz -lutil
endif
ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
ASAN_LDFLAGS+=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer ASAN_LDFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
ifdef ASAN_BUILD ifdef ASAN_BUILD
$(info Compiling ASAN version of binaries) $(info Compiling ASAN version of binaries)
@ -203,12 +209,6 @@ ifdef ASAN_BUILD
LDFLAGS+=$(ASAN_LDFLAGS) LDFLAGS+=$(ASAN_LDFLAGS)
endif endif
ifdef PROFILING
$(info Compiling profiling version of binaries)
CFLAGS+=-pg
LDFLAGS+=-pg
endif
ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1"
SHMAT_OK=1 SHMAT_OK=1
else else

View File

@ -85,7 +85,7 @@
(3) partially via AFL_CODE_START/AFL_CODE_END (3) partially via AFL_CODE_START/AFL_CODE_END
(4) Only for LLVM >= 9 and not all targets compile (4) Only for LLVM >= 11 and not all targets compile
(5) upcoming, development in the branch (5) upcoming, development in the branch
@ -137,7 +137,7 @@ afl++ has many build options.
The easiest is to build and install everything: The easiest is to build and install everything:
```shell ```shell
$ sudo apt install build-essential libtool-bin python3 automake bison libglib2.0-dev libpixman-1-dev clang python-setuptools llvm $ sudo apt install build-essential libtool-bin python3 automake flex bison libglib2.0-dev libpixman-1-dev clang python-setuptools llvm
$ make distrib $ make distrib
$ sudo make install $ sudo make install
``` ```

View File

@ -16,9 +16,12 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
- an old, old bug in afl that would show negative stability in rare - an old, old bug in afl that would show negative stability in rare
circumstances is now hopefully fixed circumstances is now hopefully fixed
- llvm_mode: - llvm_mode:
- afl-clang-fast/lto now do not skip single block functions. This
behaviour can be reactivated with AFL_LLVM_SKIPSINGLEBLOCK
- if LLVM 11 is installed the posix shm_open+mmap is used and a fixed - if LLVM 11 is installed the posix shm_open+mmap is used and a fixed
address for the shared memory map is used as this increases the address for the shared memory map is used as this increases the
fuzzing speed fuzzing speed
- InsTrim now has an LTO version! :-) That is the best and fastest mode!
- fixes to LTO mode if instrumented edges > MAP_SIZE - fixes to LTO mode if instrumented edges > MAP_SIZE
- CTX and NGRAM can now be used together - CTX and NGRAM can now be used together
- CTX and NGRAM are now also supported in CFG/INSTRIM mode - CTX and NGRAM are now also supported in CFG/INSTRIM mode
@ -38,7 +41,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
network (not fuzzing tcp/ip services but running afl-fuzz on one system network (not fuzzing tcp/ip services but running afl-fuzz on one system
and the target being on an embedded device) and the target being on an embedded device)
- added examples/afl_untracer which does a binary-only fuzzing with the - added examples/afl_untracer which does a binary-only fuzzing with the
modifications done in memory modifications done in memory (intel32/64 and aarch64 support)
- added examples/afl_proxy which can be easily used to fuzz and instrument - added examples/afl_proxy which can be easily used to fuzz and instrument
non-standard things non-standard things
- all: - all:

View File

@ -83,6 +83,10 @@ tools make fairly broad use of environmental variables:
The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset
of the settings discussed in section #1, with the exception of: of the settings discussed in section #1, with the exception of:
- Setting AFL_LLVM_SKIPSINGLEBLOCK=1 will skip instrumenting
functions with a single basic block. This is useful for most C and
some C++ targets. This works for all instrumentation modes.
- AFL_AS, since this toolchain does not directly invoke GNU as. - AFL_AS, since this toolchain does not directly invoke GNU as.
- TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are - TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are
@ -116,6 +120,9 @@ Then there are a few specific features that are only available in llvm_mode:
afl-clang-lto/afl-clang-lto++ instead of afl-clang-fast, but is only afl-clang-lto/afl-clang-lto++ instead of afl-clang-fast, but is only
built if LLVM 11 or newer is used. built if LLVM 11 or newer is used.
- AFL_LLVM_INSTRUMENT=CFG will use Control Flow Graph instrumentation.
(recommended)
- AFL_LLVM_LTO_AUTODICTIONARY will generate a dictionary in the target - AFL_LLVM_LTO_AUTODICTIONARY will generate a dictionary in the target
binary based on string compare and memory compare functions. binary based on string compare and memory compare functions.
afl-fuzz will automatically get these transmitted when starting to afl-fuzz will automatically get these transmitted when starting to
@ -139,7 +146,13 @@ Then there are a few specific features that are only available in llvm_mode:
### INSTRIM ### INSTRIM
This feature increases the speed by ~15% without any disadvantages. This feature increases the speed by ~15% without any disadvantages to the
classic instrumentation.
Note that there is also an LTO version (if you have llvm 11 or higher) -
that is the best instrumentation we have. Use `afl-clang-lto` to activate.
The InsTrim LTO version additionally has all the options and features of
LTO (see above).
- Setting AFL_LLVM_INSTRIM or AFL_LLVM_INSTRUMENT=CFG to activates this mode - Setting AFL_LLVM_INSTRIM or AFL_LLVM_INSTRUMENT=CFG to activates this mode
@ -147,10 +160,6 @@ Then there are a few specific features that are only available in llvm_mode:
afl-fuzz will only be able to see the path the loop took, but not how afl-fuzz will only be able to see the path the loop took, but not how
many times it was called (unless it is a complex loop). many times it was called (unless it is a complex loop).
- Setting AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK=1 will skip instrumenting
functions with a single basic block. This is useful for most C and
some C++ targets.
See llvm_mode/README.instrim.md See llvm_mode/README.instrim.md
### NGRAM ### NGRAM

View File

@ -1,10 +1,16 @@
ifdef DEBUG
OPT=-O0
else
OPT=-O3
endif
all: afl-untracer libtestinstr.so all: afl-untracer libtestinstr.so
afl-untracer: afl-untracer.c afl-untracer: afl-untracer.c
$(CC) -I../../include -g -o afl-untracer afl-untracer.c -ldl -pthread $(CC) $(OPT) -I../../include -g -o afl-untracer afl-untracer.c -ldl
libtestinstr.so: libtestinstr.c libtestinstr.so: libtestinstr.c
$(CC) -fPIC -o libtestinstr.so -shared libtestinstr.c $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c
clean: clean:
rm -f afl-untracer libtestinstr.so *~ core rm -f afl-untracer libtestinstr.so *~ core

View File

@ -1,19 +1,58 @@
# afl-untracer # afl-untracer - fast fuzzing of binary-only libraries
## Introduction
afl-untracer is an example skeleton file which can easily be used to fuzz afl-untracer is an example skeleton file which can easily be used to fuzz
a closed source library. a closed source library.
It requires less memory than qemu_mode however it is way It requires less memory and is x3-5 faster than qemu_mode however it is way
more course grained and does not provide interesting features like compcov more course grained and does not provide interesting features like compcov
or cmplog. or cmplog.
Read and modify afl-untracer.c then `make` and use it as the afl-fuzz target Supported is so far Intel (i386/x86_64) and AARCH64.
(or even remote via afl-network-proxy).
## How-to
### Modify afl-untracer.c
Read and modify afl-untracer.c then `make`.
To adapt afl-untracer.c to your need read the header of the file and then
search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations.
### Generate patches.txt file
To generate the `patches.txt` file for your target library use the To generate the `patches.txt` file for your target library use the
`ida_get_patchpoints.py` script for IDA Pro or `ida_get_patchpoints.py` script for IDA Pro or
`ghidra_get_patchpoints.java` for Ghidra. `ghidra_get_patchpoints.java` for Ghidra.
The patches.txt file has to pointed to by `AFL_UNTRACER_FILE`.
To easily run the scripts without needing to run the GUI with Ghidra:
```
$ /opt/ghidra/support/analyzeHeadless /tmp/ tmp$$ -import libtestinstr.so -postscript ./ghidra_get_patchpoints.java
$ rm -rf /tmp/tmp$$
```
### Fuzzing
Example (after modifying afl-untracer.c to your needs, compiling and creating
patches.txt):
```
AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer
```
(or even remote via afl-network-proxy).
### Testing and debugging
For testing/debugging you can try:
```
make DEBUG=1
AFL_UNTRACER_FILE=./patches.txt AFL_DEBUG=1 gdb ./afl-untracer
```
and then you can easily set breakpoints to "breakpoint" and "fuzz".
# Background
This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL) This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL)
and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz). and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz).
This implementation is slower because the traps are not patched out with each This implementation is slower because the traps are not patched out with each

View File

@ -156,7 +156,7 @@ void read_library_information() {
liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16);
if (debug) if (debug)
fprintf( fprintf(
stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name,
liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start,
liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_start,
liblist[liblist_cnt].addr_end - 1); liblist[liblist_cnt].addr_end - 1);
@ -276,6 +276,18 @@ library_list_t *find_library(char *name) {
} }
/* for having an easy breakpoint after load the shared library */
// this seems to work for clang too. nice :) requires gcc 4.4+
#pragma GCC push_options
#pragma GCC optimize("O0")
void breakpoint() {
if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n");
}
#pragma GCC pop_options
/* Error reporting to forkserver controller */ /* Error reporting to forkserver controller */
void send_forkserver_error(int error) { void send_forkserver_error(int error) {
@ -433,10 +445,17 @@ static void __afl_end_testcase(int status) {
} }
#ifdef __aarch64__
#define SHADOW(addr) \
((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \
MEMORY_MAP_DECREMENT - \
((uintptr_t)addr & 0x7) * 0x10000000000))
#else
#define SHADOW(addr) \ #define SHADOW(addr) \
((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \ ((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \
MEMORY_MAP_DECREMENT - \ MEMORY_MAP_DECREMENT - \
((uintptr_t)addr & 0x3) * 0x10000000000)) ((uintptr_t)addr & 0x3) * 0x10000000000))
#endif
void setup_trap_instrumentation() { void setup_trap_instrumentation() {
@ -452,8 +471,12 @@ void setup_trap_instrumentation() {
FILE *patches = fopen(filename, "r"); FILE *patches = fopen(filename, "r");
if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename); if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename);
// Index into the coverage bitmap for the current trap instruction. // Index into the coverage bitmap for the current trap instruction.
int bitmap_index = 0; #ifdef __aarch64__
uint64_t bitmap_index = 0;
#else
uint32_t bitmap_index = 0;
#endif
while ((nread = getline(&line, &len, patches)) != -1) { while ((nread = getline(&line, &len, patches)) != -1) {
@ -485,9 +508,15 @@ void setup_trap_instrumentation() {
PROT_READ | PROT_WRITE | PROT_EXEC) != 0) PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
FATAL("Failed to mprotect library %s writable", line); FATAL("Failed to mprotect library %s writable", line);
// Create shadow memory. // Create shadow memory.
#ifdef __aarch64__
for (int i = 0; i < 8; i++) {
#else
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
#endif
void *shadow_addr = SHADOW(lib_addr + i); void *shadow_addr = SHADOW(lib_addr + i);
void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE, void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0); MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0);
@ -513,12 +542,18 @@ void setup_trap_instrumentation() {
if (bitmap_index >= __afl_map_size) if (bitmap_index >= __afl_map_size)
FATAL("Too many basic blocks to instrument"); FATAL("Too many basic blocks to instrument");
uint32_t *shadow = SHADOW(lib_addr + offset); #ifdef __arch64__
uint64_t
#else
uint32_t
#endif
*shadow = SHADOW(lib_addr + offset);
if (*shadow != 0) continue; // skip duplicates if (*shadow != 0) continue; // skip duplicates
// Make lookup entry in shadow memory. // Make lookup entry in shadow memory.
#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__)) #if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \
defined(__i386__))
// this is for Intel x64 // this is for Intel x64
@ -531,15 +566,29 @@ void setup_trap_instrumentation() {
lib_addr, offset, lib_addr + offset, orig_byte, shadow, lib_addr, offset, lib_addr + offset, orig_byte, shadow,
bitmap_index, *shadow); bitmap_index, *shadow);
#elif defined(__aarch64__)
// this is for aarch64
uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset);
uint32_t orig_bytes = *patch_bytes;
*shadow = (bitmap_index << 32) | orig_bytes;
*patch_bytes = 0xd4200000; // replace instruction with debug trap
if (debug)
fprintf(stderr,
"Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n",
lib_addr, offset, lib_addr + offset, orig_bytes, shadow,
bitmap_index, *shadow);
#else #else
// this will be ARM and AARCH64 // this will be ARM and AARCH64
// for ARM we will need to identify if the code is in thumb or ARM // for ARM we will need to identify if the code is in thumb or ARM
#error "non x86_64 not supported yet" #error "non x86_64/aarch64 not supported yet"
//__arm__: //__arm__:
// linux thumb: 0xde01 // linux thumb: 0xde01
// linux arm: 0xe7f001f0 // linux arm: 0xe7f001f0
//__aarch64__: //__aarch64__:
// linux aarch64: 0xd4200000 // linux aarch64: 0xd4200000
#endif #endif
bitmap_index++; bitmap_index++;
@ -573,8 +622,15 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
ctx->uc_mcontext->__ss.__rip -= 1; ctx->uc_mcontext->__ss.__rip -= 1;
addr = ctx->uc_mcontext->__ss.__rip; addr = ctx->uc_mcontext->__ss.__rip;
#elif defined(__linux__) #elif defined(__linux__)
#if defined(__x86_64__) || defined(__i386__)
ctx->uc_mcontext.gregs[REG_RIP] -= 1; ctx->uc_mcontext.gregs[REG_RIP] -= 1;
addr = ctx->uc_mcontext.gregs[REG_RIP]; addr = ctx->uc_mcontext.gregs[REG_RIP];
#elif defined(__aarch64__)
ctx->uc_mcontext.pc -= 4;
addr = ctx->uc_mcontext.pc;
#else
#error "Unsupported processor"
#endif
#elif defined(__FreeBSD__) && defined(__LP64__) #elif defined(__FreeBSD__) && defined(__LP64__)
ctx->uc_mcontext.mc_rip -= 1; ctx->uc_mcontext.mc_rip -= 1;
addr = ctx->uc_mcontext.mc_rip; addr = ctx->uc_mcontext.mc_rip;
@ -642,6 +698,7 @@ int main(int argc, char *argv[]) {
// END STEP 2 // END STEP 2
/* setup instrumentation, shared memory and forkserver */ /* setup instrumentation, shared memory and forkserver */
breakpoint();
read_library_information(); read_library_information();
setup_trap_instrumentation(); setup_trap_instrumentation();
__afl_map_shm(); __afl_map_shm();

View File

@ -57,3 +57,6 @@ with open(home + "/Desktop/patches.txt", "w") as f:
f.write('\n') f.write('\n')
print("Done, found {} patchpoints".format(len(patchpoints))) print("Done, found {} patchpoints".format(len(patchpoints)))
# For headless script running remove the comment from the next line
#ida_pro.qexit()

View File

@ -238,7 +238,7 @@ ifeq "$(TEST_MMAP)" "1"
LDFLAGS += -Wno-deprecated-declarations LDFLAGS += -Wno-deprecated-declarations
endif endif
PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-lto-whitelist.so ../afl-llvm-lto-instrumentation.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-lto-whitelist.so ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so
# If prerequisites are not given, warn, do not build anything, and exit with code 0 # If prerequisites are not given, warn, do not build anything, and exit with code 0
ifeq "$(LLVMVER)" "" ifeq "$(LLVMVER)" ""
@ -330,6 +330,11 @@ ifeq "$(LLVM_LTO)" "1"
@$(CLANG_BIN) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi @$(CLANG_BIN) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
endif endif
../afl-llvm-lto-instrim.so: afl-llvm-lto-instrim.so.cc afl-llvm-common.o
ifeq "$(LLVM_LTO)" "1"
$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o
endif
# laf # laf
../split-switches-pass.so: split-switches-pass.so.cc afl-llvm-common.o | test_deps ../split-switches-pass.so: split-switches-pass.so.cc afl-llvm-common.o | test_deps
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
@ -373,7 +378,7 @@ all_done: test_build
install: all install: all
install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f ../afl-llvm-rt-32.o ]; then set -e; install -m 755 ../afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi if [ -f ../afl-llvm-rt-32.o ]; then set -e; install -m 755 ../afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi
if [ -f ../afl-llvm-rt-64.o ]; then set -e; install -m 755 ../afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi if [ -f ../afl-llvm-rt-64.o ]; then set -e; install -m 755 ../afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi
if [ -f ../compare-transform-pass.so ]; then set -e; install -m 755 ../compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi if [ -f ../compare-transform-pass.so ]; then set -e; install -m 755 ../compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi

View File

@ -134,7 +134,8 @@ struct InsTrim : public ModulePass {
} }
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") != NULL) if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
function_minimum_size = 2; function_minimum_size = 2;
unsigned PrevLocSize = 0; unsigned PrevLocSize = 0;
@ -394,7 +395,7 @@ struct InsTrim : public ModulePass {
if ((callInst = dyn_cast<CallInst>(&IN))) { if ((callInst = dyn_cast<CallInst>(&IN))) {
Function *Callee = callInst->getCalledFunction(); Function *Callee = callInst->getCalledFunction();
if (!Callee || Callee->size() < 2) if (!Callee || Callee->size() < function_minimum_size)
continue; continue;
else { else {

View File

@ -6,6 +6,8 @@ This version requires a current llvm 11 compiled from the github master.
1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better 1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better
coverage than anything else that is out there in the AFL world coverage than anything else that is out there in the AFL world
1a. Set AFL_LLVM_INSTRUMENT=CFG if you want the InsTrimLTO version
(recommended)
2. You can use it together with llvm_mode: laf-intel and whitelisting 2. You can use it together with llvm_mode: laf-intel and whitelisting
features and can be combined with cmplog/Redqueen features and can be combined with cmplog/Redqueen
@ -41,7 +43,7 @@ and many dead ends until we got to this:
-fsanitize=coverage edge coverage mode :) -fsanitize=coverage edge coverage mode :)
The result: The result:
* 10-20% speed gain compared to llvm_mode * 10-25% speed gain compared to llvm_mode
* guaranteed non-colliding edge coverage :-) * guaranteed non-colliding edge coverage :-)
* The compile time especially for libraries can be longer * The compile time especially for libraries can be longer
@ -80,11 +82,13 @@ Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc.
Also whitelisting (AFL_LLVM_WHITELIST -> [README.whitelist.md](README.whitelist.md)) and Also whitelisting (AFL_LLVM_WHITELIST -> [README.whitelist.md](README.whitelist.md)) and
laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work.
Instrim does not - but we can not really use it anyway for our approach. InsTrim (control flow graph instrumentation) is supported and recommended!
(set `AFL_LLVM_INSTRUMENT=CFG`)
Example: Example:
``` ```
CC=afl-clang-lto CXX=afl-clang-lto++ ./configure CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure
export AFL_LLVM_INSTRUMENT=CFG
make make
``` ```

View File

@ -43,7 +43,7 @@ static u8 * obj_path; /* Path to runtime libraries */
static u8 **cc_params; /* Parameters passed to the real CC */ static u8 **cc_params; /* Parameters passed to the real CC */
static u32 cc_par_cnt = 1; /* Param count, including argv0 */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */
static u8 llvm_fullpath[PATH_MAX]; static u8 llvm_fullpath[PATH_MAX];
static u8 instrument_mode, instrument_opt_mode, ngram_size; static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode;
static u8 * lto_flag = AFL_CLANG_FLTO; static u8 * lto_flag = AFL_CLANG_FLTO;
static u8 * march_opt = CFLAGS_OPT; static u8 * march_opt = CFLAGS_OPT;
static u8 debug; static u8 debug;
@ -170,7 +170,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
else else
++name; ++name;
if (instrument_mode == INSTRUMENT_LTO) if (lto_mode)
if (lto_flag[0] != '-') if (lto_flag[0] != '-')
FATAL( FATAL(
"Using afl-clang-lto is not possible because Makefile magic did not " "Using afl-clang-lto is not possible because Makefile magic did not "
@ -227,8 +227,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (getenv("LAF_TRANSFORM_COMPARES") || if (getenv("LAF_TRANSFORM_COMPARES") ||
getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
if (!be_quiet && getenv("AFL_LLVM_LTO_AUTODICTIONARY") && if (!be_quiet && getenv("AFL_LLVM_LTO_AUTODICTIONARY") && lto_mode)
instrument_mode != INSTRUMENT_LTO)
WARNF( WARNF(
"using AFL_LLVM_LAF_TRANSFORM_COMPARES together with " "using AFL_LLVM_LAF_TRANSFORM_COMPARES together with "
"AFL_LLVM_LTO_AUTODICTIONARY makes no sense. Use only " "AFL_LLVM_LTO_AUTODICTIONARY makes no sense. Use only "
@ -281,7 +280,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
} }
if (instrument_mode == INSTRUMENT_LTO) { if (lto_mode) {
if (getenv("AFL_LLVM_WHITELIST") != NULL) { if (getenv("AFL_LLVM_WHITELIST") != NULL) {
@ -295,8 +294,12 @@ static void edit_params(u32 argc, char **argv, char **envp) {
cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD); cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD);
cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition";
cc_params[cc_par_cnt++] = alloc_printf( if (instrument_mode == INSTRUMENT_CFG)
"-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); cc_params[cc_par_cnt++] =
alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so", obj_path);
else
cc_params[cc_par_cnt++] = alloc_printf(
"-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path);
cc_params[cc_par_cnt++] = lto_flag; cc_params[cc_par_cnt++] = lto_flag;
} else { } else {
@ -391,7 +394,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (getenv("AFL_USE_CFISAN")) { if (getenv("AFL_USE_CFISAN")) {
if (instrument_mode != INSTRUMENT_LTO) { if (!lto_mode) {
uint32_t i = 0, found = 0; uint32_t i = 0, found = 0;
while (envp[i] != NULL && !found) while (envp[i] != NULL && !found)
@ -417,9 +420,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
getenv("LAF_TRANSFORM_COMPARES") || getenv("LAF_TRANSFORM_COMPARES") ||
(instrument_mode == INSTRUMENT_LTO && (lto_mode && (getenv("AFL_LLVM_LTO_AUTODICTIONARY") ||
(getenv("AFL_LLVM_LTO_AUTODICTIONARY") || getenv("AFL_LLVM_AUTODICTIONARY")))) {
getenv("AFL_LLVM_AUTODICTIONARY")))) {
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
@ -500,7 +502,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
case 0: case 0:
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path);
if (instrument_mode == INSTRUMENT_LTO) if (lto_mode)
cc_params[cc_par_cnt++] = cc_params[cc_par_cnt++] =
alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); alloc_printf("%s/afl-llvm-rt-lto.o", obj_path);
break; break;
@ -509,7 +511,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); 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)) if (access(cc_params[cc_par_cnt - 1], R_OK))
FATAL("-m32 is not supported by your compiler"); FATAL("-m32 is not supported by your compiler");
if (instrument_mode == INSTRUMENT_LTO) { if (lto_mode) {
cc_params[cc_par_cnt++] = cc_params[cc_par_cnt++] =
alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path);
@ -524,7 +526,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); 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)) if (access(cc_params[cc_par_cnt - 1], R_OK))
FATAL("-m64 is not supported by your compiler"); FATAL("-m64 is not supported by your compiler");
if (instrument_mode == INSTRUMENT_LTO) { if (lto_mode) {
cc_params[cc_par_cnt++] = cc_params[cc_par_cnt++] =
alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path);
@ -548,7 +550,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
int main(int argc, char **argv, char **envp) { int main(int argc, char **argv, char **envp) {
int i; int i;
char *callname = "afl-clang-fast", *ptr; char *callname = "afl-clang-fast", *ptr = NULL;
if (getenv("AFL_DEBUG")) { if (getenv("AFL_DEBUG")) {
@ -630,7 +632,13 @@ int main(int argc, char **argv, char **envp) {
if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 || if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 ||
strncasecmp(ptr, "instrim", strlen("instrim")) == 0) { strncasecmp(ptr, "instrim", strlen("instrim")) == 0) {
if (!instrument_mode || instrument_mode == INSTRUMENT_CFG) if (instrument_mode == INSTRUMENT_LTO) {
instrument_mode = INSTRUMENT_CFG;
lto_mode = 1;
} else if (!instrument_mode || instrument_mode == INSTRUMENT_CFG)
instrument_mode = INSTRUMENT_CFG; instrument_mode = INSTRUMENT_CFG;
else else
FATAL("main instrumentation mode already set with %s", FATAL("main instrumentation mode already set with %s",
@ -640,9 +648,10 @@ int main(int argc, char **argv, char **envp) {
if (strncasecmp(ptr, "lto", strlen("lto")) == 0) { if (strncasecmp(ptr, "lto", strlen("lto")) == 0) {
lto_mode = 1;
if (!instrument_mode || instrument_mode == INSTRUMENT_LTO) if (!instrument_mode || instrument_mode == INSTRUMENT_LTO)
instrument_mode = INSTRUMENT_LTO; instrument_mode = INSTRUMENT_LTO;
else else if (instrument_mode != INSTRUMENT_CFG)
FATAL("main instrumentation mode already set with %s", FATAL("main instrumentation mode already set with %s",
instrument_mode_string[instrument_mode]); instrument_mode_string[instrument_mode]);
@ -684,9 +693,44 @@ int main(int argc, char **argv, char **envp) {
} }
if (!instrument_opt_mode) if (strstr(argv[0], "afl-clang-lto") != NULL) {
ptr = instrument_mode_string[instrument_mode];
else if (instrument_opt_mode == INSTRUMENT_OPT_CTX) if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO ||
instrument_mode == INSTRUMENT_CFG) {
lto_mode = 1;
callname = "afl-clang-lto";
if (!instrument_mode) {
instrument_mode = INSTRUMENT_LTO;
ptr = instrument_mode_string[instrument_mode];
}
} else {
if (!be_quiet)
WARNF("afl-clang-lto called with mode %s, using that mode instead",
instrument_mode_string[instrument_mode]);
}
}
if (instrument_opt_mode && lto_mode)
FATAL(
"CTX and NGRAM can not be used in LTO mode (and would make LTO "
"useless)");
if (!instrument_opt_mode) {
if (lto_mode && instrument_mode == INSTRUMENT_CFG)
ptr = alloc_printf("InsTrimLTO");
else
ptr = instrument_mode_string[instrument_mode];
} else if (instrument_opt_mode == INSTRUMENT_OPT_CTX)
ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]); ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]);
else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM) else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM)
ptr = alloc_printf("%s + NGRAM-%u", instrument_mode_string[instrument_mode], ptr = alloc_printf("%s + NGRAM-%u", instrument_mode_string[instrument_mode],
@ -695,26 +739,8 @@ int main(int argc, char **argv, char **envp) {
ptr = alloc_printf("%s + CTX + NGRAM-%u", ptr = alloc_printf("%s + CTX + NGRAM-%u",
instrument_mode_string[instrument_mode], ngram_size); instrument_mode_string[instrument_mode], ngram_size);
if (strstr(argv[0], "afl-clang-lto") != NULL) {
if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO) {
callname = "afl-clang-lto";
instrument_mode = INSTRUMENT_LTO;
ptr = instrument_mode_string[instrument_mode];
} else {
if (!be_quiet)
WARNF("afl-clang-lto called with mode %s, using that mode instead",
ptr);
}
}
#ifndef AFL_CLANG_FLTO #ifndef AFL_CLANG_FLTO
if (instrument_mode == INSTRUMENT_LTO) if (lto_mode)
FATAL( FATAL(
"instrumentation mode LTO specified but LLVM support not available " "instrumentation mode LTO specified but LLVM support not available "
"(requires LLVM 11 or higher)"); "(requires LLVM 11 or higher)");
@ -733,7 +759,7 @@ int main(int argc, char **argv, char **envp) {
if (argc < 2 || strcmp(argv[1], "-h") == 0) { if (argc < 2 || strcmp(argv[1], "-h") == 0) {
if (instrument_mode != INSTRUMENT_LTO) if (!lto_mode)
printf("afl-clang-fast" VERSION " by <lszekeres@google.com> in %s mode\n", printf("afl-clang-fast" VERSION " by <lszekeres@google.com> in %s mode\n",
ptr); ptr);
else else
@ -831,7 +857,7 @@ int main(int argc, char **argv, char **envp) {
getenv("AFL_DEBUG") != NULL) { getenv("AFL_DEBUG") != NULL) {
if (instrument_mode != INSTRUMENT_LTO) if (!lto_mode)
SAYF(cCYA "afl-clang-fast" VERSION cRST SAYF(cCYA "afl-clang-fast" VERSION cRST
" by <lszekeres@google.com> in %s mode\n", " by <lszekeres@google.com> in %s mode\n",
@ -846,7 +872,7 @@ int main(int argc, char **argv, char **envp) {
} }
u8 *ptr2; u8 *ptr2;
if (!be_quiet && instrument_mode != INSTRUMENT_LTO && if (!be_quiet && !lto_mode &&
((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) { ((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) {
u32 map_size = atoi(ptr2); u32 map_size = atoi(ptr2);

View File

@ -201,3 +201,20 @@ bool isInWhitelist(llvm::Function *F) {
} }
// Calculate the number of average collisions that would occur if all
// location IDs would be assigned randomly (like normal afl/afl++).
// This uses the "balls in bins" algorithm.
unsigned long long int calculateCollisions(uint32_t edges) {
double bins = MAP_SIZE;
double balls = edges;
double step1 = 1 - (1 / bins);
double step2 = pow(step1, balls);
double step3 = bins * step2;
double step4 = round(step3);
unsigned long long int empty = step4;
unsigned long long int collisions = edges - (MAP_SIZE - empty);
return collisions;
}

View File

@ -32,10 +32,11 @@ typedef long double max_align_t;
#include "llvm/Support/CFG.h" #include "llvm/Support/CFG.h"
#endif #endif
char *getBBName(const llvm::BasicBlock *BB); char * getBBName(const llvm::BasicBlock *BB);
bool isBlacklisted(const llvm::Function *F); bool isBlacklisted(const llvm::Function *F);
void initWhitelist(); void initWhitelist();
bool isInWhitelist(llvm::Function *F); bool isInWhitelist(llvm::Function *F);
unsigned long long int calculateCollisions(uint32_t edges);
#endif #endif

View File

@ -0,0 +1,934 @@
/*
american fuzzy lop++ - LLVM-mode instrumentation pass
---------------------------------------------------
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
This library is plugged into LLVM when invoking clang through afl-clang-fast.
*/
#define AFL_LLVM_PASS
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <unordered_set>
#include <list>
#include <string>
#include <fstream>
#include <set>
#include "llvm/Config/llvm-config.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Pass.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/ValueTracking.h"
#include "MarkNodes.h"
#include "afl-llvm-common.h"
#include "config.h"
#include "debug.h"
using namespace llvm;
static cl::opt<bool> MarkSetOpt("markset", cl::desc("MarkSet"),
cl::init(false));
static cl::opt<bool> LoopHeadOpt("loophead", cl::desc("LoopHead"),
cl::init(false));
namespace {
struct InsTrimLTO : public ModulePass {
protected:
uint32_t function_minimum_size = 1;
char * skip_nozero = NULL;
int afl_global_id = 1, debug = 0, autodictionary = 0;
uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0;
uint64_t map_addr = 0x10000;
public:
static char ID;
InsTrimLTO() : ModulePass(ID) {
char *ptr;
if (getenv("AFL_DEBUG")) debug = 1;
if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL)
if ((afl_global_id = atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE)
FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n",
ptr, MAP_SIZE - 1);
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
ModulePass::getAnalysisUsage(AU);
AU.addRequired<DominatorTreeWrapperPass>();
AU.addRequired<LoopInfoWrapperPass>();
}
StringRef getPassName() const override {
return "InstTrim LTO Instrumentation";
}
bool runOnModule(Module &M) override {
char be_quiet = 0;
char *ptr;
if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
SAYF(cCYA "InsTrimLTO" VERSION cRST
" by csienslab and Marc \"vanHauser\" Heuse\n");
} else
be_quiet = 1;
/* Process environment variables */
if (getenv("AFL_LLVM_AUTODICTIONARY") ||
getenv("AFL_LLVM_LTO_AUTODICTIONARY"))
autodictionary = 1;
if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0;
if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) {
uint64_t val;
if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) {
map_addr = 0;
} else if (map_addr == 0) {
FATAL(
"AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used "
"together");
} else if (strncmp(ptr, "0x", 2) != 0) {
map_addr = 0x10000; // the default
} else {
val = strtoull(ptr, NULL, 16);
if (val < 0x100 || val > 0xffffffff00000000) {
FATAL(
"AFL_LLVM_MAP_ADDR must be a value between 0x100 and "
"0xffffffff00000000");
}
map_addr = val;
}
}
if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); }
if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL ||
getenv("LOOPHEAD") != NULL) {
LoopHeadOpt = true;
}
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
function_minimum_size = 2;
// this is our default
MarkSetOpt = true;
/* Initialize LLVM instrumentation */
LLVMContext & C = M.getContext();
std::vector<std::string> dictionary;
std::vector<CallInst *> calls;
DenseMap<Value *, std::string *> valueMap;
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
/* Get/set globals for the SHM region. */
GlobalVariable *AFLMapPtr = NULL;
Value * MapPtrFixed = NULL;
if (!map_addr) {
AFLMapPtr =
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
} else {
ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
MapPtrFixed =
ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty));
}
if (autodictionary) {
/* Some implementation notes.
*
* We try to handle 3 cases:
* - memcmp("foo", arg, 3) <- literal string
* - static char globalvar[] = "foo";
* memcmp(globalvar, arg, 3) <- global variable
* - char localvar[] = "foo";
* memcmp(locallvar, arg, 3) <- local variable
*
* The local variable case is the hardest. We can only detect that
* case if there is no reassignment or change in the variable.
* And it might not work across llvm version.
* What we do is hooking the initializer function for local variables
* (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned
* variable. And if that variable is then used in a compare function
* we use that noted string.
* This seems not to work for tokens that have a size <= 4 :-(
*
* - if the compared length is smaller than the string length we
* save the full string. This is likely better for fuzzing but
* might be wrong in a few cases depending on optimizers
*
* - not using StringRef because there is a bug in the llvm 11
* checkout I am using which sometimes points to wrong strings
*
* Over and out. Took me a full day. damn. mh/vh
*/
for (Function &F : M) {
for (auto &BB : F) {
for (auto &IN : BB) {
CallInst *callInst = nullptr;
if ((callInst = dyn_cast<CallInst>(&IN))) {
bool isStrcmp = true;
bool isMemcmp = true;
bool isStrncmp = true;
bool isStrcasecmp = true;
bool isStrncasecmp = true;
bool isIntMemcpy = true;
bool addedNull = false;
uint8_t optLen = 0;
Function *Callee = callInst->getCalledFunction();
if (!Callee) continue;
if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
std::string FuncName = Callee->getName().str();
isStrcmp &= !FuncName.compare("strcmp");
isMemcmp &= !FuncName.compare("memcmp");
isStrncmp &= !FuncName.compare("strncmp");
isStrcasecmp &= !FuncName.compare("strcasecmp");
isStrncasecmp &= !FuncName.compare("strncasecmp");
isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64");
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
!isStrncasecmp && !isIntMemcpy)
continue;
/* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp
* function prototype */
FunctionType *FT = Callee->getFunctionType();
isStrcmp &= FT->getNumParams() == 2 &&
FT->getReturnType()->isIntegerTy(32) &&
FT->getParamType(0) == FT->getParamType(1) &&
FT->getParamType(0) ==
IntegerType::getInt8PtrTy(M.getContext());
isStrcasecmp &= FT->getNumParams() == 2 &&
FT->getReturnType()->isIntegerTy(32) &&
FT->getParamType(0) == FT->getParamType(1) &&
FT->getParamType(0) ==
IntegerType::getInt8PtrTy(M.getContext());
isMemcmp &= FT->getNumParams() == 3 &&
FT->getReturnType()->isIntegerTy(32) &&
FT->getParamType(0)->isPointerTy() &&
FT->getParamType(1)->isPointerTy() &&
FT->getParamType(2)->isIntegerTy();
isStrncmp &= FT->getNumParams() == 3 &&
FT->getReturnType()->isIntegerTy(32) &&
FT->getParamType(0) == FT->getParamType(1) &&
FT->getParamType(0) ==
IntegerType::getInt8PtrTy(M.getContext()) &&
FT->getParamType(2)->isIntegerTy();
isStrncasecmp &= FT->getNumParams() == 3 &&
FT->getReturnType()->isIntegerTy(32) &&
FT->getParamType(0) == FT->getParamType(1) &&
FT->getParamType(0) ==
IntegerType::getInt8PtrTy(M.getContext()) &&
FT->getParamType(2)->isIntegerTy();
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
!isStrncasecmp && !isIntMemcpy)
continue;
/* is a str{n,}{case,}cmp/memcmp, check if we have
* str{case,}cmp(x, "const") or str{case,}cmp("const", x)
* strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x,
* ..) memcmp(x, "const", ..) or memcmp("const", x, ..) */
Value *Str1P = callInst->getArgOperand(0),
*Str2P = callInst->getArgOperand(1);
std::string Str1, Str2;
StringRef TmpStr;
bool HasStr1 = getConstantStringInfo(Str1P, TmpStr);
if (TmpStr.empty())
HasStr1 = false;
else
Str1 = TmpStr.str();
bool HasStr2 = getConstantStringInfo(Str2P, TmpStr);
if (TmpStr.empty())
HasStr2 = false;
else
Str2 = TmpStr.str();
if (debug)
fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n",
FuncName.c_str(), Str1P, Str1P->getName().str().c_str(),
Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P,
Str2P->getName().str().c_str(), Str2.c_str(),
HasStr2 == true ? "true" : "false");
// we handle the 2nd parameter first because of llvm memcpy
if (!HasStr2) {
auto *Ptr = dyn_cast<ConstantExpr>(Str2P);
if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
if (auto *Var =
dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
if (Var->hasInitializer()) {
if (auto *Array = dyn_cast<ConstantDataArray>(
Var->getInitializer())) {
HasStr2 = true;
Str2 = Array->getAsString().str();
}
}
}
}
}
// for the internal memcpy routine we only care for the second
// parameter and are not reporting anything.
if (isIntMemcpy == true) {
if (HasStr2 == true) {
Value * op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
if (ilen) {
uint64_t literalLength = Str2.size();
uint64_t optLength = ilen->getZExtValue();
if (literalLength + 1 == optLength) {
Str2.append("\0", 1); // add null byte
addedNull = true;
}
}
valueMap[Str1P] = new std::string(Str2);
if (debug)
fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P);
continue;
}
continue;
}
// Neither a literal nor a global variable?
// maybe it is a local variable that we saved
if (!HasStr2) {
std::string *strng = valueMap[Str2P];
if (strng && !strng->empty()) {
Str2 = *strng;
HasStr2 = true;
if (debug)
fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(),
Str2P);
}
}
if (!HasStr1) {
auto Ptr = dyn_cast<ConstantExpr>(Str1P);
if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
if (auto *Var =
dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
if (Var->hasInitializer()) {
if (auto *Array = dyn_cast<ConstantDataArray>(
Var->getInitializer())) {
HasStr1 = true;
Str1 = Array->getAsString().str();
}
}
}
}
}
// Neither a literal nor a global variable?
// maybe it is a local variable that we saved
if (!HasStr1) {
std::string *strng = valueMap[Str1P];
if (strng && !strng->empty()) {
Str1 = *strng;
HasStr1 = true;
if (debug)
fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(),
Str1P);
}
}
/* handle cases of one string is const, one string is variable */
if (!(HasStr1 ^ HasStr2)) continue;
std::string thestring;
if (HasStr1)
thestring = Str1;
else
thestring = Str2;
optLen = thestring.length();
if (isMemcmp || isStrncmp || isStrncasecmp) {
Value * op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
if (ilen) {
uint64_t literalLength = optLen;
optLen = ilen->getZExtValue();
if (literalLength + 1 == optLen) { // add null byte
thestring.append("\0", 1);
addedNull = true;
}
}
}
// add null byte if this is a string compare function and a null
// was not already added
if (addedNull == false && !isMemcmp) {
thestring.append("\0", 1); // add null byte
optLen++;
}
if (!be_quiet) {
std::string outstring;
fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen,
(unsigned int)thestring.length());
for (uint8_t i = 0; i < thestring.length(); i++) {
uint8_t c = thestring[i];
if (c <= 32 || c >= 127)
fprintf(stderr, "\\x%02x", c);
else
fprintf(stderr, "%c", c);
}
fprintf(stderr, "\"\n");
}
// we take the longer string, even if the compare was to a
// shorter part. Note that depending on the optimizer of the
// compiler this can be wrong, but it is more likely that this
// is helping the fuzzer
if (optLen != thestring.length()) optLen = thestring.length();
if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA;
if (optLen < MIN_AUTO_EXTRA) // too short? skip
continue;
dictionary.push_back(thestring.substr(0, optLen));
}
}
}
}
}
/* InsTrim instrumentation starts here */
u64 total_rs = 0;
u64 total_hs = 0;
for (Function &F : M) {
if (debug) {
uint32_t bb_cnt = 0;
for (auto &BB : F)
if (BB.size() > 0) ++bb_cnt;
SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n",
F.getName().str().c_str(), F.size(), bb_cnt);
}
// if the function below our minimum size skip it (1 or 2)
if (F.size() < function_minimum_size) continue;
if (isBlacklisted(&F)) continue;
std::unordered_set<BasicBlock *> MS;
if (!MarkSetOpt) {
for (auto &BB : F) {
MS.insert(&BB);
}
total_rs += F.size();
} else {
auto Result = markNodes(&F);
auto RS = Result.first;
auto HS = Result.second;
MS.insert(RS.begin(), RS.end());
if (!LoopHeadOpt) {
MS.insert(HS.begin(), HS.end());
total_rs += MS.size();
} else {
DenseSet<std::pair<BasicBlock *, BasicBlock *>> EdgeSet;
DominatorTreeWrapperPass * DTWP =
&getAnalysis<DominatorTreeWrapperPass>(F);
auto DT = &DTWP->getDomTree();
total_rs += RS.size();
total_hs += HS.size();
for (BasicBlock *BB : HS) {
bool Inserted = false;
for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) {
auto Edge = BasicBlockEdge(*BI, BB);
if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) {
EdgeSet.insert({*BI, BB});
Inserted = true;
break;
}
}
if (!Inserted) {
MS.insert(BB);
total_rs += 1;
total_hs -= 1;
}
}
for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) {
auto PredBB = I->first;
auto SuccBB = I->second;
auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT,
nullptr, nullptr, false);
MS.insert(NewBB);
}
}
}
for (BasicBlock &BB : F) {
auto PI = pred_begin(&BB);
auto PE = pred_end(&BB);
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
Value * L = NULL;
if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; }
if (PI == PE) {
L = ConstantInt::get(Int32Ty, afl_global_id++);
} else {
auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin());
DenseMap<BasicBlock *, unsigned> PredMap;
for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) {
BasicBlock *PBB = *PI;
auto It = PredMap.insert({PBB, afl_global_id++});
unsigned Label = It.first->second;
PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
}
L = PN;
}
/* Load SHM pointer */
Value *MapPtrIdx;
if (map_addr) {
MapPtrIdx = IRB.CreateGEP(MapPtrFixed, L);
} else {
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
MapPtr->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
MapPtrIdx = IRB.CreateGEP(MapPtr, L);
}
/* Update bitmap */
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *Incr = IRB.CreateAdd(Counter, One);
if (skip_nozero) {
auto cf = IRB.CreateICmpEQ(Incr, Zero);
auto carry = IRB.CreateZExt(cf, Int8Ty);
Incr = IRB.CreateAdd(Incr, carry);
}
IRB.CreateStore(Incr, MapPtrIdx)
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
// done :)
inst_blocks++;
}
}
// save highest location ID to global variable
// do this after each function to fail faster
if (!be_quiet && afl_global_id > MAP_SIZE &&
afl_global_id > FS_OPT_MAX_MAPSIZE) {
uint32_t pow2map = 1, map = afl_global_id;
while ((map = map >> 1))
pow2map++;
WARNF(
"We have %u blocks to instrument but the map size is only %u. Either "
"edit config.h and set MAP_SIZE_POW2 from %u to %u, then recompile "
"afl-fuzz and llvm_mode and then make this target - or set "
"AFL_MAP_SIZE with at least size %u when running afl-fuzz with this "
"target.",
afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id);
}
if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) {
// yes we could create our own function, insert it into ctors ...
// but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o
Function *f = M.getFunction("__afl_auto_init_globals");
if (!f) {
fprintf(stderr,
"Error: init function could not be found (this should not "
"happen)\n");
exit(-1);
}
BasicBlock *bb = &f->getEntryBlock();
if (!bb) {
fprintf(stderr,
"Error: init function does not have an EntryBlock (this should "
"not happen)\n");
exit(-1);
}
BasicBlock::iterator IP = bb->getFirstInsertionPt();
IRBuilder<> IRB(&(*IP));
if (map_addr) {
GlobalVariable *AFLMapAddrFixed =
new GlobalVariable(M, Int64Ty, true, GlobalValue::ExternalLinkage,
0, "__afl_map_addr");
ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed);
StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) {
uint32_t write_loc = afl_global_id;
if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3);
GlobalVariable *AFLFinalLoc =
new GlobalVariable(M, Int32Ty, true, GlobalValue::ExternalLinkage,
0, "__afl_final_loc");
ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc);
StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
if (dictionary.size()) {
size_t memlen = 0, count = 0, offset = 0;
char * ptr;
for (auto token : dictionary) {
memlen += token.length();
count++;
}
if (!be_quiet)
printf("AUTODICTIONARY: %lu string%s found\n", count,
count == 1 ? "" : "s");
if (count) {
if ((ptr = (char *)malloc(memlen + count)) == NULL) {
fprintf(stderr, "Error: malloc for %lu bytes failed!\n",
memlen + count);
exit(-1);
}
count = 0;
for (auto token : dictionary) {
if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) {
ptr[offset++] = (uint8_t)token.length();
memcpy(ptr + offset, token.c_str(), token.length());
offset += token.length();
count++;
}
}
GlobalVariable *AFLDictionaryLen = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
"__afl_dictionary_len");
ConstantInt *const_len = ConstantInt::get(Int32Ty, offset);
StoreInst * StoreDictLen =
IRB.CreateStore(const_len, AFLDictionaryLen);
StoreDictLen->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset);
GlobalVariable *AFLInternalDictionary = new GlobalVariable(
M, ArrayTy, true, GlobalValue::ExternalLinkage,
ConstantDataArray::get(
C, *(new ArrayRef<char>((char *)ptr, offset))),
"__afl_internal_dictionary");
AFLInternalDictionary->setInitializer(ConstantDataArray::get(
C, *(new ArrayRef<char>((char *)ptr, offset))));
AFLInternalDictionary->setConstant(true);
GlobalVariable *AFLDictionary = new GlobalVariable(
M, PointerType::get(Int8Ty, 0), false,
GlobalValue::ExternalLinkage, 0, "__afl_dictionary");
Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero);
Value *AFLDictPtr =
IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0));
StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary);
StoreDict->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
}
// count basic blocks for comparison with classic instrumentation
u32 edges = 0;
for (auto &F : M) {
if (F.size() < function_minimum_size) continue;
for (auto &BB : F) {
bool would_instrument = false;
for (BasicBlock *Pred : predecessors(&BB)) {
int count = 0;
for (BasicBlock *Succ : successors(Pred))
if (Succ != NULL) count++;
if (count > 1) return true;
}
if (would_instrument == true) edges++;
}
}
/* Say something nice. */
if (!be_quiet) {
if (!inst_blocks)
WARNF("No instrumentation targets found.");
else {
char modeline[100];
snprintf(modeline, sizeof(modeline), "%s%s%s%s%s",
getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
getenv("AFL_USE_ASAN") ? ", ASAN" : "",
getenv("AFL_USE_MSAN") ? ", MSAN" : "",
getenv("AFL_USE_CFISAN") ? ", CFISAN" : "",
getenv("AFL_USE_UBSAN") ? ", UBSAN" : "");
OKF("Instrumented %u locations (%llu, %llu) with no collisions (on "
"average %llu collisions would be in afl-gcc/afl-clang-fast for %u "
"edges) (%s mode).",
inst_blocks, total_rs, total_hs, calculateCollisions(edges), edges,
modeline);
}
}
return true;
}
}; // end of struct InsTrim
} // end of anonymous namespace
char InsTrimLTO::ID = 0;
static void registerInsTrimLTO(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new InsTrimLTO());
}
static RegisterPass<InsTrimLTO> X("afl-lto-instrim",
"afl++ InsTrim LTO instrumentation pass",
false, false);
static RegisterStandardPasses RegisterInsTrimLTO(
PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerInsTrimLTO);

View File

@ -1,14 +1,9 @@
/* /*
american fuzzy lop++ - LLVM-mode instrumentation pass american fuzzy lop++ - LLVM LTO instrumentation pass
--------------------------------------------------- ----------------------------------------------------
Written by Laszlo Szekeres <lszekeres@google.com> and Written by Marc Heuse <mh@mh-sec.de>
Michal Zalewski
LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted
from afl-as.c are Michal's fault.
Copyright 2015, 2016 Google Inc. All rights reserved.
Copyright 2019-2020 AFLplusplus Project. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -17,9 +12,7 @@
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
This library is plugged into LLVM when invoking clang through afl-clang-fast. This library is plugged into LLVM when invoking clang through afl-clang-lto.
It tells the compiler to add code roughly equivalent to the bits discussed
in ../afl-as.h.
*/ */
@ -32,11 +25,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <sys/time.h>
#include <list> #include <list>
#include <string> #include <string>
#include <fstream> #include <fstream>
#include <sys/time.h> #include <set>
#include "llvm/Config/llvm-config.h" #include "llvm/Config/llvm-config.h"
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
@ -56,7 +50,6 @@
#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/ValueTracking.h"
#include "llvm/Pass.h" #include "llvm/Pass.h"
#include <set>
#include "afl-llvm-common.h" #include "afl-llvm-common.h"
using namespace llvm; using namespace llvm;
@ -90,27 +83,11 @@ class AFLLTOPass : public ModulePass {
} }
// Calculate the number of average collisions that would occur if all
// location IDs would be assigned randomly (like normal afl/afl++).
// This uses the "balls in bins" algorithm.
unsigned long long int calculateCollisions(uint32_t edges) {
double bins = MAP_SIZE;
double balls = edges;
double step1 = 1 - (1 / bins);
double step2 = pow(step1, balls);
double step3 = bins * step2;
double step4 = round(step3);
unsigned long long int empty = step4;
unsigned long long int collisions = edges - (MAP_SIZE - empty);
return collisions;
}
bool runOnModule(Module &M) override; bool runOnModule(Module &M) override;
protected: protected:
int afl_global_id = 1, debug = 0, autodictionary = 0; int afl_global_id = 1, debug = 0, autodictionary = 0;
uint32_t function_minimum_size = 1;
uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0; uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0;
uint64_t map_addr = 0x10000; uint64_t map_addr = 0x10000;
char * skip_nozero = NULL; char * skip_nozero = NULL;
@ -131,8 +108,6 @@ bool AFLLTOPass::runOnModule(Module &M) {
IntegerType *Int32Ty = IntegerType::getInt32Ty(C); IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
IntegerType *Int64Ty = IntegerType::getInt64Ty(C); IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
if (getenv("AFL_DEBUG")) debug = 1;
/* Show a banner */ /* Show a banner */
if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
@ -150,6 +125,10 @@ bool AFLLTOPass::runOnModule(Module &M) {
if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0;
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
function_minimum_size = 2;
if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) {
uint64_t val; uint64_t val;
@ -185,12 +164,10 @@ bool AFLLTOPass::runOnModule(Module &M) {
if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); } if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); }
/* Get globals for the SHM region and the previous location. Note that /* Get/set the globals for the SHM region. */
__afl_prev_loc is thread-local. */
GlobalVariable *AFLMapPtr = NULL; GlobalVariable *AFLMapPtr = NULL;
; Value * MapPtrFixed = NULL;
Value *MapPtrFixed = NULL;
if (!map_addr) { if (!map_addr) {
@ -217,7 +194,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
// fprintf(stderr, "DEBUG: Function %s\n", F.getName().str().c_str()); // fprintf(stderr, "DEBUG: Function %s\n", F.getName().str().c_str());
if (F.size() < 2) continue; if (F.size() < function_minimum_size) continue;
if (isBlacklisted(&F)) continue; if (isBlacklisted(&F)) continue;
std::vector<BasicBlock *> InsBlocks; std::vector<BasicBlock *> InsBlocks;
@ -354,11 +331,15 @@ bool AFLLTOPass::runOnModule(Module &M) {
if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) { if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
if (auto *Array = if (Var->hasInitializer()) {
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
HasStr2 = true; if (auto *Array = dyn_cast<ConstantDataArray>(
Str2 = Array->getAsString().str(); Var->getInitializer())) {
HasStr2 = true;
Str2 = Array->getAsString().str();
}
} }
@ -426,11 +407,15 @@ bool AFLLTOPass::runOnModule(Module &M) {
if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) { if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
if (auto *Array = if (Var->hasInitializer()) {
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
HasStr1 = true; if (auto *Array = dyn_cast<ConstantDataArray>(
Str1 = Array->getAsString().str(); Var->getInitializer())) {
HasStr1 = true;
Str1 = Array->getAsString().str();
}
} }

View File

@ -84,6 +84,7 @@ class AFLCoverage : public ModulePass {
uint32_t ngram_size = 0; uint32_t ngram_size = 0;
uint32_t debug = 0; uint32_t debug = 0;
uint32_t map_size = MAP_SIZE; uint32_t map_size = MAP_SIZE;
uint32_t function_minimum_size = 1;
char * ctx_str = NULL, *skip_nozero = NULL; char * ctx_str = NULL, *skip_nozero = NULL;
}; };
@ -182,6 +183,10 @@ bool AFLCoverage::runOnModule(Module &M) {
#endif #endif
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
function_minimum_size = 2;
unsigned PrevLocSize = 0; unsigned PrevLocSize = 0;
char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE");
@ -294,13 +299,15 @@ bool AFLCoverage::runOnModule(Module &M) {
if (!isInWhitelist(&F)) continue; if (!isInWhitelist(&F)) continue;
if (F.size() < function_minimum_size) continue;
for (auto &BB : F) { for (auto &BB : F) {
BasicBlock::iterator IP = BB.getFirstInsertionPt(); BasicBlock::iterator IP = BB.getFirstInsertionPt();
IRBuilder<> IRB(&(*IP)); IRBuilder<> IRB(&(*IP));
// Context sensitive coverage // Context sensitive coverage
if (ctx_str && &BB == &F.getEntryBlock() && F.size() > 1) { if (ctx_str && &BB == &F.getEntryBlock()) {
// load the context ID of the previous function and write to to a local // load the context ID of the previous function and write to to a local
// variable on the stack // variable on the stack
@ -318,7 +325,7 @@ bool AFLCoverage::runOnModule(Module &M) {
if ((callInst = dyn_cast<CallInst>(&IN))) { if ((callInst = dyn_cast<CallInst>(&IN))) {
Function *Callee = callInst->getCalledFunction(); Function *Callee = callInst->getCalledFunction();
if (!Callee || Callee->size() < 2) if (!Callee || Callee->size() < function_minimum_size)
continue; continue;
else { else {
@ -389,11 +396,11 @@ bool AFLCoverage::runOnModule(Module &M) {
} }
// fprintf(stderr, " == %d\n", more_than_one); // fprintf(stderr, " == %d\n", more_than_one);
if (more_than_one != 1) { if (F.size() > 1 && more_than_one != 1) {
// in CTX mode we have to restore the original context for the caller - // in CTX mode we have to restore the original context for the caller -
// she might be calling other functions which need the correct CTX // she might be calling other functions which need the correct CTX
if (ctx_str && has_calls && F.size() > 1) { if (ctx_str && has_calls) {
Instruction *Inst = BB.getTerminator(); Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) { if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
@ -526,7 +533,7 @@ bool AFLCoverage::runOnModule(Module &M) {
// in CTX mode we have to restore the original context for the caller - // in CTX mode we have to restore the original context for the caller -
// she might be calling other functions which need the correct CTX. // she might be calling other functions which need the correct CTX.
// Currently this is only needed for the Ubuntu clang-6.0 bug // Currently this is only needed for the Ubuntu clang-6.0 bug
if (ctx_str && has_calls && F.size() > 1) { if (ctx_str && has_calls) {
Instruction *Inst = BB.getTerminator(); Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) { if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {

View File

@ -163,6 +163,8 @@ fi
cd qemu-$VERSION || exit 1 cd qemu-$VERSION || exit 1
echo Building for CPU target $CPU_TARGET
echo "[*] Applying patches..." echo "[*] Applying patches..."
patch -p1 <../patches/elfload.diff || exit 1 patch -p1 <../patches/elfload.diff || exit 1
@ -188,6 +190,7 @@ echo "[+] Patching done."
if [ "$STATIC" = "1" ]; then if [ "$STATIC" = "1" ]; then
echo Building STATIC binary
./configure --extra-cflags="-O3 -ggdb -DAFL_QEMU_STATIC_BUILD=1" \ ./configure --extra-cflags="-O3 -ggdb -DAFL_QEMU_STATIC_BUILD=1" \
--disable-bsd-user --disable-guest-agent --disable-strip --disable-werror \ --disable-bsd-user --disable-guest-agent --disable-strip --disable-werror \
--disable-gcrypt --disable-debug-info --disable-debug-tcg --disable-tcg-interpreter \ --disable-gcrypt --disable-debug-info --disable-debug-tcg --disable-tcg-interpreter \

View File

@ -64,15 +64,15 @@ char *afl_environment_variables[] = {
"AFL_LD_PRELOAD", "AFL_LD_VERBOSE", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LD_PRELOAD", "AFL_LD_VERBOSE", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM",
"AFL_LLVM_CTX", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", "AFL_LLVM_CTX", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD",
"AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY", "AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY",
"AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", "AFL_LLVM_LAF_SPLIT_COMPARES", "AFL_LLVM_SKIPSINGLEBLOCK", "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK",
"AFL_LLVM_LAF_SPLIT_COMPARES_BITW", "AFL_LLVM_LAF_SPLIT_FLOATS", "AFL_LLVM_LAF_SPLIT_COMPARES", "AFL_LLVM_LAF_SPLIT_COMPARES_BITW",
"AFL_LLVM_LAF_SPLIT_SWITCHES", "AFL_LLVM_LAF_TRANSFORM_COMPARES", "AFL_LLVM_LAF_SPLIT_FLOATS", "AFL_LLVM_LAF_SPLIT_SWITCHES",
"AFL_LLVM_MAP_ADDR", "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", "AFL_LLVM_LAF_TRANSFORM_COMPARES", "AFL_LLVM_MAP_ADDR",
"AFL_NGRAM_SIZE", "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", "AFL_NGRAM_SIZE",
"AFL_LLVM_SKIP_NEVERZERO", "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", "AFL_LLVM_SKIP_NEVERZERO",
"AFL_LLVM_LTO_DONTWRITEID", "AFL_NO_ARITH", "AFL_NO_BUILTIN", "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID",
"AFL_NO_CPU_RED", "AFL_NO_FORKSRV", "AFL_NO_UI", "AFL_NO_PYTHON", "AFL_NO_ARITH", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV",
"AFL_UNTRACER_FILE", "AFL_NO_UI", "AFL_NO_PYTHON", "AFL_UNTRACER_FILE",
"AFL_NO_X86", // not really an env but we dont want to warn on it "AFL_NO_X86", // not really an env but we dont want to warn on it
"AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_PATH", "AFL_PERFORMANCE_FILE", "AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_PATH", "AFL_PERFORMANCE_FILE",
//"AFL_PERSISTENT", // not implemented anymore, so warn additionally //"AFL_PERSISTENT", // not implemented anymore, so warn additionally

View File

@ -30,13 +30,37 @@
#include "cmplog.h" #include "cmplog.h"
#ifdef PROFILING
u64 time_spent_working = 0;
#endif
/* Execute target application, monitoring for timeouts. Return status /* Execute target application, monitoring for timeouts. Return status
information. The called program will update afl->fsrv->trace_bits. */ information. The called program will update afl->fsrv->trace_bits. */
fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv,
u32 timeout) { u32 timeout) {
#ifdef PROFILING
static u64 time_spent_start = 0;
struct timespec spec;
if (time_spent_start) {
u64 current;
clock_gettime(CLOCK_REALTIME, &spec);
current = (spec.tv_sec * 1000000000) + spec.tv_nsec;
time_spent_working += (current - time_spent_start);
}
#endif
fsrv_run_result_t res = afl_fsrv_run_target(fsrv, timeout, &afl->stop_soon); fsrv_run_result_t res = afl_fsrv_run_target(fsrv, timeout, &afl->stop_soon);
#ifdef PROFILING
clock_gettime(CLOCK_REALTIME, &spec);
time_spent_start = (spec.tv_sec * 1000000000) + spec.tv_nsec;
#endif
// TODO: Don't classify for faults? // TODO: Don't classify for faults?
classify_counts(fsrv); classify_counts(fsrv);
return res; return res;

View File

@ -27,6 +27,10 @@
#include "cmplog.h" #include "cmplog.h"
#include <limits.h> #include <limits.h>
#ifdef PROFILING
extern u64 time_spent_working;
#endif
static u8 *get_libradamsa_path(u8 *own_loc) { static u8 *get_libradamsa_path(u8 *own_loc) {
u8 *tmp, *cp, *rsl, *own_copy; u8 *tmp, *cp, *rsl, *own_copy;
@ -1351,6 +1355,13 @@ stop_fuzzing:
} }
#ifdef PROFILING
SAYF(cYEL "[!] " cRST
"Profiling information: %llu ms total work, %llu ns/run\n",
time_spent_working / 1000000,
time_spent_working / afl->fsrv.total_execs);
#endif
fclose(afl->fsrv.plot_file); fclose(afl->fsrv.plot_file);
destroy_queue(afl); destroy_queue(afl);
destroy_extras(afl); destroy_extras(afl);

View File

@ -1 +1 @@
d4cc77cce71c15bb3d1f552d703a77e2a17cf42d 94c1976