mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-10 17:21:33 +00:00
Merge branch 'master' of https://github.com/vanhauser-thc/AFLplusplus
This commit is contained in:
commit
a10a3f2fa7
@ -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
|
||||
|
1
Makefile
1
Makefile
@ -417,6 +417,7 @@ install: all $(MANPAGES)
|
||||
if [ -f afl-gcc-fast ]; then set e; install -m 755 afl-gcc-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-gcc-fast $${DESTDIR}$(BIN_PATH)/afl-g++-fast; install -m 755 afl-gcc-pass.so afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
||||
ifndef AFL_TRACE_PC
|
||||
if [ -f afl-clang-fast -a -f libLLVMInsTrim.so -a -f afl-llvm-rt.o ]; then set -e; install -m 755 afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 libLLVMInsTrim.so afl-llvm-pass.so afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f afl-clang-lto -a -f afl-ld ]; then set -e; install -m 755 afl-clang-lto $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 afl-ld $${DESTDIR}$(HELPER_PATH); ln -sf afl-ld $${DESTDIR}$(HELPER_PATH)/ld; install -m 755 afl-llvm-lto-instrumentation.so $${DESTDIR}$(HELPER_PATH); install -m 755 afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
else
|
||||
if [ -f afl-clang-fast -a -f afl-llvm-rt.o ]; then set -e; install -m 755 afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
||||
endif
|
||||
|
13
afl-cmin
13
afl-cmin
@ -397,10 +397,19 @@ BEGIN {
|
||||
cur = 0;
|
||||
if (!stdin_file) {
|
||||
print " Processing "in_count" files (forkserver mode)..."
|
||||
system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
|
||||
retval = system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
|
||||
} else {
|
||||
print " Processing "in_count" files (forkserver mode)..."
|
||||
system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
retval = system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
print "[!]Exit code != 0 received from afl-showmap, terminating..."
|
||||
|
||||
if (!ENVIRON["AFL_KEEP_TRACES"]) {
|
||||
system("rm -rf "trace_dir" 2>/dev/null")
|
||||
}
|
||||
exit retval
|
||||
}
|
||||
|
||||
#######################################################
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
88
llvm_mode/NOTES
Normal 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
150
llvm_mode/README.lto.md
Normal 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
10
llvm_mode/TODO
Normal 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?
|
||||
|
@ -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
750
llvm_mode/afl-ld.c
Normal 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));
|
||||
|
||||
}
|
||||
|
424
llvm_mode/afl-llvm-lto-instrumentation.so.cc
Normal file
424
llvm_mode/afl-llvm-lto-instrumentation.so.cc
Normal 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);
|
||||
|
266
llvm_mode/afl-llvm-lto-whitelist.so.cc
Normal file
266
llvm_mode/afl-llvm-lto-whitelist.so.cc
Normal 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);
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 "
|
||||
|
@ -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)
|
||||
|
||||
|
76
test/test.sh
76
test/test.sh
@ -184,6 +184,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc
|
||||
;;
|
||||
esac
|
||||
rm -f in2/in*
|
||||
export AFL_QUIET=1
|
||||
AFL_PATH=`pwd`/.. ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null
|
||||
CNT=`ls in2/* 2>/dev/null | wc -l`
|
||||
case "$CNT" in
|
||||
@ -200,6 +201,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc
|
||||
CODE=1
|
||||
}
|
||||
rm -rf in out errors in2
|
||||
unset AFL_QUIET
|
||||
}
|
||||
rm -f test-instr.plain
|
||||
} || {
|
||||
@ -380,6 +382,80 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
|
||||
INCOMPLETE=1
|
||||
}
|
||||
|
||||
$ECHO "$BLUE[*] Testing: LTO llvm_mode"
|
||||
test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && {
|
||||
# on FreeBSD need to set AFL_CC
|
||||
test `uname -s` = 'FreeBSD' && {
|
||||
if which clang >/dev/null; then
|
||||
export AFL_CC=`which clang`
|
||||
else
|
||||
export AFL_CC=`$LLVM_CONFIG --bindir`/clang
|
||||
fi
|
||||
}
|
||||
|
||||
../afl-clang-lto -o test-instr.plain ../test-instr.c > /dev/null 2>&1
|
||||
test -e test-instr.plain && {
|
||||
$ECHO "$GREEN[+] llvm_mode LTO compilation succeeded"
|
||||
echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1
|
||||
../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1
|
||||
test -e test-instr.plain.0 -a -e test-instr.plain.1 && {
|
||||
diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && {
|
||||
$ECHO "$RED[!] llvm_mode LTO instrumentation should be different on different input but is not"
|
||||
CODE=1
|
||||
} || {
|
||||
$ECHO "$GREEN[+] llvm_mode LTO instrumentation present and working correctly"
|
||||
TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'`
|
||||
test "$TUPLES" -gt 3 -a "$TUPLES" -lt 6 && {
|
||||
$ECHO "$GREEN[+] llvm_mode LTO run reported $TUPLES instrumented locations which is fine"
|
||||
} || {
|
||||
$ECHO "$RED[!] llvm_mode LTO instrumentation produces weird numbers: $TUPLES"
|
||||
CODE=1
|
||||
}
|
||||
}
|
||||
} || {
|
||||
$ECHO "$RED[!] llvm_mode LTO instrumentation failed"
|
||||
CODE=1
|
||||
}
|
||||
rm -f test-instr.plain.0 test-instr.plain.1
|
||||
} || {
|
||||
$ECHO "$RED[!] LTO llvm_mode failed"
|
||||
CODE=1
|
||||
}
|
||||
rm -f test-instr.plain
|
||||
|
||||
# Disabled whitelist and persistent until I have a different solution -mh
|
||||
# echo foobar.c > whitelist.txt
|
||||
# AFL_LLVM_WHITELIST=whitelist.txt ../afl-clang-lto -o test-compcov test-compcov.c > test.out 2>&1
|
||||
# test -e test-compcov && {
|
||||
# grep -q "No instrumentation targets found" test.out && {
|
||||
# $ECHO "$GREEN[+] llvm_mode LTO whitelist feature works correctly"
|
||||
# } || {
|
||||
# $ECHO "$RED[!] llvm_mode LTO whitelist feature failed"
|
||||
# CODE=1
|
||||
# }
|
||||
# } || {
|
||||
# $ECHO "$RED[!] llvm_mode LTO whitelist feature compilation failed"
|
||||
# CODE=1
|
||||
# }
|
||||
# rm -f test-compcov test.out whitelist.txt
|
||||
# ../afl-clang-lto -o test-persistent ../experimental/persistent_demo/persistent_demo.c > /dev/null 2>&1
|
||||
# test -e test-persistent && {
|
||||
# echo foo | ../afl-showmap -o /dev/null -q -r ./test-persistent && {
|
||||
# $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly"
|
||||
# } || {
|
||||
# $ECHO "$RED[!] llvm_mode LTO persistent mode feature failed to work"
|
||||
# CODE=1
|
||||
# }
|
||||
# } || {
|
||||
# $ECHO "$RED[!] llvm_mode LTO persistent mode feature compilation failed"
|
||||
# CODE=1
|
||||
# }
|
||||
# rm -f test-persistent
|
||||
} || {
|
||||
$ECHO "$YELLOW[-] LTO llvm_mode not compiled, cannot test"
|
||||
INCOMPLETE=1
|
||||
}
|
||||
|
||||
$ECHO "$BLUE[*] Testing: gcc_plugin"
|
||||
export AFL_CC=`which gcc`
|
||||
test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && {
|
||||
|
Loading…
x
Reference in New Issue
Block a user