This commit is contained in:
h1994st 2020-03-06 16:28:26 -05:00
commit a10a3f2fa7
26 changed files with 2125 additions and 74 deletions

View File

@ -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

View File

@ -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

View File

@ -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
}
#######################################################

View File

@ -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

View File

@ -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

View File

@ -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

88
llvm_mode/NOTES Normal file
View File

@ -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
}
}
}

150
llvm_mode/README.lto.md Normal file
View File

@ -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

10
llvm_mode/TODO Normal file
View File

@ -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?

View File

@ -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;
}

750
llvm_mode/afl-ld.c Normal file
View File

@ -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));
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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);
}

View File

@ -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;

View File

@ -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:

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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 "

View File

@ -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)

View File

@ -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 && {