aboutsummaryrefslogtreecommitdiff
path: root/llvm_mode
diff options
context:
space:
mode:
authorh1994st <h1994st@gmail.com>2020-03-06 16:28:26 -0500
committerh1994st <h1994st@gmail.com>2020-03-06 16:28:26 -0500
commita10a3f2fa75f16bf7781a3c02cd23eab7164cff1 (patch)
tree653138ebd36b80fd9fd3faa31eedd8666e185135 /llvm_mode
parent9e5c4973eb8f9b2f007bb1fe10976a4634c0ea6a (diff)
parent2287534ec6dd68b06a5052caa4ab3305d15861ec (diff)
downloadafl++-a10a3f2fa75f16bf7781a3c02cd23eab7164cff1.tar.gz
Merge branch 'master' of https://github.com/vanhauser-thc/AFLplusplus
Diffstat (limited to 'llvm_mode')
-rw-r--r--llvm_mode/Makefile145
-rw-r--r--llvm_mode/NOTES88
-rw-r--r--llvm_mode/README.lto.md150
-rw-r--r--llvm_mode/TODO10
-rw-r--r--llvm_mode/afl-clang-fast.c138
-rw-r--r--llvm_mode/afl-ld.c750
-rw-r--r--llvm_mode/afl-llvm-lto-instrumentation.so.cc424
-rw-r--r--llvm_mode/afl-llvm-lto-whitelist.so.cc266
-rw-r--r--llvm_mode/afl-llvm-rt.o.c2
-rw-r--r--llvm_mode/compare-transform-pass.so.cc1
-rw-r--r--llvm_mode/split-compares-pass.so.cc1
-rw-r--r--llvm_mode/split-switches-pass.so.cc1
12 files changed, 1919 insertions, 57 deletions
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"