#include "common-llvm.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef _WIN32
#include <unistd.h>
#include <sys/time.h>
#else
#include <io.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <list>
#include <string>
#include <fstream>
#include "llvm/Support/CommandLine.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/FormatVariadic.h"
#if LLVM_VERSION_MAJOR >= 14 && !defined(USE_NEW_PM)
#include "llvm/Pass.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
typedef uint32_t prev_loc_t;
#define NGRAM_SIZE_MAX 16U
#define CTX_MAX_K 32U
#define MAP_SIZE LIBAFL_EDGES_MAP_SIZE
using namespace llvm;
static cl::opt<bool> Debug("debug", cl::desc("Debug prints"), cl::init(false),
cl::NotHidden);
static cl::opt<uint32_t> InstRatio(
"inst_ratio", cl::desc("Instrumentation ratio in percentage"),
cl::init(100), cl::NotHidden);
static cl::opt<bool> NotZero("not_zero",
cl::desc("Never hit 0 again in the hitcount"),
cl::init(true), cl::NotHidden);
static cl::opt<uint32_t> Ngram(
"ngram", cl::desc("Size of the Ngram instrumentation (0 to disable)"),
cl::init(0), cl::NotHidden);
static cl::opt<uint32_t> CtxK(
"ctx_k",
cl::desc(
"Size of the context for K-Ctx context sensitivity (0 to disable)"),
cl::init(0), cl::NotHidden);
static cl::opt<bool> Ctx("ctx",
cl::desc("Enable full context sensitive coverage"),
cl::init(false), cl::NotHidden);
static cl::opt<bool> ThreadSafe("thread_safe",
cl::desc("Use the thread safe instrumentation"),
cl::init(false), cl::NotHidden);
static cl::opt<bool> DumpCFG(
"dump_afl_cfg", cl::desc("Dump CFG containing AFL-style edge index"),
cl::init(false), cl::NotHidden);
static cl::opt<std::string> DumpCFGPath(
"dump_afl_cfg_path",
cl::desc("Path to dump CFG containing AFL-style edge index"),
cl::init(".cfg"), cl::NotHidden);
namespace {
#ifdef USE_NEW_PM
class AFLCoverage : public PassInfoMixin<AFLCoverage> {
public:
AFLCoverage() {
#else
class AFLCoverage : public ModulePass {
public:
static char ID;
AFLCoverage() : ModulePass(ID) {
#endif
}
#ifdef USE_NEW_PM
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
#else
bool runOnModule(Module &M) override;
#endif
protected:
uint32_t map_size = MAP_SIZE;
uint32_t function_minimum_size = 1;
DenseMap<BasicBlock *, int32_t> bb_to_cur_loc;
DenseMap<StringRef, BasicBlock *> entry_bb;
};
}
#ifdef USE_NEW_PM
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "AFLCoverage", "v0.1",
[](PassBuilder &PB) {
#if 1
#if LLVM_VERSION_MAJOR <= 13
using OptimizationLevel = typename PassBuilder::OptimizationLevel;
#endif
PB.registerOptimizerLastEPCallback(
[](ModulePassManager &MPM, OptimizationLevel OL) {
MPM.addPass(AFLCoverage());
});
#else #endif
}};
}
#else
char AFLCoverage::ID = 0;
#endif
#ifdef USE_NEW_PM
PreservedAnalyses AFLCoverage::run(Module &M, ModuleAnalysisManager &MAM) {
#else
bool AFLCoverage::runOnModule(Module &M) {
#endif
if (Ctx && DumpCFG) {
FATAL(
"Does not support dumping CFG with full context sensitive coverage "
"enabled.");
}
LLVMContext &C = M.getContext();
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
#ifdef HAVE_VECTOR_INTRINSICS
IntegerType *IntLocTy =
IntegerType::getIntNTy(C, sizeof(prev_loc_t) * CHAR_BIT);
#endif
uint32_t rand_seed;
unsigned int cur_loc = 0;
#ifdef USE_NEW_PM
auto PA = PreservedAnalyses::all();
#endif
rand_seed = time(NULL);
srand(rand_seed);
if (!InstRatio || InstRatio > 100) {
FATAL("Bad value of the instrumentation ratio (must be between 1 and 100)");
}
unsigned PrevLocSize = 0;
unsigned PrevCallerSize = 0;
bool instrument_ctx = Ctx || CtxK > 0;
bool instrument_caller = false;
#ifdef HAVE_VECTOR_INTRINSICS
VectorType *PrevLocTy = NULL;
if (Ngram && (Ngram < 2 || Ngram > NGRAM_SIZE_MAX)) {
FATAL(
"Bad value of the Ngram size (must be between 2 and NGRAM_SIZE_MAX "
"(%u))",
NGRAM_SIZE_MAX);
}
if (Ngram) {
PrevLocSize = Ngram - 1;
} else {
PrevLocSize = 1;
}
VectorType *PrevCallerTy = NULL;
if (CtxK > CTX_MAX_K) {
FATAL(
"Bad value of K for K-context sensitivity (must be between 1 and "
"CTX_MAX_K (%u))",
CTX_MAX_K);
}
if (CtxK == 1) {
CtxK = 0;
instrument_ctx = true;
instrument_caller = true; }
if (CtxK) {
PrevCallerSize = CtxK;
instrument_ctx = true;
}
#else
if (Ngram)
#ifndef LLVM_VERSION_PATCH
FATAL(
"Sorry, NGRAM branch coverage is not supported with llvm version "
"%d.%d.%d!",
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0);
#else
FATAL(
"Sorry, NGRAM branch coverage is not supported with llvm version "
"%d.%d.%d!",
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH);
#endif
if (CtxK)
#ifndef LLVM_VERSION_PATCH
FATAL(
"Sorry, K-CTX branch coverage is not supported with llvm version "
"%d.%d.%d!",
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0);
#else
FATAL(
"Sorry, K-CTX branch coverage is not supported with llvm version "
"%d.%d.%d!",
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH);
#endif
PrevLocSize = 1;
#endif
#ifdef HAVE_VECTOR_INTRINSICS
int PrevLocVecSize = PowerOf2Ceil(PrevLocSize);
if (Ngram)
PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize
#if LLVM_VERSION_MAJOR >= 12
,
false
#endif
);
#endif
#ifdef HAVE_VECTOR_INTRINSICS
int PrevCallerVecSize = PowerOf2Ceil(PrevCallerSize);
if (CtxK)
PrevCallerTy = VectorType::get(IntLocTy, PrevCallerVecSize
#if LLVM_VERSION_MAJOR >= 12
,
false
#endif
);
#endif
GlobalVariable *AFLMapPtr =
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
GlobalVariable *AFLPrevLoc;
GlobalVariable *AFLPrevCaller;
GlobalVariable *AFLContext = NULL;
if (Ctx || instrument_caller)
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
#else
AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx", 0,
GlobalVariable::GeneralDynamicTLSModel, 0, false);
#endif
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram)
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLPrevLoc = new GlobalVariable(
M, PrevLocTy, false, GlobalValue::ExternalLinkage,
nullptr, "__afl_prev_loc");
#else
AFLPrevLoc = new GlobalVariable(
M, PrevLocTy, false, GlobalValue::ExternalLinkage,
nullptr, "__afl_prev_loc",
nullptr, GlobalVariable::GeneralDynamicTLSModel,
0, false);
#endif
else
#endif
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLPrevLoc = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
#else
AFLPrevLoc = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0,
GlobalVariable::GeneralDynamicTLSModel, 0, false);
#endif
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK)
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLPrevCaller = new GlobalVariable(
M, PrevCallerTy, false, GlobalValue::ExternalLinkage,
nullptr, "__afl_prev_caller");
#else
AFLPrevCaller = new GlobalVariable(
M, PrevCallerTy, false, GlobalValue::ExternalLinkage,
nullptr, "__afl_prev_caller",
nullptr, GlobalVariable::GeneralDynamicTLSModel,
0, false);
#endif
else
#endif
#if defined(__ANDROID__) || defined(__HAIKU__)
AFLPrevCaller =
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
"__afl_prev_caller");
#else
AFLPrevCaller = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller",
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
#endif
#ifdef HAVE_VECTOR_INTRINSICS
SmallVector<Constant *, 32> PrevLocShuffle = {UndefValue::get(Int32Ty)};
for (unsigned I = 0; I < PrevLocSize - 1; ++I) {
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I));
}
for (int I = PrevLocSize; I < PrevLocVecSize; ++I) {
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize));
}
Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle);
Constant *PrevCallerShuffleMask = NULL;
SmallVector<Constant *, 32> PrevCallerShuffle = {UndefValue::get(Int32Ty)};
if (CtxK) {
for (unsigned I = 0; I < PrevCallerSize - 1; ++I) {
PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, I));
}
for (int I = PrevCallerSize; I < PrevCallerVecSize; ++I) {
PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, PrevCallerSize));
}
PrevCallerShuffleMask = ConstantVector::get(PrevCallerShuffle);
}
#endif
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
Value *PrevCtx = NULL; LoadInst *PrevCaller = NULL;
int inst_blocks = 0;
for (auto &F : M) {
int has_calls = 0;
if (Debug)
fprintf(stderr, "FUNCTION: %s (%zu)\n", F.getName().str().c_str(),
F.size());
if (F.size() < function_minimum_size) { continue; }
if (DumpCFG) { entry_bb[F.getName()] = &F.getEntryBlock(); }
std::list<Value *> todo;
for (auto &BB : F) {
BasicBlock::iterator IP = BB.getFirstInsertionPt();
IRBuilder<> IRB(&(*IP));
if (instrument_ctx && &BB == &F.getEntryBlock()) {
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK) {
PrevCaller = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
PrevCallerTy,
#endif
AFLPrevCaller);
PrevCaller->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
PrevCtx =
IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty());
} else
#endif
{
LoadInst *PrevCtxLoad = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
IRB.getInt32Ty(),
#endif
AFLContext);
PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
PrevCtx = PrevCtxLoad;
}
for (auto &BB_2 : F) {
if (has_calls) { break; }
for (auto &IN : BB_2) {
CallInst *callInst = nullptr;
if ((callInst = dyn_cast<CallInst>(&IN))) {
Function *Callee = callInst->getCalledFunction();
if (!Callee || Callee->size() < function_minimum_size)
continue;
else {
has_calls = 1;
break;
}
}
}
}
if (has_calls) {
Value *NewCtx = ConstantInt::get(Int32Ty, RandBelow(map_size));
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK) {
Value *ShuffledPrevCaller = IRB.CreateShuffleVector(
PrevCaller, UndefValue::get(PrevCallerTy),
PrevCallerShuffleMask);
Value *UpdatedPrevCaller = IRB.CreateInsertElement(
ShuffledPrevCaller, NewCtx, (uint64_t)0);
StoreInst *Store =
IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller);
Store->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
} else
#endif
{
if (Ctx) { NewCtx = IRB.CreateXor(PrevCtx, NewCtx); }
StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
}
if (RandBelow(100) >= InstRatio) { continue; }
cur_loc = RandBelow(map_size);
if (DumpCFG) { bb_to_cur_loc[&BB] = cur_loc; }
#if !(LLVM_VERSION_MAJOR == 6 && LLVM_VERSION_MINOR == 0) || !defined __linux__
int more_than_one = -1;
for (pred_iterator PI = pred_begin(&BB), E = pred_end(&BB); PI != E;
++PI) {
BasicBlock *Pred = *PI;
int count = 0;
if (more_than_one == -1) { more_than_one = 0; }
for (succ_iterator SI = succ_begin(Pred), E = succ_end(Pred); SI != E;
++SI) {
BasicBlock *Succ = *SI;
if (Succ != NULL) { count++; }
}
if (count > 1) { more_than_one = 1; }
}
if (F.size() > 1 && more_than_one != 1) {
if (instrument_ctx && has_calls) {
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
IRBuilder<> Post_IRB(Inst);
StoreInst *RestoreCtx;
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK)
RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller);
else
#endif
RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
continue;
}
#endif
ConstantInt *CurLoc;
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram)
CurLoc = ConstantInt::get(IntLocTy, cur_loc);
else
#endif
CurLoc = ConstantInt::get(Int32Ty, cur_loc);
LoadInst *PrevLoc;
if (Ngram) {
PrevLoc = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
PrevLocTy,
#endif
AFLPrevLoc);
} else {
PrevLoc = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
IRB.getInt32Ty(),
#endif
AFLPrevLoc);
}
PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *PrevLocTrans;
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram)
PrevLocTrans =
IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty());
else
#endif
PrevLocTrans = PrevLoc;
if (instrument_ctx)
PrevLocTrans =
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty);
else
PrevLocTrans = IRB.CreateZExt(PrevLocTrans, IRB.getInt32Ty());
LoadInst *MapPtr = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
PointerType::get(Int8Ty, 0),
#endif
AFLMapPtr);
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *MapPtrIdx;
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram)
MapPtrIdx = IRB.CreateGEP(
#if LLVM_VERSION_MAJOR >= 14
Int8Ty,
#endif
MapPtr,
IRB.CreateZExt(
IRB.CreateXor(PrevLocTrans, IRB.CreateZExt(CurLoc, Int32Ty)),
Int32Ty));
else
#endif
MapPtrIdx = IRB.CreateGEP(
#if LLVM_VERSION_MAJOR >= 14
Int8Ty,
#endif
MapPtr, IRB.CreateXor(PrevLocTrans, CurLoc));
if (ThreadSafe) {
IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, One,
#if LLVM_VERSION_MAJOR >= 13
llvm::MaybeAlign(1),
#endif
llvm::AtomicOrdering::Monotonic);
} else {
LoadInst *Counter = IRB.CreateLoad(
#if LLVM_VERSION_MAJOR >= 14
IRB.getInt8Ty(),
#endif
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) { #else
if (NotZero) {
#endif
ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
auto cf = IRB.CreateICmpEQ(Incr, Zero);
auto carry = IRB.CreateZExt(cf, Int8Ty);
Incr = IRB.CreateAdd(Incr, carry);
}
IRB.CreateStore(Incr, MapPtrIdx)
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
}
StoreInst *Store;
#ifdef HAVE_VECTOR_INTRINSICS
if (Ngram) {
Value *ShuffledPrevLoc = IRB.CreateShuffleVector(
PrevLoc, UndefValue::get(PrevLocTy), PrevLocShuffleMask);
Value *UpdatedPrevLoc = IRB.CreateInsertElement(
ShuffledPrevLoc, IRB.CreateLShr(CurLoc, (uint64_t)1), (uint64_t)0);
Store = IRB.CreateStore(UpdatedPrevLoc, AFLPrevLoc);
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
} else
#endif
{
Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1),
AFLPrevLoc);
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
}
if (instrument_ctx && has_calls) {
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
IRBuilder<> Post_IRB(Inst);
StoreInst *RestoreCtx;
#ifdef HAVE_VECTOR_INTRINSICS
if (CtxK)
RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller);
else
#endif
RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
MDNode::get(C, None));
}
}
inst_blocks++;
}
}
if (DumpCFG) {
int fd;
#ifndef _WIN32
if ((fd = open(DumpCFGPath.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644)) <
0)
#else
if ((fd = _open(DumpCFGPath.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644)) <
0)
#endif
FATAL("Could not open/create CFG dump file.");
std::string cfg = "";
for (auto record = entry_bb.begin(); record != entry_bb.end(); record++) {
cfg += formatv("$${0}+{1}\n", record->getFirst(),
bb_to_cur_loc[record->getSecond()]);
}
for (auto record = bb_to_cur_loc.begin(); record != bb_to_cur_loc.end();
record++) {
auto current_bb = record->getFirst();
Function *calling_func = current_bb->getParent();
if (calling_func) {
auto function_name = calling_func->getName().str();
cfg += formatv("%%{0}", function_name);
} else
cfg += "%%__";
auto current_cur_loc = record->getSecond();
cfg += formatv("+{0}\n", current_cur_loc);
for (auto bb_successor = succ_begin(current_bb);
bb_successor != succ_end(current_bb); bb_successor++) {
cfg += formatv("->{0}\n", bb_to_cur_loc[*bb_successor]).str();
}
}
if (Debug) { errs() << "CFG: \n" << cfg; }
if (cfg.size() > 0 && write(fd, cfg.c_str(), cfg.length()) <= 0)
FATAL("Failed to dump CFG.\n");
}
if (Debug) {
if (!inst_blocks)
fprintf(stderr, "No instrumentation targets found.\n");
else
fprintf(stderr, "Instrumented %d locations (ratio %u%%).\n", inst_blocks,
(unsigned)InstRatio);
}
#ifdef USE_NEW_PM
return PA;
#else
return true;
#endif
}
#ifndef USE_NEW_PM
static void registerAFLPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new AFLCoverage());
}
static RegisterStandardPasses RegisterAFLPass(
PassManagerBuilder::EP_OptimizerLast, registerAFLPass);
static RegisterStandardPasses RegisterAFLPass0(
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
#endif