diff options
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rwxr-xr-x | afl-cmin | 13 | ||||
-rw-r--r-- | examples/custom_mutators/example.c | 8 | ||||
-rw-r--r-- | include/envs.h | 1 | ||||
-rw-r--r-- | llvm_mode/Makefile | 145 | ||||
-rw-r--r-- | llvm_mode/NOTES | 88 | ||||
-rw-r--r-- | llvm_mode/README.lto.md | 150 | ||||
-rw-r--r-- | llvm_mode/TODO | 10 | ||||
-rw-r--r-- | llvm_mode/afl-clang-fast.c | 138 | ||||
-rw-r--r-- | llvm_mode/afl-ld.c | 750 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-lto-instrumentation.so.cc | 424 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-lto-whitelist.so.cc | 266 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-rt.o.c | 2 | ||||
-rw-r--r-- | llvm_mode/compare-transform-pass.so.cc | 1 | ||||
-rw-r--r-- | llvm_mode/split-compares-pass.so.cc | 1 | ||||
-rw-r--r-- | llvm_mode/split-switches-pass.so.cc | 1 | ||||
-rw-r--r-- | qemu_mode/patches/afl-qemu-tcg-runtime-inl.h | 59 | ||||
-rw-r--r-- | qemu_mode/patches/i386-translate.diff | 22 | ||||
-rw-r--r-- | qemu_mode/patches/syscall.diff | 22 | ||||
-rw-r--r-- | qemu_mode/patches/tcg-runtime-head.diff | 5 | ||||
-rw-r--r-- | src/afl-common.c | 1 | ||||
-rw-r--r-- | src/afl-fuzz-run.c | 3 | ||||
-rw-r--r-- | src/afl-fuzz.c | 2 | ||||
-rw-r--r-- | src/afl-showmap.c | 8 | ||||
-rwxr-xr-x | test/test.sh | 76 |
26 files changed, 2125 insertions, 74 deletions
diff --git a/.travis.yml b/.travis.yml index fe81d19f..278f312c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,4 +50,4 @@ script: - if [ "$TRAVIS_OS_NAME" = "linux" -a "$TRAVIS_CPU_ARCH" = "amd64" ]; then make distrib ASAN_BUILD=1 ; fi - if [ "$TRAVIS_CPU_ARCH" = "arm64" ] ; then echo DEBUG ; find / -name llvm-config.h 2>/dev/null; apt-cache search clang | grep clang- ; apt-cache search llvm | grep llvm- ; dpkg -l | egrep 'clang|llvm'; echo DEBUG ; export LLVM_CONFIG=llvm-config-6.0 ; make ASAN_BUILD=1 ; cd qemu_mode && sh ./build_qemu_support.sh ; cd .. ; fi - make tests - - travis_terminate 0 +# - travis_terminate 0 diff --git a/Makefile b/Makefile index b64a6dfe..8f537e28 100644 --- a/Makefile +++ b/Makefile @@ -417,6 +417,7 @@ install: all $(MANPAGES) if [ -f afl-gcc-fast ]; then set e; install -m 755 afl-gcc-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-gcc-fast $${DESTDIR}$(BIN_PATH)/afl-g++-fast; install -m 755 afl-gcc-pass.so afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH); fi ifndef AFL_TRACE_PC 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 -a -f afl-ld ]; then set -e; install -m 755 afl-clang-lto $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 afl-ld $${DESTDIR}$(HELPER_PATH); ln -sf afl-ld $${DESTDIR}$(HELPER_PATH)/ld; install -m 755 afl-llvm-lto-instrumentation.so $${DESTDIR}$(HELPER_PATH); install -m 755 afl-llvm-lto-whitelist.so $${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 diff --git a/afl-cmin b/afl-cmin index 28d8c746..ea57015e 100755 --- a/afl-cmin +++ b/afl-cmin @@ -397,10 +397,19 @@ BEGIN { cur = 0; if (!stdin_file) { print " Processing "in_count" files (forkserver mode)..." - system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string) + retval = system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string) } else { print " Processing "in_count" files (forkserver mode)..." - system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null") + retval = system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null") + } + + if (retval) { + print "[!]Exit code != 0 received from afl-showmap, terminating..." + + if (!ENVIRON["AFL_KEEP_TRACES"]) { + system("rm -rf "trace_dir" 2>/dev/null") + } + exit retval } ####################################################### diff --git a/examples/custom_mutators/example.c b/examples/custom_mutators/example.c index 737cf631..5aaa6a5d 100644 --- a/examples/custom_mutators/example.c +++ b/examples/custom_mutators/example.c @@ -89,10 +89,10 @@ size_t afl_custom_pre_save(uint8_t *buf, size_t buf_size, uint8_t **out_buf) { } -static uint8_t *trim_buf; -static size_t trim_buf_size; -static int trimmming_steps; -static int cur_step; +uint8_t *trim_buf; +size_t trim_buf_size; +int trimmming_steps; +int cur_step; /** * This method is called at the start of each trimming operation and receives diff --git a/include/envs.h b/include/envs.h index 791887d7..fee74fd7 100644 --- a/include/envs.h +++ b/include/envs.h @@ -19,6 +19,7 @@ const char *afl_environment_variables[] = { "AFL_LLVM_LAF_SPLIT_COMPARES_BITW", "AFL_LLVM_LAF_SPLIT_FLOATS", "AFL_LLVM_LAF_SPLIT_SWITCHES", "AFL_LLVM_LAF_TRANSFORM_COMPARES", "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", "AFL_NO_AFFINITY", + "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID", "AFL_NO_ARITH", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", "AFL_NO_UI", "AFL_NO_X86", // not really an env but we dont want to warn on it diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile index ff20c516..a4c60ea4 100644 --- a/llvm_mode/Makefile +++ b/llvm_mode/Makefile @@ -43,9 +43,10 @@ LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//') LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) LLVM_STDCXX = gnu++11 LLVM_APPLE = $(shell clang -v 2>&1 | grep -iq apple && echo 1 || echo 0) +LLVM_LTO = 0 ifeq "$(LLVMVER)" "" - $(warning llvm_mode needs llvm-config, which was not found) + $(warning [!] llvm_mode needs llvm-config, which was not found) endif ifeq "$(LLVM_UNSUPPORTED)" "1" @@ -53,24 +54,89 @@ ifeq "$(LLVM_UNSUPPORTED)" "1" endif ifeq "$(LLVM_MAJOR)" "9" - $(info llvm_mode detected llvm 9, enabling neverZero implementation) + $(info [+] llvm_mode detected llvm 9, enabling neverZero implementation) + $(info [+] llvm_mode detected llvm 9, enabling afl-clang-lto LTO implementation) + LLVM_LTO = 1 endif ifeq "$(LLVM_NEW_API)" "1" - $(info llvm_mode detected llvm 10+, enabling neverZero implementation and c++14) + $(info [+] llvm_mode detected llvm 10+, enabling neverZero implementation and c++14) + $(info [+] llvm_mode detected llvm 9, enabling afl-clang-lto LTO implementation) LLVM_STDCXX = c++14 + LLVM_LTO = 1 +endif + +ifeq "$(LLVM_LTO)" "0" + $(info [+] llvm_mode detected llvm < 9, afl-clang-lto LTO will not be build.) endif ifeq "$(LLVM_APPLE)" "1" $(warning llvm_mode will not compile with Xcode clang...) endif +# We were using llvm-config --bindir to get the location of clang, but +# this seems to be busted on some distros, so using the one in $PATH is +# probably better. + +CC = $(LLVM_BINDIR)/clang +CXX = $(LLVM_BINDIR)/clang++ + +ifeq "$(shell test -e $(CC) || echo 1 )" "1" + # llvm-config --bindir may not providing a valid path, so ... + ifeq "$(shell test -e '$(BIN_DIR)/clang' && echo 1)" "1" + # we found one in the local install directory, lets use these + CC = $(BIN_DIR)/clang + CXX = $(BIN_DIR)/clang++ + else + # hope for the best + $(warning we have trouble finding clang/clang++ - llvm-config is not helping us) + CC = clang + CXX = clang++ + endif +endif + +# sanity check. +# Are versions of clang --version and llvm-config --version equal? +CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*version\ ([0-9]\.[0-9]\.[0-9]).*/s//\1/p') + +ifneq "$(CLANGVER)" "$(LLVMVER)" + CC = $(shell $(LLVM_CONFIG) --bindir)/clang + CXX = $(shell $(LLVM_CONFIG) --bindir)/clang++ +endif + +# After we set CC/CXX we can start makefile magic tests + +ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + CFLAGS_OPT = -march=native +endif + +ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_FLTO ?= -flto=full +else + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_FLTO ?= -flto=thin + else + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_FLTO ?= -flto + endif + endif +endif + +ifneq "$(AFL_CLANG_FLTO)" "" +ifeq "$(AFL_REAL_LD)" "" + AFL_REAL_LD = $(shell readlink /bin/ld 2>/dev/null) + ifeq "$(AFL_REAL_LD)" "" + AFL_REAL_LD = $(shell readlink /usr/bin/ld 2>/dev/null) + endif +endif +endif + CFLAGS ?= -O3 -funroll-loops override CFLAGS = -Wall \ -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I ../include/ \ -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ - -DLLVM_VERSION=\"$(LLVMVER)\" + -DLLVM_VERSION=\"$(LLVMVER)\" -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" ifdef AFL_TRACE_PC CFLAGS += -DUSE_TRACE_PC=1 endif @@ -92,32 +158,10 @@ ifeq "$(shell uname)" "OpenBSD" CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so endif -# We were using llvm-config --bindir to get the location of clang, but -# this seems to be busted on some distros, so using the one in $PATH is -# probably better. - -CC = $(LLVM_BINDIR)/clang -CXX = $(LLVM_BINDIR)/clang++ - -ifeq "$(shell test -e $(CC) || echo 1 )" "1" - # llvm-config --bindir is not providing a valid path, so ... - ifeq "$(shell test -e '$(BIN_DIR)/clang' && echo 1)" "1" - # we found one in the local install directory, lets use these - CC = $(BIN_DIR)/clang - CXX = $(BIN_DIR)/clang++ - else - # hope for the best - $(warning we have trouble finding clang/clang++ - llvm-config is not helping us) - CC = clang - CXX = clang++ - endif +ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -fuse-ld=`which ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + CFLAGS += -DAFL_CLANG_FUSELD=1 endif -# sanity check. -# Are versions of clang --version and llvm-config --version equal? -CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*version\ ([0-9]\.[0-9]\.[0-9]).*/s//\1/p') - - 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) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" SHMAT_OK=1 else @@ -133,16 +177,11 @@ ifeq "$(TEST_MMAP)" "1" endif ifndef AFL_TRACE_PC - PROGS = ../afl-clang-fast ../afl-llvm-pass.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-ld ../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 else PROGS = ../afl-clang-fast ../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 endif -ifneq "$(CLANGVER)" "$(LLVMVER)" - CC = $(shell $(LLVM_CONFIG) --bindir)/clang - CXX = $(shell $(LLVM_CONFIG) --bindir)/clang++ -endif - # If prerequisites are not given, warn, do not build anything, and exit with code 0 ifeq "$(LLVMVER)" "" NO_BUILD = 1 @@ -202,15 +241,41 @@ afl-common.o: ../src/afl-common.c $(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS) ../afl-clang-fast: afl-clang-fast.c afl-common.o | test_deps - $(CC) $(CFLAGS) $< afl-common.o -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" ln -sf afl-clang-fast ../afl-clang-fast++ +ifneq "$(AFL_CLANG_FLTO)" "" +ifeq "$(LLVM_LTO)" "1" + ln -sf afl-clang-fast ../afl-clang-lto + ln -sf afl-clang-fast ../afl-clang-lto++ +endif +endif +../afl-ld: afl-ld.c +ifneq "$(AFL_CLANG_FLTO)" "" +ifeq "$(LLVM_LTO)" "1" + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" + ln -sf afl-ld ../ld + @rm -f .test-instr + @-export AFL_QUIET=1 AFL_PATH=.. PATH="..:$(PATH)" ; ../afl-clang-lto -Wl,--afl -o .test-instr ../test-instr.c && echo "[+] afl-clang-lto and afl-ld seem to work fine :)" || echo "[!] WARNING: clang seems to have a hardcoded "'/bin/ld'" - check README.lto" + @rm -f .test-instr +endif +endif ../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc | test_deps -$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) ../afl-llvm-pass.so: afl-llvm-pass.so.cc | test_deps $(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) +../afl-llvm-lto-whitelist.so: afl-llvm-lto-whitelist.so.cc +ifeq "$(LLVM_LTO)" "1" + $(CXX) $(CLANG_CFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) +endif + +../afl-llvm-lto-instrumentation.so: afl-llvm-lto-instrumentation.so.cc MarkNodes.cc +ifeq "$(LLVM_LTO)" "1" + $(CXX) $(CLANG_CFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) +endif + # laf ../split-switches-pass.so: split-switches-pass.so.cc | test_deps $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) @@ -271,7 +336,13 @@ vpath % .. @echo .SH LICENSE >> ../$@ @echo Apache License Version 2.0, January 2004 >> ../$@ ln -sf afl-clang-fast.8 ../afl-clang-fast++.8 +ifneq "$(AFL_CLANG_FLTO)" "" +ifeq "$(LLVM_LTO)" "0" + ln -sf afl-clang-fast.8 ../afl-clang-lto.8 + ln -sf afl-clang-fast.8 ../afl-clang-lto++.8 +endif +endif clean: - rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1 afl-llvm-pass.dwo - rm -f $(PROGS) afl-common.o ../afl-clang-fast++ ../afl-clang-fast*.8 + rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1 *.dwo + rm -f $(PROGS) afl-common.o ../afl-clang-fast++ ../afl-clang-lto ../afl-clang-lto++ ../afl-clang*.8 diff --git a/llvm_mode/NOTES b/llvm_mode/NOTES new file mode 100644 index 00000000..9aee7f46 --- /dev/null +++ b/llvm_mode/NOTES @@ -0,0 +1,88 @@ + +markNodes + -> + +whitelist: + set meta information/context to functions? ask llvm-dev + setAttribute/hasAttribute? + +afl-ld: + handle(=instrument) .a archives on the cmdline + +afl-pass-lto-instrument.so: + either a or b: + a) use instrim + b) start in main() or _init() and first otherwise (warn!) + keep list of done functions + final: go through function list and instrument those missing + + + +--------------------------- + + + +for (auto &module : Ctx.getModules()) { + auto &functionList = module->getModule()->getFunctionList(); + for (auto &function : functionList) { + for (auto &bb : function) { + for (auto &instruction : bb) { + if (CallInst *callInst = dyn_cast<CallInst>(&instruction)) { + if (Function *calledFunction = callInst->getCalledFunction()) { + if (calledFunction->getName().startswith("llvm.dbg.declare")) { + + +for (auto &U : F.getUsers()) { <- unbekannt + if (auto CS = CallSite(U)) { + if (CS->getCalledFunction() == F) + +getCalledValue()->stripPointerCasts() + -> for indirect calls + + +CallGraph(M) + + + +#include "llvm/IR/CallSite.h" + +unsigned int indirect_call_cnt = 0; + + printf("Function: %s\n", F.getName().str().c_str()); + int cnt=0; + for (auto *U : F.users()) { +// auto *I = dyn_cast<Instruction>(U); +// if (I) { +// if (cast<CallInst>(I)->getCalledFunction()->getName() == F.getName()) { +// printf("DIRECT CALL %s->%s->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), cast<CallInst>(I)->getCalledFunction()->getName().str().c_str(), F.getName().str().c_str()); +// } +printf("Callsite #%d\n", ++cnt); + CallSite CS(U); + auto *I = CS.getInstruction(); + if (I) { + Value *called = CS.getCalledValue()->stripPointerCasts(); + Function* f = dyn_cast<Function>(called); + if (f->getName().size() > 0) { + printf("test %s->%s->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), f->getName().str().c_str(), F.getName().str().c_str()); + if (f->getName() == F.getName()) { + printf("CALL %s->%s->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), f->getName().str().c_str(), F.getName().str().c_str()); + } + } else + printf("FOO %s->...->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), F.getName().str().c_str()); + if (cast<CallInst>(I)->getCalledFunction()->getName() == F.getName()) { + printf("DIRECT %s->%s->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), cast<CallInst>(I)->getCalledFunction()->getName().str().c_str(), F.getName().str().c_str()); + } + } else { + printf("WE MISSED SOMETHING HERE!!\n"); + indirect_call_cnt++; + } + } + +oder: + for (auto *U : F.users()) { + if (auto CS = CallSite(U->getUser())) { + if (CS->isCallee(&U)) { + // foo + } + } + } diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md new file mode 100644 index 00000000..b7392adb --- /dev/null +++ b/llvm_mode/README.lto.md @@ -0,0 +1,150 @@ +# afl-clang-lto - collision free instrumentation at link time + +## TLDR; + +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 + +2. You can use it together with llvm_mode: laf-intel and whitelisting + features and can be combined with cmplog/Redqueen + +3. It only works with llvm 9 (and likely 10+ but is not tested there yet) + +## Introduction and problem description + +A big issue with how afl/afl++ works is that the basic block IDs that are +set during compilation are random - and hence natually the larger the number +of instrumented locations, the higher the number of edge collisions in the +map. This can result in not discovering new paths and therefore degrade the +efficiency of the fuzzing. + +*This issue is understimated in the fuzzing community!* +With a 2^16 = 64kb standard map at already 256 instrumented blocks there is +on average one collision. On average a target has 10.000 to 50.000 +instrumented blocks hence the real collisions are between 750-18.000! + +To get to a solution that prevents any collision took several approaches +and many dead ends until we got to this: + + * We instrument at link time when we have all files pre-compiled + * To instrument at link time we compile in LTO (link time optimization) mode + * Our compiler (afl-clang-lto/afl-clang-lto++) takes care of setting the + correct LTO options and runs our own afl-ld linker instead of the system + linker + * Our linker collects all LTO files to link and instruments them so that + we have non-colliding edge overage + * We use a new (for afl) edge coverage - which is the same as in llvm + -fsanitize=coverage edge coverage mode :) + * after inserting our instrumentation in all interesting edges we link + all parts of the program together to our executable + +The result: + * 10-15% speed gain compared to llvm_mode + * guaranteed non-colliding edge coverage :-) + * The compile time especially for libraries can be longer + +Example build output from a libtiff build: +``` +/bin/bash ../libtool --tag=CC --mode=link afl-clang-lto -g -O2 -Wall -W -o thumbnail thumbnail.o ../libtiff/libtiff.la ../port/libport.la -llzma -ljbig -ljpeg -lz -lm +libtool: link: afl-clang-lto -g -O2 -Wall -W -o thumbnail thumbnail.o ../libtiff/.libs/libtiff.a ../port/.libs/libport.a -llzma -ljbig -ljpeg -lz -lm +afl-clang-lto++2.62d by Marc "vanHauser" Heuse <mh@mh-sec.de> +afl-ld++2.62d by Marc "vanHauser" Heuse <mh@mh-sec.de> (level 0) +[+] Running ar unpacker on /prg/tests/lto/tiff-4.0.4/tools/../libtiff/.libs/libtiff.a into /tmp/.afl-3914343-1583339800.dir +[+] Running ar unpacker on /prg/tests/lto/tiff-4.0.4/tools/../port/.libs/libport.a into /tmp/.afl-3914343-1583339800.dir +[+] Running bitcode linker, creating /tmp/.afl-3914343-1583339800-1.ll +[+] Performing optimization via opt, creating /tmp/.afl-3914343-1583339800-2.bc +[+] Performing instrumentation via opt, creating /tmp/.afl-3914343-1583339800-3.bc +afl-llvm-lto++2.62d by Marc "vanHauser" Heuse <mh@mh-sec.de> +[+] Instrumented 15833 locations with no collisions (on average 1767 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode). +[+] Running real linker /bin/x86_64-linux-gnu-ld +[+] Linker was successful +``` + +## How to use afl-clang-lto + +Just use afl-clang-lto like you did afl-clang-fast or afl-gcc. + +Also whitelisting (AFL_LLVM_WHITELIST -> [README.whitelist.md](README.whitelist.md)) and +laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. +Instrim does not - but we can not really use it anyway for our approach. + +Example: +``` +CC=afl-clang-lto CXX=afl-clang-lto++ ./configure +make +``` + +## Potential issues + +### compiling libraries fails + +If you see this message: +``` +/bin/ld: libfoo.a: error adding symbols: archive has no index; run ranlib to add one +``` +This is because usually gnu gcc ranlib is being called which cannot deal with clang LTO files. +The solution is simple: when you ./configure you have also have to set RANLIB=llvm-ranlib and AR=llvm-ar + +Solution: +``` +AR=llvm-ar RANLIB=llvm-ranlib CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --disable-shared +``` +and on some target you have to to AR=/RANLIB= even for make as the configure script does not save it ... + +### clang is hardcoded to /bin/ld + +Some clang packages have 'ld' hardcoded to /bin/ld. This is an issue as this +prevents "our" afl-ld being called. + +-fuse-ld=/path/to/afl-ld should be set through makefile magic in llvm_mode - +if it is supported - however if this fails you can try: +``` +LDFLAGS=-fuse-ld=</path/to/afl-ld +``` + +As workaround attempt #2 you will have to switch /bin/ld: +``` + mv /bin/ld /bin/ld.orig + cp afl-ld /bin/ld +``` +This can result in two problems though: + + !1! + When compiling afl-ld, the build process looks at where the /bin/ld link + is going to. So when the workaround was applied and a recompiling afl-ld + is performed then the link is gone and the new afl-ld clueless where + the real ld is. + In this case set AFL_REAL_LD=/bin/ld.orig + + !2! + When you install an updated gcc/clang/... package, your OS might restore + the ld link. + +### compiling programs still fail + +afl-clang-lto is still work in progress. +Complex targets are still likely not to compile and this needs to be fixed. +Please report issues at: +[https://github.com/vanhauser-thc/AFLplusplus/issues/226](https://github.com/vanhauser-thc/AFLplusplus/issues/226) + +Known issues: +* ffmpeg +* bogofilter +* libjpeg-turbo-1.3.1 + +## Upcoming Work + +1. Currently the LTO whitelist feature does not allow to not instrument main, start and init functions +2. Modify the forkserver + afl-fuzz so that only the necessary map size is + loaded and used - and communicated to afl-fuzz too. + Result: faster fork in the target and faster map analysis in afl-fuzz + => more speed :-) + +## Tested and working targets + +* libpng-1.2.53 +* libxml2-2.9.2 +* tiff-4.0.4 +* unrar-nonfree-5.6.6 +* exiv 0.27 +* jpeg-6b diff --git a/llvm_mode/TODO b/llvm_mode/TODO new file mode 100644 index 00000000..2729d688 --- /dev/null +++ b/llvm_mode/TODO @@ -0,0 +1,10 @@ +TODO for afl-ld: +* handle libfoo.a object archives + +TODO for afl-llvm-lto-instrumentation: +* better algo for putting stuff in the map? +* try to predict how long the instrumentation process will take + +TODO for afl-llvm-lto-whitelist +* different solution then renaming? + diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index fa3b5c78..4c4f802d 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -41,10 +41,23 @@ static u8* obj_path; /* Path to runtime libraries */ static u8** cc_params; /* Parameters passed to the real CC */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */ static u8 llvm_fullpath[PATH_MAX]; +static u8 lto_mode; +static u8* lto_flag = AFL_CLANG_FLTO; +static u8* march_opt = CFLAGS_OPT; +static u8 debug; +static u8 cwd[4096]; static u8 cmplog_mode; u8 use_stdin = 0; /* dummy */ u8 be_quiet = 0; +u8* getthecwd() { + + static u8 fail[] = ""; + if (getcwd(cwd, sizeof(cwd)) == NULL) return fail; + return cwd; + +} + /* Try to find the runtime libraries. If that fails, abort. */ static void find_obj(u8* argv0) { @@ -138,7 +151,22 @@ static void edit_params(u32 argc, char** argv) { has_llvm_config = (strlen(LLVM_BINDIR) > 0); - if (!strcmp(name, "afl-clang-fast++")) { + if (!strncmp(name, "afl-clang-lto", strlen("afl-clang-lto"))) { + +#ifdef USE_TRACE_PC + FATAL("afl-clang-lto does not work with TRACE_PC mode"); +#endif + if (lto_flag[0] != '-') + FATAL( + "afl-clang-lto not possible because Makefile magic did not identify " + "the correct -flto flag"); + if (getenv("AFL_LLVM_INSTRIM") != NULL) + FATAL("afl-clang-lto does not work with InsTrim mode"); + lto_mode = 1; + + } + + if (!strcmp(name, "afl-clang-fast++") || !strcmp(name, "afl-clang-lto++")) { u8* alt_cxx = getenv("AFL_CXX"); if (has_llvm_config) @@ -200,6 +228,8 @@ static void edit_params(u32 argc, char** argv) { // /laf + unsetenv("AFL_LD"); + unsetenv("AFL_LD_CALLER"); if (cmplog_mode) { cc_params[cc_par_cnt++] = "-Xclang"; @@ -234,6 +264,36 @@ static void edit_params(u32 argc, char** argv) { // "-fsanitize-coverage=trace-cmp,trace-div,trace-gep"; // cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; #else + + if (lto_mode) { + + char* old_path = getenv("PATH"); + char* new_path = alloc_printf("%s:%s", AFL_PATH, old_path); + + setenv("PATH", new_path, 1); + setenv("AFL_LD", "1", 1); + + if (getenv("AFL_LLVM_WHITELIST") != NULL) { + + 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-lto-whitelist.so", obj_path); + + } + +#ifdef AFL_CLANG_FUSELD + cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s/afl-ld", AFL_PATH); +#endif + + cc_params[cc_par_cnt++] = "-B"; + cc_params[cc_par_cnt++] = AFL_PATH; + + cc_params[cc_par_cnt++] = lto_flag; + + } else + if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { @@ -343,6 +403,8 @@ static void edit_params(u32 argc, char** argv) { cc_params[cc_par_cnt++] = "-g"; cc_params[cc_par_cnt++] = "-O3"; cc_params[cc_par_cnt++] = "-funroll-loops"; + if (strlen(march_opt) > 1 && march_opt[0] == '-') + cc_params[cc_par_cnt++] = march_opt; } @@ -461,21 +523,40 @@ static void edit_params(u32 argc, char** argv) { int main(int argc, char** argv, char** envp) { + int i; + char* callname = "afl-clang-fast"; + + if (getenv("AFL_DEBUG")) { + + debug = 1; + if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG"); + + } + + if (strstr(argv[0], "afl-clang-lto") == NULL) callname = "afl-clang-lto"; + if (argc < 2 || strcmp(argv[1], "-h") == 0) { #ifdef USE_TRACE_PC - printf( - cCYA - "afl-clang-fast" VERSION cRST - " [tpcg] by <lszekeres@google.com>\n" + printf(cCYA "afl-clang-fast" VERSION cRST + " [tpcg] by <lszekeres@google.com>\n") #else - printf( - cCYA - "afl-clang-fast" VERSION cRST - " by <lszekeres@google.com>\n" + if (strstr(argv[0], "afl-clang-lto") == NULL) + + printf(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n"); + + else { + + printf(cCYA "afl-clang-lto" VERSION cRST + " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); + + } + #endif /* ^USE_TRACE_PC */ + + SAYF( "\n" - "afl-clang-fast[++] [options]\n" + "%s[++] [options]\n" "\n" "This is a helper application for afl-fuzz. It serves as a drop-in " "replacement\n" @@ -521,7 +602,7 @@ int main(int argc, char** argv, char** envp) { "AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n" "\nafl-clang-fast was built for llvm %s with the llvm binary path of " "\"%s\".\n\n", - BIN_PATH, BIN_PATH, LLVM_VERSION, LLVM_BINDIR); + callname, BIN_PATH, BIN_PATH, LLVM_VERSION, LLVM_BINDIR); exit(1); @@ -535,11 +616,28 @@ int main(int argc, char** argv, char** envp) { #warning \ "You do not need to specifically compile with USE_TRACE_PC anymore, setting the environment variable AFL_LLVM_USE_TRACE_PC is enough." #else - SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n"); + if (strstr(argv[0], "afl-clang-lto") == NULL) + + SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n"); + + else + + SAYF(cCYA "afl-clang-lto" VERSION cRST + " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); + #endif /* ^USE_TRACE_PC */ } + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < argc; i++) + SAYF(" \"%s\"", argv[i]); + SAYF("\n"); + + } + check_environment_vars(envp); cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG"); @@ -551,13 +649,14 @@ int main(int argc, char** argv, char** envp) { edit_params(argc, argv); - /* - int i = 0; - printf("EXEC:"); - while (cc_params[i] != NULL) - printf(" %s", cc_params[i++]); - printf("\n"); - */ + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < cc_par_cnt; i++) + SAYF(" \"%s\"", cc_params[i]); + SAYF("\n"); + + } execvp(cc_params[0], (char**)cc_params); @@ -566,4 +665,3 @@ int main(int argc, char** argv, char** envp) { return 0; } - diff --git a/llvm_mode/afl-ld.c b/llvm_mode/afl-ld.c new file mode 100644 index 00000000..65a75879 --- /dev/null +++ b/llvm_mode/afl-ld.c @@ -0,0 +1,750 @@ +/* + american fuzzy lop++ - wrapper for GNU ld + ----------------------------------------- + + Written by Marc Heuse <mh@mh-sec.de> for afl++ + + Maintained by Marc Heuse <mh@mh-sec.de>, + Heiko Eißfeldt <heiko.eissfeldt@hexco.de> + Andrea Fioraldi <andreafioraldi@gmail.com> + Dominik Maier <domenukk@gmail.com> + + 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 + + The sole purpose of this wrapper is to preprocess clang LTO files before + linking by ld and perform the instrumentation on the whole program. + +*/ + +#define AFL_MAIN + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <fcntl.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> + +#include <dirent.h> + +static u8 **ld_params, /* Parameters passed to the real 'ld' */ + **link_params, /* Parameters passed to 'llvm-link' */ + **opt_params, /* Parameters passed to 'opt' opt */ + **inst_params; /* Parameters passed to 'opt' inst */ + +static u8* input_file; /* Originally specified input file */ +static u8 *final_file, /* Instrumented file for the real 'ld' */ + *linked_file, /* file where we link all files */ + *modified_file; /* file that was optimized before instr */ +static u8* afl_path = AFL_PATH; +static u8* real_ld = AFL_REAL_LD; +static u8 cwd[4096]; +static u8* tmp_dir; +static u8* ar_dir; +static u8 ar_dir_cnt; + +static u8 be_quiet, /* Quiet mode (no stderr output) */ + debug, /* AFL_DEBUG */ + passthrough, /* AFL_LD_PASSTHROUGH - no link+optimize*/ + we_link, /* we have bc/ll -> link + optimize */ + just_version; /* Just show version? */ + +static u32 ld_param_cnt = 1, /* Number of params to 'ld' */ + link_param_cnt = 1, /* Number of params to 'llvm-link' */ + opt_param_cnt = 1, /* Number of params to 'opt' opt */ + inst_param_cnt = 1; /* Number of params to 'opt' instr */ + +/* This function wipes a directory - our AR unpack directory in this case */ +static u8 wipe_directory(u8* path) { + + DIR* d; + struct dirent* d_ent; + + d = opendir(path); + + if (!d) return 0; + + while ((d_ent = readdir(d))) { + + if (strcmp(d_ent->d_name, ".") != 0 && strcmp(d_ent->d_name, "..") != 0) { + + u8* fname = alloc_printf("%s/%s", path, d_ent->d_name); + if (unlink(fname)) PFATAL("Unable to delete '%s'", fname); + ck_free(fname); + + } + + } + + closedir(d); + + return !!rmdir(path); + +} + +/* remove temporary files on fatal errors */ +static void at_exit_handler(void) { + + if (!getenv("AFL_KEEP_ASSEMBLY")) { + + if (linked_file) { + + unlink(linked_file); + linked_file = NULL; + + } + + if (modified_file) { + + unlink(modified_file); + modified_file = NULL; + + } + + if (final_file) { + + unlink(final_file); + final_file = NULL; + + } + + if (ar_dir != NULL) { + + wipe_directory(ar_dir); + ar_dir = NULL; + + } + + } + +} + +/* This function checks if the parameter is a) an existing file and b) + if it is a BC or LL file, if both are true it returns 1 and 0 otherwise */ +int is_llvm_file(const char* file) { + + int fd; + u8 buf[5]; + + if ((fd = open(file, O_RDONLY)) < 0) return 0; + + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) return 0; + buf[sizeof(buf) - 1] = 0; + + close(fd); + + if (strncmp(buf, "; Mo", 4) == 0) return 1; + if (buf[0] == 'B' && buf[1] == 'C' && buf[2] == 0xC0 && buf[3] == 0xDE) + return 1; + + return 0; + +} + +/* Return the current working directory, not thread safe ;-) */ +u8* getthecwd() { + + static u8 fail[] = ""; + if (getcwd(cwd, sizeof(cwd)) == NULL) return fail; + return cwd; + +} + +/* Check if an ar extracted file is already in the parameter list */ +int is_duplicate(u8** params, u32 ld_param_cnt, u8* ar_file) { + + for (uint32_t i = 0; i < ld_param_cnt; i++) + if (params[i] != NULL) + if (strcmp(params[i], ar_file) == 0) return 1; + + return 0; + +} + +/* Examine and modify parameters to pass to 'ld', 'llvm-link' and 'llmv-ar'. + Note that the file name is always the last parameter passed by GCC, + so we exploit this property to keep the code "simple". */ +static void edit_params(int argc, char** argv) { + + u32 i, have_lto = 0; + + if (tmp_dir == NULL) { + + tmp_dir = getenv("TMPDIR"); + if (!tmp_dir) tmp_dir = getenv("TEMP"); + if (!tmp_dir) tmp_dir = getenv("TMP"); + if (!tmp_dir) tmp_dir = "/tmp"; + + } + + linked_file = + alloc_printf("%s/.afl-%u-%u-1.ll", tmp_dir, getpid(), (u32)time(NULL)); + modified_file = + alloc_printf("%s/.afl-%u-%u-2.bc", tmp_dir, getpid(), (u32)time(NULL)); + final_file = + alloc_printf("%s/.afl-%u-%u-3.bc", tmp_dir, getpid(), (u32)time(NULL)); + + ld_params = ck_alloc((argc + 128) * sizeof(u8*)); + link_params = ck_alloc((argc + 256) * sizeof(u8*)); + inst_params = ck_alloc(12 * sizeof(u8*)); + opt_params = ck_alloc(12 * sizeof(u8*)); + + ld_params[0] = (u8*)real_ld; + ld_params[argc] = 0; + + link_params[0] = alloc_printf("%s/%s", LLVM_BINDIR, "llvm-link"); + link_params[link_param_cnt++] = "-S"; // we create the linked file as .ll + link_params[link_param_cnt++] = "-o"; + link_params[link_param_cnt++] = linked_file; + + opt_params[0] = alloc_printf("%s/%s", LLVM_BINDIR, "opt"); + if (getenv("AFL_DONT_OPTIMIZE") == NULL) + opt_params[opt_param_cnt++] = "-O3"; + else + opt_params[opt_param_cnt++] = "-O0"; + // opt_params[opt_param_cnt++] = "-S"; // only when debugging + opt_params[opt_param_cnt++] = linked_file; // input: .ll file + opt_params[opt_param_cnt++] = "-o"; + opt_params[opt_param_cnt++] = modified_file; // output: .bc file + + inst_params[0] = alloc_printf("%s/%s", LLVM_BINDIR, "opt"); + inst_params[inst_param_cnt++] = + alloc_printf("--load=%s/afl-llvm-lto-instrumentation.so", afl_path); + // inst_params[inst_param_cnt++] = "-S"; // only when debugging + inst_params[inst_param_cnt++] = "--disable-opt"; + inst_params[inst_param_cnt++] = "--afl-lto"; + inst_params[inst_param_cnt++] = modified_file; // input: .bc file + inst_params[inst_param_cnt++] = "-o"; + inst_params[inst_param_cnt++] = final_file; // output: .bc file + + for (i = 1; i < argc; i++) { + + if (strncmp(argv[i], "-flto", 5) == 0) have_lto = 1; + + if (!strcmp(argv[i], "-version")) { + + just_version = 1; + ld_params[1] = argv[i]; + ld_params[2] = NULL; + final_file = input_file; + return; + + } + + if (strcmp(argv[i], "--afl") == 0) { + + if (!be_quiet) OKF("afl++ test command line flag detected, exiting."); + exit(0); + + } + + // is the parameter an .a AR archive? If so, unpack and check its files + if (argv[i][0] != '-' && strlen(argv[i]) > 2 && + argv[i][strlen(argv[i]) - 1] == 'a' && + argv[i][strlen(argv[i]) - 2] == '.') { + + // This gets a bit odd. I encountered several .a files being linked and + // where the same "foo.o" was in both .a archives. llvm-link does not + // like this so we have to work around that ... + + u8 this_wd[4096], *this_ar; + u8 ar_params_cnt = 4; + u8* ar_params[ar_params_cnt]; + s32 pid, status; + DIR* arx; + struct dirent* dir_ent; + + if (ar_dir_cnt == 0) { // first archive, we setup up the basics + + ar_dir = alloc_printf("%s/.afl-%u-%u.dir", tmp_dir, getpid(), + (u32)time(NULL)); + if (mkdir(ar_dir, 0700) != 0) + FATAL("can not create temporary directory %s", ar_dir); + + } + + if (getcwd(this_wd, sizeof(this_wd)) == NULL) + FATAL("can not get the current working directory"); + if (chdir(ar_dir) != 0) + FATAL("can not chdir to temporary directory %s", ar_dir); + if (argv[i][0] == '/') + this_ar = argv[i]; + else + this_ar = alloc_printf("%s/%s", this_wd, argv[i]); + ar_params[0] = alloc_printf("%s/%s", LLVM_BINDIR, "llvm-ar"); + ar_params[1] = "x"; + ar_params[2] = this_ar; + ar_params[3] = NULL; + + if (!be_quiet) OKF("Running ar unpacker on %s into %s", this_ar, ar_dir); + + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (uint32_t j = 0; j < ar_params_cnt; j++) + SAYF(" \"%s\"", ar_params[j]); + SAYF("\n"); + + } + + if (!(pid = fork())) { + + execvp(ar_params[0], (char**)ar_params); + FATAL("Oops, failed to execute '%s'", ar_params[0]); + + } + + if (pid < 0) FATAL("fork() failed"); + if (waitpid(pid, &status, 0) <= 0) FATAL("waitpid() failed"); + if (WEXITSTATUS(status) != 0) exit(WEXITSTATUS(status)); + + if (chdir(this_wd) != 0) + FATAL("can not chdir back to our working directory %s", this_wd); + + if (!(arx = opendir(ar_dir))) FATAL("can not open directory %s", ar_dir); + + while ((dir_ent = readdir(arx)) != NULL) { + + u8* ar_file = alloc_printf("%s/%s", ar_dir, dir_ent->d_name); + + if (dir_ent->d_name[strlen(dir_ent->d_name) - 1] == 'o' && + dir_ent->d_name[strlen(dir_ent->d_name) - 2] == '.') { + + if (passthrough || argv[i][0] == '-' || is_llvm_file(ar_file) == 0) { + + if (is_duplicate(ld_params, ld_param_cnt, ar_file) == 0) { + + ld_params[ld_param_cnt++] = ar_file; + if (debug) + SAYF(cMGN "[D] " cRST "not a LTO link file: %s\n", ar_file); + + } + + } else { + + if (is_duplicate(link_params, link_param_cnt, ar_file) == 0) { + + if (we_link == 0) { // we have to honor order ... + + ld_params[ld_param_cnt++] = final_file; + we_link = 1; + + } + + link_params[link_param_cnt++] = ar_file; + if (debug) SAYF(cMGN "[D] " cRST "is a link file: %s\n", ar_file); + + } + + } + + } else + + if (dir_ent->d_name[0] != '.') + WARNF("Unusual file found in ar archive %s: %s", argv[i], ar_file); + + } + + closedir(arx); + ar_dir_cnt++; + + continue; + + } + + if (passthrough || argv[i][0] == '-' || is_llvm_file(argv[i]) == 0) { + + // -O3 fucks up the CFG and instrumentation, so we downgrade to O2 + // which is as we want things. Lets hope this is not too different + // in the various llvm versions! + if (strncmp(argv[i], "-plugin-opt=O", 13) == 0 && + !getenv("AFL_DONT_OPTIMIZE")) + ld_params[ld_param_cnt++] = "-plugin-opt=O2"; + else + ld_params[ld_param_cnt++] = argv[i]; + + } else { + + if (we_link == 0) { // we have to honor order ... + ld_params[ld_param_cnt++] = final_file; + we_link = 1; + + } + + link_params[link_param_cnt++] = argv[i]; + + } + + } + + // if (have_lto == 0) ld_params[ld_param_cnt++] = AFL_CLANG_FLTO; // maybe we + // should not ... + ld_params[ld_param_cnt] = NULL; + link_params[link_param_cnt] = NULL; + opt_params[opt_param_cnt] = NULL; + inst_params[inst_param_cnt] = NULL; + +} + +/* clean AFL_PATH from PATH */ + +void clean_path() { + + char *tmp, *newpath = NULL, *path = getenv("PATH"); + u8 done = 0; + + if (debug) + SAYF(cMGN "[D]" cRST " old PATH=%s, AFL_PATH=%s\n", path, AFL_PATH); + + // wipe AFL paths from PATH that we set + // we added two paths so we remove the two paths + while (!done) { + + if (*path == 0) + done = 1; + else if (*path++ == ':') + done = 1; + + } + + while (*path == ':') + path++; + + // AFL_PATH could be additionally in PATH so check and remove to not call our + // 'ld' + const size_t pathlen = strlen(path); + const size_t afl_pathlen = strlen(AFL_PATH); + newpath = malloc(pathlen + 1); + if (strcmp(AFL_PATH, "/bin") != 0 && strcmp(AFL_PATH, "/usr/bin") != 0 && + afl_pathlen > 1 && (tmp = strstr(path, AFL_PATH)) != NULL && // it exists + (tmp == path || + (tmp > path && + tmp[-1] == ':')) && // either starts with it or has a colon before + (tmp + afl_pathlen == path + pathlen || + (tmp + afl_pathlen < + path + (pathlen && tmp[afl_pathlen] == + ':')) // end with it or has a colon at the end + )) { + + int one_colon = 1; + + if (tmp > path) { + + memcpy(newpath, path, tmp - path); + newpath[tmp - path - 1] = 0; // remove ':' + one_colon = 0; + + } + + if (tmp + afl_pathlen < path + pathlen) tmp += afl_pathlen + one_colon; + + setenv("PATH", newpath, 1); + + } else + + setenv("PATH", path, 1); + + if (debug) SAYF(cMGN "[D]" cRST " new PATH=%s\n", getenv("PATH")); + free(newpath); + +} + +/* Main entry point */ + +int main(int argc, char** argv) { + + s32 pid, i; + int status; + u8 *ptr, exe[4096], exe2[4096], proc[32], val[2] = " "; + int have_afl_ld_caller = 0; + + if (isatty(2) && !getenv("AFL_QUIET") && !getenv("AFL_DEBUG")) { + + if (getenv("AFL_LD") != NULL) + SAYF(cCYA "afl-ld" VERSION cRST + " by Marc \"vanHauser\" Heuse <mh@mh-sec.de> (level %d)\n", + have_afl_ld_caller); + + } else + + be_quiet = 1; + + if (getenv("AFL_DEBUG") != NULL) debug = 1; + if (getenv("AFL_PATH") != NULL) afl_path = getenv("AFL_PATH"); + if (getenv("AFL_LD_PASSTHROUGH") != NULL) passthrough = 1; + if (getenv("AFL_REAL_LD") != NULL) real_ld = getenv("AFL_REAL_LD"); + if (real_ld == NULL || strlen(real_ld) < 2) real_ld = "/bin/ld"; + if (real_ld != NULL && real_ld[0] != '/') + real_ld = alloc_printf("/bin/%s", real_ld); + + if ((ptr = getenv("AFL_LD_CALLER")) != NULL) have_afl_ld_caller = atoi(ptr); + val[0] = 0x31 + have_afl_ld_caller; + setenv("AFL_LD_CALLER", val, 1); + + if (debug) { + + SAYF(cMGN "[D] " cRST + "AFL_LD=%s, set AFL_LD_CALLER=%s, have_afl_ld_caller=%d, " + "real_ld=%s\n", + getenv("AFL_LD"), val, have_afl_ld_caller, real_ld); + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < argc; i++) + SAYF(" \"%s\"", argv[i]); + SAYF("\n"); + + } + + sprintf(proc, "/proc/%d/exe", getpid()); + if (readlink(proc, exe, sizeof(exe) - 1) > 0) { + + if (readlink(real_ld, exe2, sizeof(exe2) - 1) < 1) exe2[0] = 0; + exe[sizeof(exe) - 1] = 0; + exe[sizeof(exe2) - 1] = 0; + if (strcmp(exe, real_ld) == 0 || strcmp(exe, exe2) == 0) + PFATAL(cLRD "[!] " cRST + "Error: real 'ld' path points to afl-ld, set AFL_REAL_LD to " + "the real 'ld' program!"); + + } + + if (have_afl_ld_caller > 1) + PFATAL(cLRD "[!] " cRST + "Error: afl-ld calls itself in a loop, set AFL_REAL_LD to the " + "real 'ld' program!"); + + if (argc < 2) { + + SAYF( + "\n" + "This is a helper application for afl-fuzz. It is a wrapper around GNU " + "'ld',\n" + "executed by the toolchain whenever using " + "afl-clang-lto/afl-clang-lto++.\n" + "You probably don't want to run this program directly.\n\n" + + "Environment variables:\n" + " AFL_LD_PASSTHROUGH do not link+optimize == no instrumentation\n" + " AFL_REAL_LD point to the real ld if necessary\n" + + "\nafl-ld was compiled with the fixed real 'ld' path of %s and the " + "clang " + "bin path of %s\n\n", + real_ld, LLVM_BINDIR); + + exit(1); + + } + + if (getenv("AFL_LD") == NULL) { + + /* if someone install clang/ld into the same directory as afl++ then + they are out of luck ... */ + + if (have_afl_ld_caller == 1) { clean_path(); } + + if (real_ld != NULL && strlen(real_ld) > 1) execvp(real_ld, argv); + execvp("ld", argv); // fallback + PFATAL("Oops, failed to execute 'ld' - check your PATH"); + + } + + atexit(at_exit_handler); // ensure to wipe temp files if things fail + + edit_params(argc, argv); // here most of the magic happens :-) + + if (!just_version) { + + if (we_link == 0) { + + if (!getenv("AFL_QUIET")) + WARNF("No LTO input file found, cannot instrument!"); + + } else { + + /* first we link all files */ + if (!be_quiet) OKF("Running bitcode linker, creating %s", linked_file); + + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < link_param_cnt; i++) + SAYF(" \"%s\"", link_params[i]); + SAYF("\n"); + + } + + if (!(pid = fork())) { + + execvp(link_params[0], (char**)link_params); + FATAL("Oops, failed to execute '%s'", link_params[0]); + + } + + if (pid < 0) PFATAL("fork() failed"); + if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + if (WEXITSTATUS(status) != 0) exit(WEXITSTATUS(status)); + + /* then we perform an optimization on the collected objects files */ + if (!be_quiet) + OKF("Performing optimization via opt, creating %s", modified_file); + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < opt_param_cnt; i++) + SAYF(" \"%s\"", opt_params[i]); + SAYF("\n"); + + } + + if (!(pid = fork())) { + + execvp(opt_params[0], (char**)opt_params); + FATAL("Oops, failed to execute '%s'", opt_params[0]); + + } + + if (pid < 0) PFATAL("fork() failed"); + if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + if (WEXITSTATUS(status) != 0) exit(WEXITSTATUS(status)); + + /* then we run the instrumentation through the optimizer */ + if (!be_quiet) + OKF("Performing instrumentation via opt, creating %s", final_file); + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < inst_param_cnt; i++) + SAYF(" \"%s\"", inst_params[i]); + SAYF("\n"); + + } + + if (!(pid = fork())) { + + execvp(inst_params[0], (char**)inst_params); + FATAL("Oops, failed to execute '%s'", inst_params[0]); + + } + + if (pid < 0) PFATAL("fork() failed"); + if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + if (WEXITSTATUS(status) != 0) exit(WEXITSTATUS(status)); + + } + + /* next step - run the linker! :-) */ + + } + + if (!be_quiet) OKF("Running real linker %s", real_ld); + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < ld_param_cnt; i++) + SAYF(" \"%s\"", ld_params[i]); + SAYF("\n"); + + } + + if (!(pid = fork())) { + + clean_path(); + + unsetenv("AFL_LD"); + + if (strlen(real_ld) > 1) execvp(real_ld, (char**)ld_params); + execvp("ld", (char**)ld_params); // fallback + FATAL("Oops, failed to execute 'ld' - check your PATH"); + + } + + if (pid < 0) PFATAL("fork() failed"); + + if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + if (debug) SAYF(cMGN "[D] " cRST "linker result: %d\n", status); + + if (!just_version) { + + if (!getenv("AFL_KEEP_ASSEMBLY")) { + + if (linked_file) { + + unlink(linked_file); + linked_file = NULL; + + } + + if (modified_file) { + + unlink(modified_file); + modified_file = NULL; + + } + + if (final_file) { + + unlink(final_file); + final_file = NULL; + + } + + if (ar_dir != NULL) { + + wipe_directory(ar_dir); + ar_dir = NULL; + + } + + } else { + + if (!be_quiet) { + + SAYF( + "[!] afl-ld: keeping link file %s, optimized bitcode %s and " + "instrumented bitcode %s", + linked_file, modified_file, final_file); + if (ar_dir_cnt > 0 && ar_dir) + SAYF(" and ar archive unpack directory %s", ar_dir); + SAYF("\n"); + + } + + } + + if (status == 0) { + + if (!be_quiet) OKF("Linker was successful"); + + } else { + + SAYF(cLRD "[-] " cRST + "Linker failed, please investigate and send a bug report. Most " + "likely an 'ld' option is incompatible with %s. Try " + "AFL_KEEP_ASSEMBLY=1 and AFL_DEBUG=1 for replaying.\n", + AFL_CLANG_FLTO); + + } + + } + + exit(WEXITSTATUS(status)); + +} + diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc new file mode 100644 index 00000000..4075d966 --- /dev/null +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -0,0 +1,424 @@ +/* + american fuzzy lop++ - LLVM-mode instrumentation pass + --------------------------------------------------- + + Written by Laszlo Szekeres <lszekeres@google.com> and + 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. + + 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. + It tells the compiler to add code roughly equivalent to the bits discussed + in ../afl-as.h. + + */ + +// CONFIG OPTION: +// If #define USE_SPLIT is used, then the llvm::SplitEdge function is used +// instead of our own implementation. Ours looks better and will +// compile everywhere. But it is not working for complex code. yet. damn. +#define USE_SPLIT + +#define AFL_LLVM_PASS + +#include "config.h" +#include "debug.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <list> +#include <string> +#include <fstream> +#include <sys/time.h> + +#include "llvm/Config/llvm-config.h" +#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 +typedef long double max_align_t; +#endif + +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +#ifdef USE_SPLIT +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#endif + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/CFG.h" +#else +#include "llvm/DebugInfo.h" +#include "llvm/Support/CFG.h" +#endif + +using namespace llvm; + +namespace { + +class AFLLTOPass : public ModulePass { + + public: + static char ID; + + AFLLTOPass() : 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); + + } + +#ifdef USE_SPLIT + void getAnalysisUsage(AnalysisUsage &AU) const override { + + ModulePass::getAnalysisUsage(AU); + AU.addRequired<DominatorTreeWrapperPass>(); + AU.addRequired<LoopInfoWrapperPass>(); + + } +#endif + + // 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; + + } + + // Get the internal llvm name of a basic block + // This is an ugly debug support so it is commented out :-) +/* + static char *getBBName(const BasicBlock *BB) { + + static char *name; + + if (!BB->getName().empty()) { + + name = strdup(BB->getName().str().c_str()); + return name; + + } + + std::string Str; + raw_string_ostream OS(Str); + + BB->printAsOperand(OS, false); + + name = strdup(OS.str().c_str()); + + return name; + + } +*/ + + static bool isBlacklisted(const Function *F) { + + static const char *Blacklist[] = { + + "asan.", "llvm.", "sancov.", "__ubsan_handle_", "ign.", + "__afl_", "_fini", "__libc_csu" + + }; + + for (auto const &BlacklistFunc : Blacklist) { + + if (F->getName().startswith(BlacklistFunc)) { return true; } + + } + + return false; + + } + + bool runOnModule(Module &M) override; + + protected: + int afl_global_id = 1, debug = 0; + uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0; + +}; + +} // namespace + +bool AFLLTOPass::runOnModule(Module &M) { + + LLVMContext &C = M.getContext(); + + IntegerType * Int8Ty = IntegerType::getInt8Ty(C); + IntegerType * Int32Ty = IntegerType::getInt32Ty(C); + struct timeval tv; + struct timezone tz; + u32 rand_seed; + + /* Setup random() so we get Actually Random(TM) outputs from AFL_R() */ + gettimeofday(&tv, &tz); + rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); + AFL_SR(rand_seed); + + /* Show a banner */ + + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + + SAYF(cCYA "afl-llvm-lto" VERSION cRST + " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); + + } else + + be_quiet = 1; + +#if LLVM_VERSION_MAJOR < 9 + char *neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO"); +#endif + + /* 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"); + + ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); + ConstantInt *One = ConstantInt::get(Int8Ty, 1); + + /* Instrument all the things! */ + + int inst_blocks = 0; + + for (auto &F : M) { + + if (F.size() < 2) continue; + if (isBlacklisted(&F)) continue; + +#ifdef USE_SPLIT + // DominatorTree &DT = + // getAnalysis<DominatorTreeWrapperPass>(F).getDomTree(); LoopInfo & LI = + // getAnalysis<LoopInfoWrapperPass>(F).getLoopInfo(); +#endif + + std::vector<BasicBlock *> InsBlocks; + + for (auto &BB : F) { + + uint32_t succ = 0; + + for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE; + ++SI) + if ((*SI)->size() > 0) + succ++; + + if (succ < 2) // no need to instrument + continue; + + InsBlocks.push_back(&BB); + + } + + if (InsBlocks.size() > 0) { + + uint32_t i = InsBlocks.size(); + + do { + + --i; + BasicBlock *origBB = &(*InsBlocks[i]); + std::vector<BasicBlock *> Successors; + Instruction *TI = origBB->getTerminator(); + + for (succ_iterator SI = succ_begin(origBB), SE = succ_end(origBB); + SI != SE; ++SI) { + + BasicBlock *succ = *SI; + Successors.push_back(succ); + + } + + if (TI == NULL || TI->getNumSuccessors() < 2) continue; + + //if (Successors.size() != TI->getNumSuccessors()) + // FATAL("Different successor numbers %lu <-> %u\n", Successors.size(), + // TI->getNumSuccessors()); + + for (uint32_t j = 0; j < Successors.size(); j++) { + +#ifdef USE_SPLIT + BasicBlock *newBB = llvm::SplitEdge(origBB, Successors[j]); +#else + BasicBlock *newBB = BasicBlock::Create(C, "", &F, nullptr); +#endif + + if (!newBB) { + + WARNF("Split failed!"); + continue; + + } + +#ifdef USE_SPLIT + BasicBlock::iterator IP = newBB->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); +#else + IRBuilder<> IRB(&(*newBB)); +#endif + + /* Set the ID of the inserted basic block */ + + ConstantInt *CurLoc = ConstantInt::get(Int32Ty, afl_global_id++); + + /* Load SHM pointer */ + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + MapPtr->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + Value *MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); + + /* Update bitmap */ + + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + Counter->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + Value *Incr = IRB.CreateAdd(Counter, One); + +#if LLVM_VERSION_MAJOR < 9 + if (neverZero_counters_str != + NULL) { // with llvm 9 we make this the default as the bug in + // llvm is then fixed +#endif + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); +#if LLVM_VERSION_MAJOR < 9 + + } + +#endif + IRB.CreateStore(Incr, MapPtrIdx) + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + +#ifdef USE_SPLIT + // nothing +#else + + // Unconditional jump to the destination BB + + IRB.CreateBr(Successors[j]); + + // Replace the original destination to this newly inserted BB + + origBB->replacePhiUsesWith(Successors[j], newBB); + BasicBlock *S = Successors[j]; + S->replacePhiUsesWith(origBB, newBB); + TI->setSuccessor(j, newBB); + +#endif + // done :) + + inst_blocks++; + + } + + } while (i > 0); + + } + + } + + // save highest location ID to global variable + + if (afl_global_id > MAP_SIZE) { + + uint32_t pow2map = 1, map = afl_global_id; + while ((map = map >> 1)) + pow2map++; + FATAL( + "We have %u blocks to instrument but the map size is only %u! Edit " + "config.h and set MAP_SIZE_POW2 from %u to %u, then recompile " + "afl-fuzz and llvm_mode.", + afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map); + + } + + if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { + + GlobalVariable *AFLFinalLoc = new GlobalVariable( + M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc", 0, + GlobalVariable::GeneralDynamicTLSModel, 0, false); + ConstantInt *const_loc = ConstantInt::get(Int32Ty, afl_global_id); + AFLFinalLoc->setAlignment(4); + AFLFinalLoc->setInitializer(const_loc); + + } + + /* 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", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations with no collisions (on average %llu collisions would be in afl-gcc/afl-clang-fast) (%s mode).", + inst_blocks, calculateCollisions(inst_blocks), modeline); + + } + + } + + return true; + +} + +char AFLLTOPass::ID = 0; + +static void registerAFLLTOPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new AFLLTOPass()); + +} + +static RegisterPass<AFLLTOPass> X("afl-lto", "afl++ LTO instrumentation pass", + false, false); + +static RegisterStandardPasses RegisterAFLLTOPass( + PassManagerBuilder::EP_OptimizerLast, registerAFLLTOPass); + diff --git a/llvm_mode/afl-llvm-lto-whitelist.so.cc b/llvm_mode/afl-llvm-lto-whitelist.so.cc new file mode 100644 index 00000000..617f9ca1 --- /dev/null +++ b/llvm_mode/afl-llvm-lto-whitelist.so.cc @@ -0,0 +1,266 @@ +/* + american fuzzy lop++ - LLVM-mode instrumentation pass + --------------------------------------------------- + + Written by Laszlo Szekeres <lszekeres@google.com> and + 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. + + 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. + It tells the compiler to add code roughly equivalent to the bits discussed + in ../afl-as.h. + + */ + +#define AFL_LLVM_PASS + +#include "config.h" +#include "debug.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <list> +#include <string> +#include <fstream> +#include <sys/time.h> + +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/IR/CFG.h" + +using namespace llvm; + +namespace { + +class AFLwhitelist : public ModulePass { + + public: + static char ID; + AFLwhitelist() : ModulePass(ID) { + + int entries = 0; + + if (getenv("AFL_DEBUG")) debug = 1; + + char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST"); + if (instWhiteListFilename) { + + std::string line; + std::ifstream fileStream; + fileStream.open(instWhiteListFilename); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST"); + getline(fileStream, line); + while (fileStream) { + + myWhitelist.push_back(line); + getline(fileStream, line); + entries++; + + } + + } else + + PFATAL("afl-llvm-lto-whitelist.so loaded without AFL_LLVM_WHITELIST?!"); + + if (debug) + SAYF(cMGN "[D] " cRST "loaded whitelist %s with %d entries\n", + instWhiteListFilename, entries); + + } + + // ripped from aflgo + static bool isBlacklisted(const Function *F) { + + static const SmallVector<std::string, 5> Blacklist = { + + "asan.", "llvm.", "sancov.", "__ubsan_handle_", "ign." + + }; + + for (auto const &BlacklistFunc : Blacklist) { + + if (F->getName().startswith(BlacklistFunc)) { return true; } + + } + + return false; + + } + + bool runOnModule(Module &M) override; + + // StringRef getPassName() const override { + + // return "American Fuzzy Lop Instrumentation"; + // } + + protected: + std::list<std::string> myWhitelist; + int debug = 0; + +}; + +} // namespace + +char AFLwhitelist::ID = 0; + +bool AFLwhitelist::runOnModule(Module &M) { + + /* Show a banner */ + + char be_quiet = 0; + + if (isatty(2) && !getenv("AFL_QUIET")) { + + SAYF(cCYA "afl-llvm-lto-whitelist" VERSION cRST + " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); + + } else if (getenv("AFL_QUIET")) + + be_quiet = 1; + + for (auto &F : M) { + + if (isBlacklisted(&F)) continue; + + for (auto &BB : F) { + + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + if (!myWhitelist.empty()) { + + bool instrumentBlock = false; + + /* Get the current location using debug information. + * For now, just instrument the block if we are not able + * to determine our location. */ + DebugLoc Loc = IP->getDebugLoc(); + if (Loc) { + + DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); + + unsigned int instLine = cDILoc->getLine(); + StringRef instFilename = cDILoc->getFilename(); + + if (instFilename.str().empty()) { + + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { + + instFilename = oDILoc->getFilename(); + instLine = oDILoc->getLine(); + + } + + } + + (void)instLine; + + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list<std::string>::iterator it = myWhitelist.begin(); + it != myWhitelist.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. */ + if (instFilename.str().length() >= it->length()) { + + if (instFilename.str().compare( + instFilename.str().length() - it->length(), + it->length(), *it) == 0) { + + instrumentBlock = true; + break; + + } + + } + + } + + } + + } + + /* Either we couldn't figure out our location or the location is + * not whitelisted, so we skip instrumentation. + * We do this by renaming the function. */ + if (!instrumentBlock) { + + if (F.getName().compare("main") == 0 || + F.getName().compare("start") == 0 || + F.getName().compare("_start") == 0 || + F.getName().compare("init") == 0 || + F.getName().compare("_init") == 0) { + + WARNF("Cannot ignore functions main/init/start"); + + } else { + + // StringRef newName = StringRef("ign.") + F.getName(); + if (debug) + SAYF(cMGN "[D] " cRST "renamed %s to ign.%s\n", + F.getName().str().c_str(), F.getName().str().c_str()); + Function *_F(&F); + _F->setName("ign." + F.getName()); + + } + + } else if (debug) + + SAYF(cMGN "[D] " cRST "function %s is in whitelist\n", + F.getName().str().c_str()); + + } else { + + PFATAL("Whitelist is empty"); + + } + + break; + + } + + } + + return true; + +} + +static void registerAFLwhitelistpass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new AFLwhitelist()); + +} + +static RegisterStandardPasses RegisterAFLwhitelistpass( + PassManagerBuilder::EP_ModuleOptimizerEarly, registerAFLwhitelistpass); + +static RegisterStandardPasses RegisterAFLwhitelistpass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLwhitelistpass); + diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 488be3fc..64c2ff2b 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -543,6 +543,8 @@ static int area_is_mapped(void* ptr, size_t len) { void __cmplog_rtn_hook(void* ptr1, void* ptr2) { + if (!__afl_cmp_map) return; + if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc index 94b256f7..b08bcb8c 100644 --- a/llvm_mode/compare-transform-pass.so.cc +++ b/llvm_mode/compare-transform-pass.so.cc @@ -25,6 +25,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/ADT/Statistic.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc index fe021071..ec301b6a 100644 --- a/llvm_mode/split-compares-pass.so.cc +++ b/llvm_mode/split-compares-pass.so.cc @@ -27,6 +27,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/Pass.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc index d2ba28cb..c6f74b73 100644 --- a/llvm_mode/split-switches-pass.so.cc +++ b/llvm_mode/split-switches-pass.so.cc @@ -26,6 +26,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/ADT/Statistic.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" diff --git a/qemu_mode/patches/afl-qemu-tcg-runtime-inl.h b/qemu_mode/patches/afl-qemu-tcg-runtime-inl.h index 2bb0ac9e..9cdba901 100644 --- a/qemu_mode/patches/afl-qemu-tcg-runtime-inl.h +++ b/qemu_mode/patches/afl-qemu-tcg-runtime-inl.h @@ -158,3 +158,62 @@ void HELPER(afl_cmplog_64)(target_ulong cur_loc, target_ulong arg1, } +#include <sys/mman.h> + +static int area_is_mapped(void* ptr, size_t len) { + + char* p = ptr; + char* page = (char*)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1)); + + int r = msync(page, (p - page) + len, MS_ASYNC); + if (r < 0) return errno != ENOMEM; + return 1; + +} + +void HELPER(afl_cmplog_rtn)(CPUX86State *env) { + +#if defined(TARGET_X86_64) + + void* ptr1 = g2h(env->regs[R_EDI]); + void* ptr2 = g2h(env->regs[R_ESI]); + +#elif defined(TARGET_I386) + + target_ulong* stack = g2h(env->regs[R_ESP]); + + if (!area_is_mapped(stack, sizeof(target_ulong)*2)) return; + + // when this hook is executed, the retaddr is not on stack yet + void* ptr1 = g2h(stack[0]); + void* ptr2 = g2h(stack[1]); + +#else + + // dumb code to make it compile + void* ptr1 = NULL; + void* ptr2 = NULL; + return; + +#endif + + if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; + + uintptr_t k = (uintptr_t)env->eip; + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 31; + + hits &= CMP_MAP_RTN_H - 1; + __builtin_memcpy(((struct cmpfn_operands*)__afl_cmp_map->log[k])[hits].v0, + ptr1, 32); + __builtin_memcpy(((struct cmpfn_operands*)__afl_cmp_map->log[k])[hits].v1, + ptr2, 32); + +} diff --git a/qemu_mode/patches/i386-translate.diff b/qemu_mode/patches/i386-translate.diff index 8ccd6f4e..f0d1393b 100644 --- a/qemu_mode/patches/i386-translate.diff +++ b/qemu_mode/patches/i386-translate.diff @@ -1,5 +1,5 @@ diff --git a/target/i386/translate.c b/target/i386/translate.c -index 0dd5fbe4..a23da128 100644 +index 0dd5fbe4..0d405fb6 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -32,6 +32,8 @@ @@ -40,3 +40,23 @@ index 0dd5fbe4..a23da128 100644 next_byte: b = x86_ldub_code(env, s); /* Collect prefixes. */ +@@ -5056,6 +5063,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) + tcg_gen_ext16u_tl(s->T0, s->T0); + } + next_eip = s->pc - s->cs_base; ++ if (__afl_cmp_map && next_eip >= afl_start_code && ++ next_eip < afl_end_code) ++ gen_helper_afl_cmplog_rtn(cpu_env); + tcg_gen_movi_tl(s->T1, next_eip); + gen_push_v(s, s->T1); + gen_op_jmp_v(s->T0); +@@ -6544,6 +6554,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) + tval = (int16_t)insn_get(env, s, MO_16); + } + next_eip = s->pc - s->cs_base; ++ if (__afl_cmp_map && next_eip >= afl_start_code && ++ next_eip < afl_end_code) ++ gen_helper_afl_cmplog_rtn(cpu_env); + tval += next_eip; + if (dflag == MO_16) { + tval &= 0xffff; diff --git a/qemu_mode/patches/syscall.diff b/qemu_mode/patches/syscall.diff index 8158aa64..775fc9e0 100644 --- a/qemu_mode/patches/syscall.diff +++ b/qemu_mode/patches/syscall.diff @@ -1,5 +1,5 @@ diff --git a/linux-user/syscall.c b/linux-user/syscall.c -index b13a170e..5678c006 100644 +index b13a170e..4af79175 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -111,6 +111,9 @@ @@ -43,7 +43,23 @@ index b13a170e..5678c006 100644 ts = (TaskState *)cpu->opaque; if (flags & CLONE_SETTLS) cpu_set_tls (env, newtls); -@@ -10529,7 +10533,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, +@@ -7324,10 +7328,12 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, + #ifdef TARGET_NR_stime /* not on alpha */ + case TARGET_NR_stime: + { +- time_t host_time; +- if (get_user_sal(host_time, arg1)) ++ struct timespec ts; ++ ts.tv_nsec = 0; ++ if (get_user_sal(ts.tv_sec, arg1)) { + return -TARGET_EFAULT; +- return get_errno(stime(&host_time)); ++ } ++ return get_errno(clock_settime(CLOCK_REALTIME, &ts)); + } + #endif + #ifdef TARGET_NR_alarm /* not on alpha */ +@@ -10529,7 +10535,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return TARGET_PAGE_SIZE; #endif case TARGET_NR_gettid: @@ -52,7 +68,7 @@ index b13a170e..5678c006 100644 #ifdef TARGET_NR_readahead case TARGET_NR_readahead: #if TARGET_ABI_BITS == 32 -@@ -10813,8 +10817,19 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, +@@ -10813,8 +10819,19 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return get_errno(safe_tkill((int)arg1, target_to_host_signal(arg2))); case TARGET_NR_tgkill: diff --git a/qemu_mode/patches/tcg-runtime-head.diff b/qemu_mode/patches/tcg-runtime-head.diff index ef55558e..626c67ef 100644 --- a/qemu_mode/patches/tcg-runtime-head.diff +++ b/qemu_mode/patches/tcg-runtime-head.diff @@ -1,8 +1,8 @@ diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h -index 1bd39d13..c58dee31 100644 +index 1bd39d13..81ef3973 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h -@@ -260,3 +260,12 @@ DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +@@ -260,3 +260,13 @@ DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -15,3 +15,4 @@ index 1bd39d13..c58dee31 100644 +DEF_HELPER_FLAGS_3(afl_cmplog_16, TCG_CALL_NO_RWG, void, tl, tl, tl) +DEF_HELPER_FLAGS_3(afl_cmplog_32, TCG_CALL_NO_RWG, void, tl, tl, tl) +DEF_HELPER_FLAGS_3(afl_cmplog_64, TCG_CALL_NO_RWG, void, tl, tl, tl) ++DEF_HELPER_FLAGS_1(afl_cmplog_rtn, TCG_CALL_NO_RWG, void, env) diff --git a/src/afl-common.c b/src/afl-common.c index 46d7de26..20fc424a 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -156,6 +156,7 @@ char** get_qemu_argv(u8* own_loc, char** argv, int argc) { if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { + ck_free(cp); target_path = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace"); return new_argv; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 56c52c9b..58985d8b 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -643,7 +643,7 @@ void sync_fuzzers(char** argv) { fault = run_target(argv, exec_tmout); - if (stop_soon) return; + if (stop_soon) goto close_sync; syncing_party = sd_ent->d_name; queued_imported += save_if_interesting(argv, mem, st.st_size, fault); @@ -662,6 +662,7 @@ void sync_fuzzers(char** argv) { ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); +close_sync: close(id_fd); closedir(qd); ck_free(qd_path); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2d5a5743..12c7853c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -119,7 +119,7 @@ static void usage(u8* argv0, int more_help) { " if using QEMU, just use -c 0.\n\n" "Fuzzing behavior settings:\n" - " -N - do not unlink the fuzzing input file\n" + " -N - do not unlink the fuzzing input file (only for devices etc.!)\n" " -d - quick & dirty mode (skips deterministic steps)\n" " -n - fuzz without instrumentation (dumb mode)\n" " -x dir - optional fuzzer dictionary (see README.md, its really " diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 4c1168a6..6075027f 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -173,8 +173,8 @@ static u32 write_results_to_file(u8* out_file) { s32 fd; u32 i, ret = 0; - u8 cco = !!get_afl_env("AFL_CMIN_CRASHES_ONLY"), - caa = !!get_afl_env("AFL_CMIN_ALLOW_ANY"); + u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"), + caa = !!getenv("AFL_CMIN_ALLOW_ANY"); if (!strncmp(out_file, "/dev/", 5)) { @@ -654,6 +654,7 @@ static void usage(u8* argv0) { "Environment variables used:\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_DEBUG: enable extra developer output\n" + "AFL_QUIET: do not print extra informational output" "AFL_CMIN_CRASHES_ONLY: (cmin_mode) only write tuples for crashing " "inputs\n" "AFL_CMIN_ALLOW_ANY: (cmin_mode) write tuples for crashing inputs also\n" @@ -729,6 +730,9 @@ int main(int argc, char** argv, char** envp) { char** use_argv; doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; + + if (getenv("AFL_QUIET") != NULL) + be_quiet = 1; while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqZQUWbcrh")) > 0) diff --git a/test/test.sh b/test/test.sh index 0d68413d..1a3f562c 100755 --- a/test/test.sh +++ b/test/test.sh @@ -184,6 +184,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc ;; esac rm -f in2/in* + export AFL_QUIET=1 AFL_PATH=`pwd`/.. ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null CNT=`ls in2/* 2>/dev/null | wc -l` case "$CNT" in @@ -200,6 +201,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc CODE=1 } rm -rf in out errors in2 + unset AFL_QUIET } rm -f test-instr.plain } || { @@ -380,6 +382,80 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { INCOMPLETE=1 } +$ECHO "$BLUE[*] Testing: LTO llvm_mode" +test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { + # on FreeBSD need to set AFL_CC + test `uname -s` = 'FreeBSD' && { + if which clang >/dev/null; then + export AFL_CC=`which clang` + else + export AFL_CC=`$LLVM_CONFIG --bindir`/clang + fi + } + + ../afl-clang-lto -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + test -e test-instr.plain && { + $ECHO "$GREEN[+] llvm_mode LTO compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] llvm_mode LTO instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] llvm_mode LTO instrumentation present and working correctly" + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 6 && { + $ECHO "$GREEN[+] llvm_mode LTO run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + } + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + } || { + $ECHO "$RED[!] LTO llvm_mode failed" + CODE=1 + } + rm -f test-instr.plain + +# Disabled whitelist and persistent until I have a different solution -mh +# echo foobar.c > whitelist.txt +# AFL_LLVM_WHITELIST=whitelist.txt ../afl-clang-lto -o test-compcov test-compcov.c > test.out 2>&1 +# test -e test-compcov && { +# grep -q "No instrumentation targets found" test.out && { +# $ECHO "$GREEN[+] llvm_mode LTO whitelist feature works correctly" +# } || { +# $ECHO "$RED[!] llvm_mode LTO whitelist feature failed" +# CODE=1 +# } +# } || { +# $ECHO "$RED[!] llvm_mode LTO whitelist feature compilation failed" +# CODE=1 +# } +# rm -f test-compcov test.out whitelist.txt +# ../afl-clang-lto -o test-persistent ../experimental/persistent_demo/persistent_demo.c > /dev/null 2>&1 +# test -e test-persistent && { +# echo foo | ../afl-showmap -o /dev/null -q -r ./test-persistent && { +# $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" +# } || { +# $ECHO "$RED[!] llvm_mode LTO persistent mode feature failed to work" +# CODE=1 +# } +# } || { +# $ECHO "$RED[!] llvm_mode LTO persistent mode feature compilation failed" +# CODE=1 +# } +# rm -f test-persistent +} || { + $ECHO "$YELLOW[-] LTO llvm_mode not compiled, cannot test" + INCOMPLETE=1 +} + $ECHO "$BLUE[*] Testing: gcc_plugin" export AFL_CC=`which gcc` test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && { |