aboutsummaryrefslogtreecommitdiff
path: root/llvm_mode
diff options
context:
space:
mode:
Diffstat (limited to 'llvm_mode')
-rw-r--r--llvm_mode/LLVMInsTrim.so.cc350
-rw-r--r--llvm_mode/Makefile79
-rw-r--r--llvm_mode/MarkNodes.cc355
-rw-r--r--llvm_mode/MarkNodes.h11
-rw-r--r--llvm_mode/README.instrim26
-rw-r--r--llvm_mode/README.laf-intel8
-rw-r--r--llvm_mode/README.llvm53
-rw-r--r--llvm_mode/README.neverzero22
-rw-r--r--llvm_mode/afl-clang-fast.c33
-rw-r--r--llvm_mode/afl-llvm-pass.so.cc64
-rw-r--r--llvm_mode/afl-llvm-rt.o.c27
-rw-r--r--llvm_mode/compare-transform-pass.so.cc11
-rw-r--r--llvm_mode/split-compares-pass.so.cc5
-rw-r--r--llvm_mode/split-switches-pass.so.cc6
14 files changed, 996 insertions, 54 deletions
diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc
new file mode 100644
index 00000000..81cf98c4
--- /dev/null
+++ b/llvm_mode/LLVMInsTrim.so.cc
@@ -0,0 +1,350 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CFG.h"
+#include <unordered_set>
+#include <random>
+#include <list>
+#include <string>
+#include <fstream>
+
+#include "../config.h"
+#include "../debug.h"
+
+#include "MarkNodes.h"
+
+using namespace llvm;
+
+static cl::opt<bool> MarkSetOpt("markset", cl::desc("MarkSet"),
+ cl::init(false));
+static cl::opt<bool> LoopHeadOpt("loophead", cl::desc("LoopHead"),
+ cl::init(false));
+
+namespace {
+ struct InsTrim : public ModulePass {
+
+ protected:
+ std::list<std::string> myWhitelist;
+
+ private:
+ std::mt19937 generator;
+ int total_instr = 0;
+
+ unsigned genLabel() {
+ return generator() % 65536;
+ }
+
+ public:
+ static char ID;
+ InsTrim() : ModulePass(ID), generator(0) {//}
+
+// AFLCoverage() : ModulePass(ID) {
+ 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);
+ }
+ }
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequired<DominatorTreeWrapperPass>();
+ }
+
+#if LLVM_VERSION_MAJOR < 4
+ const char *
+#else
+ StringRef
+#endif
+ getPassName() const override {
+ return "InstTrim Instrumentation";
+ }
+
+ bool runOnModule(Module &M) override {
+ char be_quiet = 0;
+
+ if (isatty(2) && !getenv("AFL_QUIET")) {
+ SAYF(cCYA "LLVMInsTrim" VERSION cRST " by csienslab\n");
+ } else be_quiet = 1;
+
+#if LLVM_VERSION_MAJOR < 9
+ char* neverZero_counters_str;
+ if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL)
+ OKF("LLVM neverZero activated (by hexcoder)\n");
+#endif
+
+ if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || getenv("LOOPHEAD") != NULL) {
+ LoopHeadOpt = true;
+ }
+
+ // this is our default
+ MarkSetOpt = true;
+
+/* // I dont think this makes sense to port into LLVMInsTrim
+ char* inst_ratio_str = getenv("AFL_INST_RATIO");
+ unsigned int inst_ratio = 100;
+ if (inst_ratio_str) {
+ if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || inst_ratio > 100)
+ FATAL("Bad value of AFL_INST_RATIO (must be between 1 and 100)");
+ }
+*/
+
+ LLVMContext &C = M.getContext();
+ IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
+ IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
+
+ GlobalVariable *CovMapPtr = new GlobalVariable(
+ M, PointerType::getUnqual(Int8Ty), false, GlobalValue::ExternalLinkage,
+ nullptr, "__afl_area_ptr");
+
+ GlobalVariable *OldPrev = new GlobalVariable(
+ M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc",
+ 0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
+
+ u64 total_rs = 0;
+ u64 total_hs = 0;
+
+ for (Function &F : M) {
+ if (!F.size()) {
+ continue;
+ }
+
+ if (!myWhitelist.empty()) {
+ bool instrumentBlock = false;
+ DebugLoc Loc;
+ StringRef instFilename;
+
+ for (auto &BB : F) {
+ BasicBlock::iterator IP = BB.getFirstInsertionPt();
+ IRBuilder<> IRB(&(*IP));
+ if (!Loc)
+ Loc = IP->getDebugLoc();
+ }
+
+ if ( Loc ) {
+ DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
+
+ unsigned int instLine = cDILoc->getLine();
+ 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();
+ }
+ }
+
+ /* 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) {
+ 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. */
+ if (!instrumentBlock) {
+ if (!instFilename.str().empty())
+ SAYF(cYEL "[!] " cBRI "Not in whitelist, skipping %s ...\n", instFilename.str().c_str());
+ else
+ SAYF(cYEL "[!] " cBRI "No filename information found, skipping it");
+ continue;
+ }
+ }
+
+ std::unordered_set<BasicBlock *> MS;
+ if (!MarkSetOpt) {
+ for (auto &BB : F) {
+ MS.insert(&BB);
+ }
+ total_rs += F.size();
+ } else {
+ auto Result = markNodes(&F);
+ auto RS = Result.first;
+ auto HS = Result.second;
+
+ MS.insert(RS.begin(), RS.end());
+ if (!LoopHeadOpt) {
+ MS.insert(HS.begin(), HS.end());
+ total_rs += MS.size();
+ } else {
+ DenseSet<std::pair<BasicBlock *, BasicBlock *>> EdgeSet;
+ DominatorTreeWrapperPass *DTWP = &getAnalysis<DominatorTreeWrapperPass>(F);
+ auto DT = &DTWP->getDomTree();
+
+ total_rs += RS.size();
+ total_hs += HS.size();
+
+ for (BasicBlock *BB : HS) {
+ bool Inserted = false;
+ for (auto BI = pred_begin(BB), BE = pred_end(BB);
+ BI != BE; ++BI
+ ) {
+ auto Edge = BasicBlockEdge(*BI, BB);
+ if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) {
+ EdgeSet.insert({*BI, BB});
+ Inserted = true;
+ break;
+ }
+ }
+ if (!Inserted) {
+ MS.insert(BB);
+ total_rs += 1;
+ total_hs -= 1;
+ }
+ }
+ for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) {
+ auto PredBB = I->first;
+ auto SuccBB = I->second;
+ auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split",
+ DT, nullptr,
+#if LLVM_VERSION_MAJOR >= 8
+ nullptr,
+#endif
+ false);
+ MS.insert(NewBB);
+ }
+ }
+
+ auto *EBB = &F.getEntryBlock();
+ if (succ_begin(EBB) == succ_end(EBB)) {
+ MS.insert(EBB);
+ total_rs += 1;
+ }
+
+ for (BasicBlock &BB : F) {
+ if (MS.find(&BB) == MS.end()) {
+ continue;
+ }
+ IRBuilder<> IRB(&*BB.getFirstInsertionPt());
+ IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), OldPrev);
+ }
+ }
+
+ for (BasicBlock &BB : F) {
+ auto PI = pred_begin(&BB);
+ auto PE = pred_end(&BB);
+ if (MarkSetOpt && MS.find(&BB) == MS.end()) {
+ continue;
+ }
+
+ IRBuilder<> IRB(&*BB.getFirstInsertionPt());
+ Value *L = NULL;
+ if (PI == PE) {
+ L = ConstantInt::get(Int32Ty, genLabel());
+ } else {
+ auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin());
+ DenseMap<BasicBlock *, unsigned> PredMap;
+ for (auto PI = pred_begin(&BB), PE = pred_end(&BB);
+ PI != PE; ++PI
+ ) {
+ BasicBlock *PBB = *PI;
+ auto It = PredMap.insert({PBB, genLabel()});
+ unsigned Label = It.first->second;
+ PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
+ }
+ L = PN;
+ }
+
+ /* Load prev_loc */
+ LoadInst *PrevLoc = IRB.CreateLoad(OldPrev);
+ PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+ Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
+
+ /* Load SHM pointer */
+ LoadInst *MapPtr = IRB.CreateLoad(CovMapPtr);
+ MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+ Value *MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, L));
+
+ /* Update bitmap */
+ LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
+ Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+
+ Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
+
+#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
+#else
+ #warning "neverZero implementation needs to be reviewed!"
+#endif
+ /* hexcoder: Realize a counter that skips zero during overflow.
+ * Once this counter reaches its maximum value, it next increments to 1
+ *
+ * Instead of
+ * Counter + 1 -> Counter
+ * we inject now this
+ * Counter + 1 -> {Counter, OverflowFlag}
+ * Counter + OverflowFlag -> Counter
+ */
+ auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
+ 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));
+
+ /* Set prev_loc to cur_loc >> 1 */
+ /*
+ StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
+ Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+ */
+
+ total_instr++;
+ }
+ }
+
+ OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n"/*", ratio %u%%)."*/,
+ total_instr, total_rs, total_hs,
+ getenv("AFL_HARDEN") ? "hardened" :
+ ((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) ?
+ "ASAN/MSAN" : "non-hardened")/*, inst_ratio*/);
+ return false;
+ }
+ }; // end of struct InsTrim
+} // end of anonymous namespace
+
+char InsTrim::ID = 0;
+
+static void registerAFLPass(const PassManagerBuilder &,
+ legacy::PassManagerBase &PM) {
+ PM.add(new InsTrim());
+}
+
+static RegisterStandardPasses RegisterAFLPass(
+ PassManagerBuilder::EP_OptimizerLast, registerAFLPass);
+
+static RegisterStandardPasses RegisterAFLPass0(
+ PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile
index 6b277536..2b685ddc 100644
--- a/llvm_mode/Makefile
+++ b/llvm_mode/Makefile
@@ -16,6 +16,9 @@
# http://www.apache.org/licenses/LICENSE-2.0
#
+# For Heiko:
+#TEST_MMAP=1
+
PREFIX ?= /usr/local
HELPER_PATH = $(PREFIX)/lib/afl
BIN_PATH = $(PREFIX)/bin
@@ -23,17 +26,23 @@ BIN_PATH = $(PREFIX)/bin
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
LLVM_CONFIG ?= llvm-config
-#LLVM_OK = $(shell $(LLVM_CONFIG) --version | egrep -q '^[5-6]' && echo 0 || echo 1 )
+LLVMVER = $(shell $(LLVM_CONFIG) --version)
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version | egrep -q '^9|3.0' && echo 1 || echo 0 )
+LLVM_MAJOR = ($shell $(LLVM_CONFIG) --version | sed 's/\..*//')
ifeq "$(LLVM_UNSUPPORTED)" "1"
$(warn llvm_mode only supports versions 3.8.0 up to 8.x )
endif
+# this is not visible yet:
+ifeq "$(LLVM_MAJOR)" "9"
+ $(info llvm_mode deteted llvm 9, enabling neverZero implementation)
+endif
+
CFLAGS ?= -O3 -funroll-loops
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
- -DVERSION=\"$(VERSION)\"
+ -DVERSION=\"$(VERSION)\"
ifdef AFL_TRACE_PC
CFLAGS += -DUSE_TRACE_PC=1
endif
@@ -45,12 +54,16 @@ CXXFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS)
CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS)
-# User teor2345 reports that this is required to make things work on MacOS X.
+# User teor2345 reports that this is required to make things work on MacOS X.
ifeq "$(shell uname)" "Darwin"
CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress
endif
+ifeq "$(shell uname)" "OpenBSD"
+ CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so.0.0
+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.
@@ -60,13 +73,53 @@ ifeq "$(origin CC)" "default"
CXX = clang++
endif
+# sanity check.
+# Are versions of clang --version and llvm-config --version equal?
+CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*([0-9]\.[0-9]\.[0-9]).*/s//\1/p')
+
+
+ifeq "$(shell echo '\#include <sys/ipc.h>@\#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 )" "1"
+ SHMAT_OK=1
+else
+ SHMAT_OK=0
+ CFLAGS+=-DUSEMMAP=1
+ LDFLAGS += -lrt
+endif
+
+ifeq "$(TEST_MMAP)" "1"
+ SHMAT_OK=0
+ CFLAGS+=-DUSEMMAP=1
+ LDFLAGS += -lrt
+endif
+
+
ifndef AFL_TRACE_PC
- PROGS = ../afl-clang-fast ../afl-llvm-pass.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
+ 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
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
endif
-all: test_deps $(PROGS) test_build all_done
+ifneq "$(CLANGVER)" "$(LLVMVER)"
+ CC = $(shell llvm-config --bindir)/clang
+ CXX = $(shell llvm-config --bindir)/clang++
+endif
+
+all: test_shm test_deps $(PROGS) test_build all_done
+
+
+ifeq "$(SHMAT_OK)" "1"
+
+test_shm:
+ @echo "[+] shmat seems to be working."
+ @rm -f .test2
+
+else
+
+test_shm:
+ @echo "[-] shmat seems not to be working, switching to mmap implementation"
+
+endif
+
test_deps:
ifndef AFL_TRACE_PC
@@ -77,6 +130,13 @@ else
endif
@echo "[*] Checking for working '$(CC)'..."
@which $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
+ @echo "[*] Checking for matching versions of '$(CC)' and '$(LLVM_CONFIG)'"
+ifneq "$(CLANGVER)" "$(LLVMVER)"
+ @echo "[!] WARNING: we have llvm-config version $(LLVMVER) and a clang version $(CLANGVER)"
+ @echo "[!] Retrying with the clang compiler from llvm: CC=`llvm-config --bindir`/clang"
+else
+ @echo "[*] We have llvm-config version $(LLVMVER) with a clang version $(CLANGVER), good."
+endif
@echo "[*] Checking for '../afl-showmap'..."
@test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 )
@echo "[+] All set and ready to build."
@@ -85,8 +145,11 @@ endif
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
ln -sf afl-clang-fast ../afl-clang-fast++
+../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc | test_deps
+ $(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=gnu++11 -shared $< MarkNodes.cc -o $@ $(CLANG_LFL)
+
../afl-llvm-pass.so: afl-llvm-pass.so.cc | test_deps
- $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL)
+ $(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=gnu++11 -shared $< -o $@ $(CLANG_LFL)
# laf
../split-switches-pass.so: split-switches-pass.so.cc | test_deps
@@ -110,7 +173,7 @@ endif
test_build: $(PROGS)
@echo "[*] Testing the CC wrapper and instrumentation output..."
- unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=$(CC) LAF_SPLIT_SWITCHES=1 LAF_TRANSFORM_COMPARES=1 LAF_SPLIT_COMPARES=1 ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
+ unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=$(CC) AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
echo 0 | ../afl-showmap -m none -q -o .test-instr0 ./test-instr
echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr
@rm -f test-instr
@@ -123,5 +186,5 @@ all_done: test_build
.NOTPARALLEL: clean
clean:
- rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1
+ rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1
rm -f $(PROGS) ../afl-clang-fast++
diff --git a/llvm_mode/MarkNodes.cc b/llvm_mode/MarkNodes.cc
new file mode 100644
index 00000000..a156fccb
--- /dev/null
+++ b/llvm_mode/MarkNodes.cc
@@ -0,0 +1,355 @@
+#include <algorithm>
+#include <map>
+#include <queue>
+#include <set>
+#include <vector>
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+DenseMap<BasicBlock *, uint32_t> LMap;
+std::vector<BasicBlock *> Blocks;
+std::set<uint32_t> Marked , Markabove;
+std::vector< std::vector<uint32_t> > Succs , Preds;
+
+void reset(){
+ LMap.clear();
+ Blocks.clear();
+ Marked.clear();
+ Markabove.clear();
+}
+
+uint32_t start_point;
+
+void labelEachBlock(Function *F) {
+ // Fake single endpoint;
+ LMap[NULL] = Blocks.size();
+ Blocks.push_back(NULL);
+
+ // Assign the unique LabelID to each block;
+ for (auto I = F->begin(), E = F->end(); I != E; ++I) {
+ BasicBlock *BB = &*I;
+ LMap[BB] = Blocks.size();
+ Blocks.push_back(BB);
+ }
+
+ start_point = LMap[&F->getEntryBlock()];
+}
+
+void buildCFG(Function *F) {
+ Succs.resize( Blocks.size() );
+ Preds.resize( Blocks.size() );
+ for( size_t i = 0 ; i < Succs.size() ; i ++ ){
+ Succs[ i ].clear();
+ Preds[ i ].clear();
+ }
+
+ //uint32_t FakeID = 0;
+ for (auto S = F->begin(), E = F->end(); S != E; ++S) {
+ BasicBlock *BB = &*S;
+ uint32_t MyID = LMap[BB];
+ //if (succ_begin(BB) == succ_end(BB)) {
+ //Succs[MyID].push_back(FakeID);
+ //Marked.insert(MyID);
+ //}
+ for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) {
+ Succs[MyID].push_back(LMap[*I]);
+ }
+ }
+}
+
+std::vector< std::vector<uint32_t> > tSuccs;
+std::vector<bool> tag , indfs;
+
+void DFStree(size_t now_id) {
+ if(tag[now_id]) return;
+ tag[now_id]=true;
+ indfs[now_id]=true;
+ for (auto succ: tSuccs[now_id]) {
+ if(tag[succ] and indfs[succ]) {
+ Marked.insert(succ);
+ Markabove.insert(succ);
+ continue;
+ }
+ Succs[now_id].push_back(succ);
+ Preds[succ].push_back(now_id);
+ DFStree(succ);
+ }
+ indfs[now_id]=false;
+}
+void turnCFGintoDAG(Function *F) {
+ tSuccs = Succs;
+ tag.resize(Blocks.size());
+ indfs.resize(Blocks.size());
+ for (size_t i = 0; i < Blocks.size(); ++ i) {
+ Succs[i].clear();
+ tag[i]=false;
+ indfs[i]=false;
+ }
+ DFStree(start_point);
+ for (size_t i = 0; i < Blocks.size(); ++ i)
+ if( Succs[i].empty() ){
+ Succs[i].push_back(0);
+ Preds[0].push_back(i);
+ }
+}
+
+uint32_t timeStamp;
+namespace DominatorTree{
+ std::vector< std::vector<uint32_t> > cov;
+ std::vector<uint32_t> dfn, nfd, par, sdom, idom, mom, mn;
+
+ bool Compare(uint32_t u, uint32_t v) {
+ return dfn[u] < dfn[v];
+ }
+ uint32_t eval(uint32_t u) {
+ if( mom[u] == u ) return u;
+ uint32_t res = eval( mom[u] );
+ if(Compare(sdom[mn[mom[u]]] , sdom[mn[u]])) {
+ mn[u] = mn[mom[u]];
+ }
+ return mom[u] = res;
+ }
+
+ void DFS(uint32_t now) {
+ timeStamp += 1;
+ dfn[now] = timeStamp;
+ nfd[timeStamp - 1] = now;
+ for( auto succ : Succs[now] ) {
+ if( dfn[succ] == 0 ) {
+ par[succ] = now;
+ DFS(succ);
+ }
+ }
+ }
+
+ void DominatorTree(Function *F) {
+ if( Blocks.empty() ) return;
+ uint32_t s = start_point;
+
+ // Initialization
+ mn.resize(Blocks.size());
+ cov.resize(Blocks.size());
+ dfn.resize(Blocks.size());
+ nfd.resize(Blocks.size());
+ par.resize(Blocks.size());
+ mom.resize(Blocks.size());
+ sdom.resize(Blocks.size());
+ idom.resize(Blocks.size());
+
+ for( uint32_t i = 0 ; i < Blocks.size() ; i ++ ) {
+ dfn[i] = 0;
+ nfd[i] = Blocks.size();
+ cov[i].clear();
+ idom[i] = mom[i] = mn[i] = sdom[i] = i;
+ }
+
+ timeStamp = 0;
+ DFS(s);
+
+ for( uint32_t i = Blocks.size() - 1 ; i >= 1u ; i -- ) {
+ uint32_t now = nfd[i];
+ if( now == Blocks.size() ) {
+ continue;
+ }
+ for( uint32_t pre : Preds[ now ] ) {
+ if( dfn[ pre ] ) {
+ eval(pre);
+ if( Compare(sdom[mn[pre]], sdom[now]) ) {
+ sdom[now] = sdom[mn[pre]];
+ }
+ }
+ }
+ cov[sdom[now]].push_back(now);
+ mom[now] = par[now];
+ for( uint32_t x : cov[par[now]] ) {
+ eval(x);
+ if( Compare(sdom[mn[x]], par[now]) ) {
+ idom[x] = mn[x];
+ } else {
+ idom[x] = par[now];
+ }
+ }
+ }
+
+ for( uint32_t i = 1 ; i < Blocks.size() ; i += 1 ) {
+ uint32_t now = nfd[i];
+ if( now == Blocks.size() ) {
+ continue;
+ }
+ if(idom[now] != sdom[now])
+ idom[now] = idom[idom[now]];
+ }
+ }
+}; // End of DominatorTree
+
+std::vector<uint32_t> Visited, InStack;
+std::vector<uint32_t> TopoOrder, InDeg;
+std::vector< std::vector<uint32_t> > t_Succ , t_Pred;
+
+void Go(uint32_t now, uint32_t tt) {
+ if( now == tt ) return;
+ Visited[now] = InStack[now] = timeStamp;
+
+ for(uint32_t nxt : Succs[now]) {
+ if(Visited[nxt] == timeStamp and InStack[nxt] == timeStamp) {
+ Marked.insert(nxt);
+ }
+ t_Succ[now].push_back(nxt);
+ t_Pred[nxt].push_back(now);
+ InDeg[nxt] += 1;
+ if(Visited[nxt] == timeStamp) {
+ continue;
+ }
+ Go(nxt, tt);
+ }
+
+ InStack[now] = 0;
+}
+
+void TopologicalSort(uint32_t ss, uint32_t tt) {
+ timeStamp += 1;
+
+ Go(ss, tt);
+
+ TopoOrder.clear();
+ std::queue<uint32_t> wait;
+ wait.push(ss);
+ while( not wait.empty() ) {
+ uint32_t now = wait.front(); wait.pop();
+ TopoOrder.push_back(now);
+ for(uint32_t nxt : t_Succ[now]) {
+ InDeg[nxt] -= 1;
+ if(InDeg[nxt] == 0u) {
+ wait.push(nxt);
+ }
+ }
+ }
+}
+
+std::vector< std::set<uint32_t> > NextMarked;
+bool Indistinguish(uint32_t node1, uint32_t node2) {
+ if(NextMarked[node1].size() > NextMarked[node2].size()){
+ uint32_t _swap = node1;
+ node1 = node2;
+ node2 = _swap;
+ }
+ for(uint32_t x : NextMarked[node1]) {
+ if( NextMarked[node2].find(x) != NextMarked[node2].end() ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void MakeUniq(uint32_t now) {
+ bool StopFlag = false;
+ if (Marked.find(now) == Marked.end()) {
+ for(uint32_t pred1 : t_Pred[now]) {
+ for(uint32_t pred2 : t_Pred[now]) {
+ if(pred1 == pred2) continue;
+ if(Indistinguish(pred1, pred2)) {
+ Marked.insert(now);
+ StopFlag = true;
+ break;
+ }
+ }
+ if (StopFlag) {
+ break;
+ }
+ }
+ }
+ if(Marked.find(now) != Marked.end()) {
+ NextMarked[now].insert(now);
+ } else {
+ for(uint32_t pred : t_Pred[now]) {
+ for(uint32_t x : NextMarked[pred]) {
+ NextMarked[now].insert(x);
+ }
+ }
+ }
+}
+
+void MarkSubGraph(uint32_t ss, uint32_t tt) {
+ TopologicalSort(ss, tt);
+ if(TopoOrder.empty()) return;
+
+ for(uint32_t i : TopoOrder) {
+ NextMarked[i].clear();
+ }
+
+ NextMarked[TopoOrder[0]].insert(TopoOrder[0]);
+ for(uint32_t i = 1 ; i < TopoOrder.size() ; i += 1) {
+ MakeUniq(TopoOrder[i]);
+ }
+}
+
+void MarkVertice(Function *F) {
+ uint32_t s = start_point;
+
+ InDeg.resize(Blocks.size());
+ Visited.resize(Blocks.size());
+ InStack.resize(Blocks.size());
+ t_Succ.resize(Blocks.size());
+ t_Pred.resize(Blocks.size());
+ NextMarked.resize(Blocks.size());
+
+ for( uint32_t i = 0 ; i < Blocks.size() ; i += 1 ) {
+ Visited[i] = InStack[i] = InDeg[i] = 0;
+ t_Succ[i].clear();
+ t_Pred[i].clear();
+ }
+ timeStamp = 0;
+ uint32_t t = 0;
+ //MarkSubGraph(s, t);
+ //return;
+
+ while( s != t ) {
+ MarkSubGraph(DominatorTree::idom[t], t);
+ t = DominatorTree::idom[t];
+ }
+
+}
+
+// return {marked nodes}
+std::pair<std::vector<BasicBlock *>,
+ std::vector<BasicBlock *> >markNodes(Function *F) {
+ assert(F->size() > 0 && "Function can not be empty");
+
+ reset();
+ labelEachBlock(F);
+ buildCFG(F);
+ turnCFGintoDAG(F);
+ DominatorTree::DominatorTree(F);
+ MarkVertice(F);
+
+ std::vector<BasicBlock *> Result , ResultAbove;
+ for( uint32_t x : Markabove ) {
+ auto it = Marked.find( x );
+ if( it != Marked.end() )
+ Marked.erase( it );
+ if( x )
+ ResultAbove.push_back(Blocks[x]);
+ }
+ for( uint32_t x : Marked ) {
+ if (x == 0) {
+ continue;
+ } else {
+ Result.push_back(Blocks[x]);
+ }
+ }
+
+ return { Result , ResultAbove };
+}
diff --git a/llvm_mode/MarkNodes.h b/llvm_mode/MarkNodes.h
new file mode 100644
index 00000000..e3bf3ce5
--- /dev/null
+++ b/llvm_mode/MarkNodes.h
@@ -0,0 +1,11 @@
+#ifndef __MARK_NODES__
+#define __MARK_NODES__
+
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
+#include<vector>
+
+std::pair<std::vector<llvm::BasicBlock *>,
+ std::vector<llvm::BasicBlock *>> markNodes(llvm::Function *F);
+
+#endif
diff --git a/llvm_mode/README.instrim b/llvm_mode/README.instrim
new file mode 100644
index 00000000..956a9856
--- /dev/null
+++ b/llvm_mode/README.instrim
@@ -0,0 +1,26 @@
+# InsTrim
+InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing
+
+
+## Introduction
+
+InsTrim uses CFG and markers to instrument just what is necessary in the
+binary in llvm_mode. It is about 20-25% faster but as a cost has a lower
+path discovery.
+
+
+## Usage
+
+Set the environment variable AFL_LLVM_INSTRIM=1
+
+There is also an advanced mode which instruments loops in a way so that
+afl-fuzz can see which loop path has been selected but not being able to
+see how often the loop has been rerun.
+This again is a tradeoff for speed for less path information.
+To enable this mode set AFL_LLVM_INSTRIM_LOOPHEAD=1
+
+
+## Background
+
+The paper: [InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing]
+(https://www.ndss-symposium.org/wp-content/uploads/2018/07/bar2018_14_Hsu_paper.pdf)
diff --git a/llvm_mode/README.laf-intel b/llvm_mode/README.laf-intel
index 891ab5fd..340216c3 100644
--- a/llvm_mode/README.laf-intel
+++ b/llvm_mode/README.laf-intel
@@ -8,13 +8,13 @@ compile the target project.
The following options exist:
-export LAF_SPLIT_SWITCHES=1 Enables the split-switches pass.
+export AFL_LLVM_LAF_SPLIT_SWITCHES=1 Enables the split-switches pass.
-export LAF_TRANSFORM_COMPARES=1 Enables the transform-compares pass
+export AFL_LLVM_LAF_TRANSFORM_COMPARES=1 Enables the transform-compares pass
(strcmp, memcmp, strncmp, strcasecmp, strncasecmp).
-export LAF_SPLIT_COMPARES=1 Enables the split-compares pass.
+export AFL_LLVM_LAF_SPLIT_COMPARES=1 Enables the split-compares pass.
By default it will split all compares with a bit width <= 64 bits.
You can change this behaviour by setting
- export LAF_SPLIT_COMPARES_BITW=<bit_width>.
+ export AFL_LLVM_LAF_SPLIT_COMPARES_BITW=<bit_width>.
diff --git a/llvm_mode/README.llvm b/llvm_mode/README.llvm
index dc860e97..a0c40211 100644
--- a/llvm_mode/README.llvm
+++ b/llvm_mode/README.llvm
@@ -3,6 +3,7 @@ Fast LLVM-based instrumentation for afl-fuzz
============================================
(See ../docs/README for the general instruction manual.)
+ (See ../gcc_plugin/README.gcc for the GCC-based instrumentation.)
1) Introduction
---------------
@@ -30,7 +31,7 @@ several interesting properties:
- The instrumentation can cope a bit better with multi-threaded targets.
- Because the feature relies on the internals of LLVM, it is clang-specific
- and will *not* work with GCC.
+ and will *not* work with GCC (see ../gcc_plugin/ for an alternative).
Once this implementation is shown to be sufficiently robust and portable, it
will probably replace afl-clang. For now, it can be built separately and
@@ -38,8 +39,8 @@ co-exists with the original code.
The idea and much of the implementation comes from Laszlo Szekeres.
-2) How to use
--------------
+2) How to use this
+------------------
In order to leverage this mechanism, you need to have clang installed on your
system. You should also make sure that the llvm-config tool is in your path
@@ -69,21 +70,47 @@ operating mode of AFL, e.g.:
Be sure to also include CXX set to afl-clang-fast++ for C++ code.
The tool honors roughly the same environmental variables as afl-gcc (see
-../docs/env_variables.txt). This includes AFL_INST_RATIO, AFL_USE_ASAN,
-AFL_HARDEN, and AFL_DONT_OPTIMIZE.
+../docs/env_variables.txt). This includes AFL_USE_ASAN,
+AFL_HARDEN, and AFL_DONT_OPTIMIZE. However AFL_INST_RATIO is not honored
+as it does not serve a good purpose with the more effective instrim CFG
+analysis.
Note: if you want the LLVM helper to be installed on your system for all
users, you need to build it before issuing 'make install' in the parent
directory.
-3) Gotchas, feedback, bugs
+3) Options
+
+Several options are present to make llvm_mode faster or help it rearrange
+the code to make afl-fuzz path discovery easier.
+
+If you need just to instrument specific parts of the code, you can whitelist
+which C/C++ files to actually intrument. See README.whitelist
+
+For splitting memcmp, strncmp, etc. please see README.laf-intel
+
+Then there is an optimized instrumentation strategy that uses CFGs and
+markers to just instrument what is needed. This increases speed by 20-25%
+however has a lower path discovery.
+If you want to use this, set AFL_LLVM_INSTRIM=1
+See README.instrim
+
+Finally if your llvm version is 8 or lower, you can activate a mode that
+prevents that a counter overflow result in a 0 value. This is good for
+path discovery, but the llvm implementation for intel for this functionality
+is not optimal and was only fixed in llvm 9.
+You can set this with AFL_LLVM_NOT_ZERO=1
+See README.neverzero
+
+
+4) Gotchas, feedback, bugs
--------------------------
This is an early-stage mechanism, so field reports are welcome. You can send bug
reports to <afl-users@googlegroups.com>.
-4) Bonus feature #1: deferred instrumentation
----------------------------------------------
+5) Bonus feature #1: deferred initialization
+--------------------------------------------
AFL tries to optimize performance by executing the targeted binary just once,
stopping it just before main(), and then cloning this "master" process to get
@@ -129,7 +156,7 @@ will keep working normally when compiled with a tool other than afl-clang-fast.
Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will
*not* generate a deferred-initialization binary) - and you should be all set!
-5) Bonus feature #2: persistent mode
+6) Bonus feature #2: persistent mode
------------------------------------
Some libraries provide APIs that are stateless, or whose state can be reset in
@@ -169,7 +196,7 @@ PS. Because there are task switches still involved, the mode isn't as fast as
faster than the normal fork() model, and compared to in-process fuzzing,
should be a lot more robust.
-6) Bonus feature #3: new 'trace-pc-guard' mode
+8) Bonus feature #3: new 'trace-pc-guard' mode
----------------------------------------------
Recent versions of LLVM are shipping with a built-in execution tracing feature
@@ -178,10 +205,8 @@ post-process the assembly or install any compiler plugins. See:
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
-As of this writing, the feature is only available on SVN trunk, and is yet to
-make it to an official release of LLVM. Nevertheless, if you have a
-sufficiently recent compiler and want to give it a try, build afl-clang-fast
-this way:
+If you have a sufficiently recent compiler and want to give it a try, build
+afl-clang-fast this way:
AFL_TRACE_PC=1 make clean all
diff --git a/llvm_mode/README.neverzero b/llvm_mode/README.neverzero
new file mode 100644
index 00000000..ef873acb
--- /dev/null
+++ b/llvm_mode/README.neverzero
@@ -0,0 +1,22 @@
+Usage
+=====
+
+In larger, complex or reiterative programs the map that collects the edge pairs
+can easily fill up and wrap.
+This is not that much of an issue - unless by chance it wraps just to a 0
+when the program execution ends.
+In this case afl-fuzz is not able to see that the pair has been accessed and
+will ignore it.
+
+NeverZero prevents this behaviour. If a counter wraps, it jumps over the 0
+directly to a 1. This improves path discovery (by a very little amount)
+at a very little cost (one instruction per edge).
+
+This is implemented in afl-gcc, however for llvm_mode this is optional if
+the llvm version is below 9 - as there is a perfomance bug that is only fixed
+in version 9 and onwards.
+
+If you want to enable this for llvm < 9 then set
+
+export AFL_LLVM_NOT_ZERO=1
+
diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c
index 1e2e04ea..a4bb7539 100644
--- a/llvm_mode/afl-clang-fast.c
+++ b/llvm_mode/afl-clang-fast.c
@@ -32,6 +32,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
static u8* obj_path; /* Path to runtime libraries */
static u8** cc_params; /* Parameters passed to the real CC */
@@ -87,7 +88,7 @@ static void find_obj(u8* argv0) {
return;
}
- FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH");
+ FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so.cc'. Please set AFL_PATH");
}
@@ -112,29 +113,29 @@ static void edit_params(u32 argc, char** argv) {
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
}
- /* There are two ways to compile afl-clang-fast. In the traditional mode, we
- use afl-llvm-pass.so to inject instrumentation. In the experimental
+ /* There are three ways to compile with afl-clang-fast. In the traditional
+ mode, we use afl-llvm-pass.so, then there is libLLVMInsTrim.so which is
+ much faster but has less coverage. Finally tere is the experimental
'trace-pc-guard' mode, we use native LLVM instrumentation callbacks
- instead. The latter is a very recent addition - see:
-
+ instead. For trace-pc-guard see:
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards */
// laf
- if (getenv("LAF_SPLIT_SWITCHES")) {
+ if (getenv("LAF_SPLIT_SWITCHES")||getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
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/split-switches-pass.so", obj_path);
}
- if (getenv("LAF_TRANSFORM_COMPARES")) {
+ if (getenv("LAF_TRANSFORM_COMPARES")||getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
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/compare-transform-pass.so", obj_path);
}
- if (getenv("LAF_SPLIT_COMPARES")) {
+ if (getenv("LAF_SPLIT_COMPARES")||getenv("AFL_LLVM_LAF_SPLIT_COMPARES")) {
cc_params[cc_par_cnt++] = "-Xclang";
cc_params[cc_par_cnt++] = "-load";
cc_params[cc_par_cnt++] = "-Xclang";
@@ -143,14 +144,18 @@ static void edit_params(u32 argc, char** argv) {
// /laf
#ifdef USE_TRACE_PC
- cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
- cc_params[cc_par_cnt++] = "-mllvm";
- cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
+ cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
+ //cc_params[cc_par_cnt++] = "-mllvm";
+ //cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
+ //cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
#else
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-pass.so", obj_path);
+ if (getenv("AFL_LLVM_INSTRIM") != NULL || getenv("INSTRIM_LIB") != NULL)
+ cc_params[cc_par_cnt++] = alloc_printf("%s/libLLVMInsTrim.so", obj_path);
+ else
+ cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
#endif /* ^USE_TRACE_PC */
cc_params[cc_par_cnt++] = "-Qunused-arguments";
@@ -246,6 +251,10 @@ static void edit_params(u32 argc, char** argv) {
}
+#ifdef USEMMAP
+ cc_params[cc_par_cnt++] = "-lrt";
+#endif
+
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc
index d46db7c0..cfeff968 100644
--- a/llvm_mode/afl-llvm-pass.so.cc
+++ b/llvm_mode/afl-llvm-pass.so.cc
@@ -118,6 +118,10 @@ bool AFLCoverage::runOnModule(Module &M) {
}
+#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. */
@@ -227,21 +231,69 @@ bool AFLCoverage::runOnModule(Module &M) {
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
- Value *MapPtrIdx =
- IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
+ Value *MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
/* Update bitmap */
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
- IRB.CreateStore(Incr, MapPtrIdx)
- ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+
+#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
+ /* hexcoder: Realize a counter that skips zero during overflow.
+ * Once this counter reaches its maximum value, it next increments to 1
+ *
+ * Instead of
+ * Counter + 1 -> Counter
+ * we inject now this
+ * Counter + 1 -> {Counter, OverflowFlag}
+ * Counter + OverflowFlag -> Counter
+ */
+/* // we keep the old solutions just in case
+ // Solution #1
+ if (neverZero_counters_str[0] == '1') {
+ CallInst *AddOv = IRB.CreateBinaryIntrinsic(Intrinsic::uadd_with_overflow, Counter, ConstantInt::get(Int8Ty, 1));
+ AddOv->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+ Value *SumWithOverflowBit = AddOv;
+ Incr = IRB.CreateAdd(IRB.CreateExtractValue(SumWithOverflowBit, 0), // sum
+ IRB.CreateZExt( // convert from one bit type to 8 bits type
+ IRB.CreateExtractValue(SumWithOverflowBit, 1), // overflow
+ Int8Ty));
+ // Solution #2
+ } else if (neverZero_counters_str[0] == '2') {
+ auto cf = IRB.CreateICmpEQ(Counter, ConstantInt::get(Int8Ty, 255));
+ Value *HowMuch = IRB.CreateAdd(ConstantInt::get(Int8Ty, 1), cf);
+ Incr = IRB.CreateAdd(Counter, HowMuch);
+ // Solution #3
+ } else if (neverZero_counters_str[0] == '3') {
+*/
+ // this is the solution we choose because llvm9 should do the right thing here
+ auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
+ auto carry = IRB.CreateZExt(cf, Int8Ty);
+ Incr = IRB.CreateAdd(Incr, carry);
+/*
+ // Solution #4
+ } else if (neverZero_counters_str[0] == '4') {
+ auto cf = IRB.CreateICmpULT(Incr, ConstantInt::get(Int8Ty, 1));
+ auto carry = IRB.CreateZExt(cf, Int8Ty);
+ Incr = IRB.CreateAdd(Incr, carry);
+ } else {
+ fprintf(stderr, "Error: unknown value for AFL_NZERO_COUNTS: %s (valid is 1-4)\n", neverZero_counters_str);
+ exit(-1);
+ }
+*/
+#if LLVM_VERSION_MAJOR < 9
+ }
+#endif
+
+ IRB.CreateStore(Incr, MapPtrIdx)->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
/* Set prev_loc to cur_loc >> 1 */
- StoreInst *Store =
- IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
+ StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
inst_blocks++;
diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c
index 342dcc90..debde204 100644
--- a/llvm_mode/afl-llvm-rt.o.c
+++ b/llvm_mode/afl-llvm-rt.o.c
@@ -44,6 +44,9 @@
# define CONST_PRIO 0
#endif /* ^USE_TRACE_PC */
+#include <sys/mman.h>
+#include <fcntl.h>
+
/* Globals needed by the injected instrumentation. The __afl_area_initial region
is used for instrumentation output before __afl_map_shm() has a chance to run.
@@ -71,10 +74,34 @@ static void __afl_map_shm(void) {
hacky .init code to work correctly in projects such as OpenSSL. */
if (id_str) {
+#ifdef USEMMAP
+ const char *shm_file_path = id_str;
+ int shm_fd = -1;
+ unsigned char *shm_base = NULL;
+
+ /* create the shared memory segment as if it was a file */
+ shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
+ if (shm_fd == -1) {
+ printf("shm_open() failed\n");
+ exit(1);
+ }
+
+ /* map the shared memory segment to the address space of the process */
+ shm_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
+ if (shm_base == MAP_FAILED) {
+ close(shm_fd);
+ shm_fd = -1;
+ printf("mmap() failed\n");
+ exit(2);
+ }
+
+ __afl_area_ptr = shm_base;
+#else
u32 shm_id = atoi(id_str);
__afl_area_ptr = shmat(shm_id, NULL, 0);
+#endif
/* Whooooops. */
diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc
index d9a1f945..d0dbe8ec 100644
--- a/llvm_mode/compare-transform-pass.so.cc
+++ b/llvm_mode/compare-transform-pass.so.cc
@@ -257,6 +257,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
std::vector<Value *> args;
args.push_back(load);
load = IRB.CreateCall(tolowerFn, args, "tmp");
+ load = IRB.CreateTrunc(load, Int8Ty);
}
Value *isub;
if (HasStr1)
@@ -272,14 +273,9 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
next_bb = BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb);
BranchInst::Create(end_bb, next_bb);
-#if LLVM_VERSION_MAJOR < 8
- TerminatorInst *term = cur_bb->getTerminator();
-#else
- Instruction *term = cur_bb->getTerminator();
-#endif
Value *icmp = IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0));
IRB.CreateCondBr(icmp, next_bb, end_bb);
- term->eraseFromParent();
+ cur_bb->getTerminator()->eraseFromParent();
} else {
//IRB.CreateBr(end_bb);
}
@@ -304,7 +300,8 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
bool CompareTransform::runOnModule(Module &M) {
- llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, extended by heiko@hexco.de\n";
+ if (getenv("AFL_QUIET") == NULL)
+ llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, extended by heiko@hexco.de\n";
transformCmps(M, true, true, true, true, true);
verifyModule(M);
diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc
index 25ccb3b4..c025628f 100644
--- a/llvm_mode/split-compares-pass.so.cc
+++ b/llvm_mode/split-compares-pass.so.cc
@@ -477,6 +477,8 @@ bool SplitComparesTransform::runOnModule(Module &M) {
int bitw = 64;
char* bitw_env = getenv("LAF_SPLIT_COMPARES_BITW");
+ if (!bitw_env)
+ bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW");
if (bitw_env) {
bitw = atoi(bitw_env);
}
@@ -485,7 +487,8 @@ bool SplitComparesTransform::runOnModule(Module &M) {
simplifySignedness(M);
- errs() << "Split-compare-pass by laf.intel@gmail.com\n";
+ if (getenv("AFL_QUIET") == NULL)
+ errs() << "Split-compare-pass by laf.intel@gmail.com\n";
switch (bitw) {
case 64:
diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc
index 4c28f34c..1ace3185 100644
--- a/llvm_mode/split-switches-pass.so.cc
+++ b/llvm_mode/split-switches-pass.so.cc
@@ -244,7 +244,8 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
/* If there is only the default destination or the condition checks 8 bit or less, don't bother with the code below. */
if (!SI->getNumCases() || bitw <= 8) {
- errs() << "skip trivial switch..\n";
+ if (getenv("AFL_QUIET") == NULL)
+ errs() << "skip trivial switch..\n";
continue;
}
@@ -302,7 +303,8 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
bool SplitSwitchesTransform::runOnModule(Module &M) {
- llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n";
+ if (getenv("AFL_QUIET") == NULL)
+ llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n";
splitSwitches(M);
verifyModule(M);