diff options
Diffstat (limited to 'instrumentation')
23 files changed, 2232 insertions, 1158 deletions
diff --git a/instrumentation/README.injections.md b/instrumentation/README.injections.md new file mode 100644 index 00000000..16cc3713 --- /dev/null +++ b/instrumentation/README.injections.md @@ -0,0 +1,48 @@ +# Injection fuzzing + +Coverage guided fuzzing so far is only able to detect crashes, so usually +memory corruption issues, or - if implemented by hand in the harness - +invariants. + +This is a proof-of-concept implementation to additionally hunt for injection +vulnerabilities. +It works by instrumenting calls to specific functions and parsing the +query parameter for a specific unescaped dictionary string, and if detected, +crashes the target. + +This has a very low false positive rate. +But obviously this can only find injection vulnerailities that are suspectible +to this specific (but most common) issue. Hence in a rare kind of injection +vulnerability this won't find the bug - and be a false negative. +But this can be tweaked by the user - see the HOW TO MODIFY section below. + +## How to use + +Set one or more of the following environment variables for **compiling** +the target and - *this is important* - when **fuzzing** the target: + + - `AFL_LLVM_INJECTIONS_SQL` + - `AFL_LLVM_INJECTIONS_LDAP` + - `AFL_LLVM_INJECTIONS_XSS` + +Alternatively you can set `AFL_LLVM_INJECTIONS_ALL` to enable all. + +## How to modify + +If you want to add more fuctions to check for e.g. SQL injections: +Add these to `instrumentation/injection-pass.cc` and recompile. + +If you want to test for more injection inputs: +Add the dictionary tokens to `src/afl-fuzz.c` and the check for them to +`instrumentation/afl-compiler-rt.o.c`. + +If you want to add new injection targets: +You will have to edit all three files. + +Just search for: +``` +// Marker: ADD_TO_INJECTIONS +``` +in the files to see where this needs to be added. + +**NOTE:** pull requests to improve this feature are highly welcome :-) diff --git a/instrumentation/README.llvm.md b/instrumentation/README.llvm.md index 126cf1a2..34b80c85 100644 --- a/instrumentation/README.llvm.md +++ b/instrumentation/README.llvm.md @@ -7,7 +7,7 @@ For the GCC-based instrumentation, see ## 1) Introduction -! llvm_mode works with llvm versions 3.8 up to 13 ! +! llvm_mode works with llvm versions 3.8 up to 17 - but 13+ is recommended ! The code in this directory allows you to instrument programs for AFL++ using true compiler-level instrumentation, instead of the more crude assembly-level diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md index df59cc2a..bd479c26 100644 --- a/instrumentation/README.lto.md +++ b/instrumentation/README.lto.md @@ -2,7 +2,7 @@ ## TL;DR: -This version requires a LLVM 11 or newer. +This version requires a LLVM 12 or newer. 1. Use afl-clang-lto/afl-clang-lto++ because the resulting binaries run slightly faster and give better coverage. @@ -10,7 +10,7 @@ This version requires a LLVM 11 or newer. 2. You can use it together with COMPCOV, COMPLOG and the instrument file listing features. -3. It only works with LLVM 11 or newer. +3. It only works with LLVM 12 or newer. 4. AUTODICTIONARY feature (see below) @@ -60,7 +60,7 @@ AUTODICTIONARY: 11 strings found [+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode). ``` -## Getting LLVM 11+ +## Getting LLVM 12+ ### Installing llvm @@ -73,7 +73,7 @@ chmod +x llvm.sh sudo ./llvm.sh 15 all ``` -LLVM 11 to 16 should be available in all current Linux repositories. +LLVM 12 to 18 should be available in all current Linux repositories. ## How to build afl-clang-lto @@ -277,7 +277,7 @@ AS=llvm-as ... afl-clang-lto is still work in progress. Known issues: -* Anything that LLVM 11+ cannot compile, afl-clang-lto cannot compile either - +* Anything that LLVM 12+ cannot compile, afl-clang-lto cannot compile either - obviously. * Anything that does not compile with LTO, afl-clang-lto cannot compile either - obviously. @@ -319,7 +319,7 @@ Still more problems came up though as this only works without bugs from LLVM 9 onwards, and with high optimization the link optimization ruins the instrumented control flow graph. -This is all now fixed with LLVM 11+. The llvm's own linker is now able to load +This is all now fixed with LLVM 12+. The llvm's own linker is now able to load passes and this bypasses all problems we had. Happy end :) diff --git a/instrumentation/README.persistent_mode.md b/instrumentation/README.persistent_mode.md index 14e59f4a..8e4f6ae4 100644 --- a/instrumentation/README.persistent_mode.md +++ b/instrumentation/README.persistent_mode.md @@ -195,4 +195,34 @@ Then as first line after the `__AFL_LOOP` while loop: int len = __AFL_FUZZ_TESTCASE_LEN; ``` -And that is all! \ No newline at end of file +And that is all! + +## 6) Persistent record, and replay + +If your software under test requires keeping a state between persistent loop iterations (i.e., a stateful network stack), you can use the `AFL_PERSISTENT_RECORD` variable as described in the [environment variables documentation](../docs/env_variables.md). + +When `AFL_PERSISTENT_RECORD` is enabled, replay functionality is also included in the compiler-rt library. To replay a specific record, assign the record number to the AFL_PERSISTENT_REPLAY environment variable (i.e., `RECORD:XXXXX`` -> `AFL_PERSISTENT_REPLAY=XXXXX`), and run the test binary as you would normally do. +The directory where the record files live can be specified via the `AFL_PERSISTENT_DIR` environment varilable, otherwise by default it will be considered the current directory (`./`). + +If your harness reads the input files from arguments using the special `@@` argument you will need to include support by enabling `AFL_PERSISTENT_ARGPARSE` in `config.h`. + +In order to offer transparent support to harnesses using the `@@` command line argument, arguments are parsed by the `__afl_record_replay_init` init function. Since not all systems support passing arguments to initializers, this functionality is disabled by default, it's recommendable to use the `__AFL_FUZZ_TESTCASE_BUF/__AFL_FUZZ_TESTCASE_LEN` shared memory mechanism instead. + +## 7) Drop-in persistent loop replay replacement + +To use the replay functionality without having to use `afl-cc`, include the [include/record_compat.h](../include/afl-record_compat.h) header file. Together with the [include/afl-persistent-replay.h](../include/afl-persistent-replay.h) header included in it, `afl-record-compat.h` provides a drop-in replacement for the persistent loop mechanism. + +```c +#ifndef __AFL_FUZZ_TESTCASE_LEN + // #define AFL_PERSISTENT_REPLAY_ARGPARSE + #include "afl-record-compat.h" +#endif + +__AFL_FUZZ_INIT(); +``` + +A simple example is provided in [persistent_demo_replay.c](../utils/replay_record/persistent_demo_replay.c). + +Be aware that the [afl-record-compat.h](../include/afl-record-compat.h) header should only be included in a single compilation unit, or you will end up with clobbered functions and variables. + +If you need a cleaner solution, you'll have to move the functions and variables defined in [include/record_compat.h](../include/afl-record-compat.h) and [include/afl-persistent-replay.h](../include/afl-persistent-replay.h) in a C file, and add the relevant declarations to a header file. After including the new header file, the compilation unit resulting from compiling the C file can then be linked with your program. \ No newline at end of file diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index b3b0d2cd..a09f28a9 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -192,12 +192,15 @@ class ModuleSanitizerCoverageLTO PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); private: - void instrumentFunction(Function &F, DomTreeCallback DTCallback, - PostDomTreeCallback PDTCallback); - void InjectCoverageForIndirectCalls(Function &F, - ArrayRef<Instruction *> IndirCalls); - bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks, - bool IsLeafFunc = true); + void instrumentFunction(Function &F, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + /* void InjectCoverageForIndirectCalls(Function &F, + ArrayRef<Instruction *> + IndirCalls);*/ + bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks, + bool IsLeafFunc = true); + bool Fake_InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks, + bool IsLeafFunc = true); GlobalVariable *CreateFunctionLocalArrayInSection(size_t NumElements, Function &F, Type *Ty, const char *Section); @@ -247,6 +250,9 @@ class ModuleSanitizerCoverageLTO uint32_t afl_global_id = 0; uint32_t unhandled = 0; uint32_t select_cnt = 0; + uint32_t instrument_ctx = 0; + uint32_t instrument_ctx_max_depth = 0; + uint32_t extra_ctx_inst = 0; uint64_t map_addr = 0; const char *skip_nozero = NULL; const char *use_threadsafe_counters = nullptr; @@ -257,11 +263,14 @@ class ModuleSanitizerCoverageLTO IntegerType *Int32Tyi = NULL; IntegerType *Int64Tyi = NULL; ConstantInt *Zero = NULL; + ConstantInt *Zero32 = NULL; ConstantInt *One = NULL; LLVMContext *Ct = NULL; Module *Mo = NULL; + GlobalVariable *AFLContext = NULL; GlobalVariable *AFLMapPtr = NULL; Value *MapPtrFixed = NULL; + AllocaInst *CTX_add = NULL; std::ofstream dFile; size_t found = 0; // AFL++ END @@ -420,16 +429,51 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( setvbuf(stdout, NULL, _IONBF, 0); if (getenv("AFL_DEBUG")) { debug = 1; } if (getenv("AFL_LLVM_DICT2FILE_NO_MAIN")) { autodictionary_no_main = 1; } + if (getenv("AFL_LLVM_CALLER") || getenv("AFL_LLVM_CTX") || + getenv("AFL_LLVM_LTO_CALLER") || getenv("AFL_LLVM_LTO_CTX")) { + + instrument_ctx = 1; + + } + + if (getenv("AFL_LLVM_LTO_CALLER_DEPTH")) { + + instrument_ctx_max_depth = atoi(getenv("AFL_LLVM_LTO_CALLER_DEPTH")); + + } else if (getenv("AFL_LLVM_LTO_CTX_DEPTH")) { + + instrument_ctx_max_depth = atoi(getenv("AFL_LLVM_LTO_CTX_DEPTH")); + + } else if (getenv("AFL_LLVM_CALLER_DEPTH")) { + + instrument_ctx_max_depth = atoi(getenv("AFL_LLVM_CALLER_DEPTH")); + + } else if (getenv("AFL_LLVM_CTX_DEPTH")) { + + instrument_ctx_max_depth = atoi(getenv("AFL_LLVM_CTX_DEPTH")); + + } if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { + char buf[64] = {}; + if (instrument_ctx) { + + snprintf(buf, sizeof(buf), " (CTX mode, depth %u)\n", + instrument_ctx_max_depth); + + } + SAYF(cCYA "afl-llvm-lto" VERSION cRST - " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); + "%s by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n", + buf); - } else + } else { be_quiet = 1; + } + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST"); @@ -442,7 +486,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { dFile.open(ptr, std::ofstream::out | std::ofstream::app); - if (dFile.is_open()) WARNF("Cannot access document file %s", ptr); + if (!dFile.is_open()) WARNF("Cannot access document file %s", ptr); } @@ -500,7 +544,12 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( } + AFLContext = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx", 0, + GlobalVariable::GeneralDynamicTLSModel, 0, false); + Zero = ConstantInt::get(Int8Tyi, 0); + Zero32 = ConstantInt::get(Int32Tyi, 0); One = ConstantInt::get(Int8Tyi, 1); initInstrumentList(); @@ -597,12 +646,12 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( } dictionary.push_back(std::string((char *)&val, len)); - found++; + ++found; if (val2) { dictionary.push_back(std::string((char *)&val2, len)); - found++; + ++found; } @@ -692,33 +741,37 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( * prototype */ FunctionType *FT = Callee->getFunctionType(); - isStrcmp &= FT->getNumParams() == 2 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()); - isStrcasecmp &= FT->getNumParams() == 2 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()); + isStrcmp &= + FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0); + isStrcasecmp &= + FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0); isMemcmp &= FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) && FT->getParamType(0)->isPointerTy() && FT->getParamType(1)->isPointerTy() && FT->getParamType(2)->isIntegerTy(); - isStrncmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - isStrncasecmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); + isStrncmp &= + FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= + FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) && + FT->getParamType(2)->isIntegerTy(); isStdString &= FT->getNumParams() >= 2 && FT->getParamType(0)->isPointerTy() && FT->getParamType(1)->isPointerTy(); @@ -746,12 +799,12 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( else Str2 = TmpStr.str(); - if (debug) + /*if (debug) fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, Str2P->getName().str().c_str(), Str2.c_str(), - HasStr2 == true ? "true" : "false"); + HasStr2 == true ? "true" : "false");*/ // we handle the 2nd parameter first because of llvm memcpy if (!HasStr2) { @@ -925,7 +978,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( '\0') { thestring.append("\0", 1); // add null byte - optLen++; + ++optLen; } @@ -1076,12 +1129,12 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( for (auto token : dictionary) { memlen += token.length(); - count++; + ++count; } if (!be_quiet) - printf("AUTODICTIONARY: %lu string%s found\n", count, + printf("AUTODICTIONARY: %zu string%s found\n", count, count == 1 ? "" : "s"); if (count) { @@ -1097,7 +1150,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( ptrhld.get()[offset++] = (uint8_t)token.length(); memcpy(ptrhld.get() + offset, token.c_str(), token.length()); offset += token.length(); - count++; + ++count; } @@ -1144,7 +1197,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( WARNF("No instrumentation targets found."); else { - char modeline[100]; + char modeline[128]; snprintf(modeline, sizeof(modeline), "%s%s%s%s%s%s", getenv("AFL_HARDEN") ? "hardened" : "non-hardened", getenv("AFL_USE_ASAN") ? ", ASAN" : "", @@ -1152,9 +1205,16 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( getenv("AFL_USE_TSAN") ? ", TSAN" : "", getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); - OKF("Instrumented %u locations (%u selects) without collisions (%llu " - "collisions have been avoided) (%s mode).", - inst, select_cnt, calculateCollisions(inst), modeline); + char buf[64] = {}; + if (instrument_ctx) { + + snprintf(buf, sizeof(buf), " with %u extra map entries for CTX", + extra_ctx_inst); + + } + + OKF("Instrumented %u locations (%u selects)%s (%s mode).", inst, + select_cnt, buf, modeline); } @@ -1235,13 +1295,68 @@ static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, } +/// return the number of calls to this function +u32 countCallers(Function *F) { + + u32 callers = 0; + + if (!F) { return 0; } + + for (auto *U : F->users()) { + + if (auto *CI = dyn_cast<CallInst>(U)) { + + ++callers; + (void)(CI); + + } + + } + + return callers; + +} + +/// return the calling function of a function - only if there is a single caller +Function *returnOnlyCaller(Function *F) { + + Function *caller = NULL; + + if (!F) { return NULL; } + + for (auto *U : F->users()) { + + if (auto *CI = dyn_cast<CallInst>(U)) { + + if (caller == NULL) { + + caller = CI->getParent()->getParent(); + + } else { + + return NULL; + + } + + } + + } + + return caller; + +} + void ModuleSanitizerCoverageLTO::instrumentFunction( Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { if (F.empty()) return; if (F.getName().find(".module_ctor") != std::string::npos) return; // Should not instrument sanitizer init functions. +#if LLVM_VERSION_MAJOR >= 18 + if (F.getName().starts_with("__sanitizer_")) +#else if (F.getName().startswith("__sanitizer_")) +#endif return; // Don't instrument __sanitizer_* callbacks. // Don't touch available_externally functions, their actual body is elsewhere. if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return; @@ -1264,6 +1379,37 @@ void ModuleSanitizerCoverageLTO::instrumentFunction( // AFL++ START if (!F.size()) return; + + LLVMContext &Context = F.getContext(); + MDNode *N = MDNode::get(Context, MDString::get(Context, "nosanitize")); + + if (instrument_ctx) { + + // we have to set __afl_ctx 0 for all indirect calls in all functions, even + // those not to be instrumented. + for (auto &BB : F) { + + for (auto &IN : BB) { + + if (auto *Call = dyn_cast<CallInst>(&IN)) { + + if (Call->isIndirectCall()) { + + IRBuilder<> Builder(IN.getContext()); + Builder.SetInsertPoint(IN.getParent(), IN.getIterator()); + StoreInst *StoreCtx = Builder.CreateStore(Zero32, AFLContext); + StoreCtx->setMetadata("nosanitize", N); + + } + + } + + } + + } + + } + if (!isInInstrumentList(&F, FMNAME)) return; // AFL++ END @@ -1277,11 +1423,297 @@ void ModuleSanitizerCoverageLTO::instrumentFunction( const PostDominatorTree *PDT = PDTCallback(F); bool IsLeafFunc = true; uint32_t skip_next = 0; + uint32_t call_counter = 0, call_depth = 0; + uint32_t inst_save = inst, save_global = afl_global_id; + uint32_t inst_in_this_func = 0; + Function *caller = NULL; + LoadInst *PrevCtxLoad = NULL; + + CTX_add = NULL; + + if (debug) fprintf(stderr, "Function: %s\n", F.getName().str().c_str()); + + if (instrument_ctx) { + + caller = &F; + call_counter = countCallers(caller); + Function *callee = caller; + + if (call_counter == 1 && instrument_ctx_max_depth) { + + ++call_depth; + + while (instrument_ctx_max_depth >= call_depth && + ((caller = returnOnlyCaller(callee)) || 1 == 1) && + (call_counter = countCallers(callee)) == 1) { + + if (debug && caller && callee) + fprintf(stderr, "DEBUG: another depth: %s <- %s [%u]\n", + callee->getName().str().c_str(), + caller->getName().str().c_str(), call_depth); + ++call_depth; + callee = caller; + + } + + if (!caller && callee) { + + caller = callee; + if (debug) + fprintf(stderr, "DEBUG: depth found: %s <- %s [count=%u, depth=%u]\n", + caller->getName().str().c_str(), F.getName().str().c_str(), + call_counter, call_depth); + + } + + } + + if (debug && call_counter < 2) { + + fprintf(stderr, "Function %s only %u (%s)\n", F.getName().str().c_str(), + call_counter, caller->getName().str().c_str()); + + } + + if (call_counter == 1) { + + call_counter = 0; + caller = NULL; + + } + + if (debug) { + + fprintf(stderr, "DEBUG: result: Function=%s callers=%u depth=%u\n", + F.getName().str().c_str(), call_counter, call_depth); + + } + + if (call_counter > 1) { + + // Fake instrumentation so we can count how many instrumentations there + // will be in this function + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast<CallInst>(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + StringRef FuncName = Callee->getName(); + + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) + continue; + + ++inst; + + } + + SelectInst *selectInst = nullptr; + + if ((selectInst = dyn_cast<SelectInst>(&IN))) { + + Value *condition = selectInst->getCondition(); + auto t = condition->getType(); + + if (t->getTypeID() == llvm::Type::IntegerTyID) { + + inst += 2; + + } else + +#if LLVM_VERSION_MAJOR >= 14 + if (t->getTypeID() == llvm::Type::FixedVectorTyID) { + + FixedVectorType *tt = dyn_cast<FixedVectorType>(t); + if (tt) { + + uint32_t elements = tt->getElementCount().getFixedValue(); + inst += elements * 2; + + } + + } else + +#endif + { + + continue; + + } + + } + + } + + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) + BlocksToInstrument.push_back(&BB); + + } + + Fake_InjectCoverage(F, BlocksToInstrument, IsLeafFunc); + + if (debug) + fprintf(stderr, "DEBUG: CTX: %u instrumentations\n", inst - inst_save); + + // we only instrument functions that have more than one instrumented block + if (inst > inst_save + 1) { + + inst_in_this_func = inst - inst_save; + bool done = false; + + // in rare occasions there can be multiple entry points per function + for (auto &BB : F) { + + if (&BB == &F.getEntryBlock() && done == false) { + + // we insert a CTX value in all our callers: + IRBuilder<> Builder(Context); + CallInst *CI = NULL; + Function *F2 = NULL; + uint32_t instrumented_calls = 0; + + for (auto *U : caller->users()) { + + if ((CI = dyn_cast<CallInst>(U))) { + + F2 = CI->getParent()->getParent(); + if (debug) + fprintf(stderr, + "DEBUG: CTX call insert %s [%u/%u] -> %s/%s\n", + F2->getName().str().c_str(), instrumented_calls + 1, + call_counter, caller->getName().str().c_str(), + F.getName().str().c_str()); + + Builder.SetInsertPoint(CI); + StoreInst *StoreCtx = Builder.CreateStore( + ConstantInt::get(Type::getInt32Ty(Context), + instrumented_calls++), + AFLContext); + StoreCtx->setMetadata("nosanitize", N); + + } + + } + + if (instrumented_calls != call_counter) { + + fprintf(stderr, "BUG! %s/%s <=> %u vs %u\n", + caller->getName().str().c_str(), + F.getName().str().c_str(), instrumented_calls, + call_counter); + exit(-1); + + } + + done = true; + + } + + // in all entrypoints we have to load the CTX value + if (&BB == &F.getEntryBlock()) { + + Value *CTX_offset; + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + PrevCtxLoad = IRB.CreateLoad( +#if LLVM_VERSION_MAJOR >= 14 + IRB.getInt32Ty(), +#endif + AFLContext); + PrevCtxLoad->setMetadata("nosanitize", N); + + CTX_offset = IRB.CreateMul( + ConstantInt::get(Type::getInt32Ty(Context), inst_in_this_func), + PrevCtxLoad, "CTXmul", false, true); + + CTX_add = + IRB.CreateAlloca(Type::getInt32Ty(Context), nullptr, "CTX_add"); + auto nosan = IRB.CreateStore(CTX_offset, CTX_add); + nosan->setMetadata("nosanitize", N); + + if (debug) + fprintf( + stderr, "DEBUG: extra CTX instrumentations for %s: %u * %u\n", + F.getName().str().c_str(), inst - inst_save, call_counter); + + } + + for (auto &IN : BB) { + + // check all calls and where callee count == 1 instrument + // our current caller_id to __afl_ctx + if (auto callInst = dyn_cast<CallInst>(&IN)) { + + Function *Callee = callInst->getCalledFunction(); + if (countCallers(Callee) == 1) { + + if (debug) + fprintf(stderr, "DEBUG: %s call to %s with only one caller\n", + F.getName().str().c_str(), + Callee->getName().str().c_str()); + + IRBuilder<> Builder(IN.getContext()); + Builder.SetInsertPoint(callInst); + StoreInst *StoreCtx = + Builder.CreateStore(PrevCtxLoad, AFLContext); + StoreCtx->setMetadata("nosanitize", N); + + } + + } + + } + + } + + } + + } + + inst = inst_save; + + /* if (debug) + fprintf(stderr, "Next instrumentation (%u-%u=%u %u-%u=%u)\n", inst, + inst_save, inst - inst_save, afl_global_id, save_global, + afl_global_id - save_global);*/ + + } for (auto &BB : F) { + skip_next = 0; + + /* + uint32_t j = 0; + fprintf(stderr, "BB %p ============================================\n", + CTX_add);*/ + for (auto &IN : BB) { + /* j++; + uint32_t i = 1; + std::string errMsg; + raw_string_ostream os(errMsg); + IN.print(os); + fprintf(stderr, "Next instruction, BB size now %zu: %02u %s\n", + BB.size(), j, os.str().c_str()); for (auto &IN2 : BB) { + + std::string errMsg2; + raw_string_ostream os2(errMsg2); + IN2.print(os2); + fprintf( + stderr, "%s %02u: %s\n", + strcmp(os.str().c_str(), os2.str().c_str()) == 0 ? ">>>" : " + ", i++, os2.str().c_str()); + + }*/ + CallInst *callInst = nullptr; if ((callInst = dyn_cast<CallInst>(&IN))) { @@ -1305,6 +1737,19 @@ void ModuleSanitizerCoverageLTO::instrumentFunction( if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; Value *val = ConstantInt::get(Int32Ty, ++afl_global_id); + if (CTX_add) { + + IRBuilder<> Builder(Context); + LoadInst *CTX_load = Builder.CreateLoad( +#if LLVM_VERSION_MAJOR >= 14 + Builder.getInt32Ty(), +#endif + CTX_add); + ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(CTX_load); + val = Builder.CreateAdd(val, CTX_load); + + } + callInst->setOperand(1, val); ++inst; @@ -1312,164 +1757,228 @@ void ModuleSanitizerCoverageLTO::instrumentFunction( SelectInst *selectInst = nullptr; - /* - std::string errMsg; - raw_string_ostream os(errMsg); - IN.print(os); - fprintf(stderr, "X(%u): %s\n", skip_next, os.str().c_str()); - */ - if (!skip_next && (selectInst = dyn_cast<SelectInst>(&IN))) { + if ((selectInst = dyn_cast<SelectInst>(&IN))) { - uint32_t vector_cnt = 0; - Value *condition = selectInst->getCondition(); - Value *result; - auto t = condition->getType(); - IRBuilder<> IRB(selectInst->getNextNode()); + if (!skip_next) { - ++select_cnt; + // fprintf(stderr, "Select in\n"); - if (t->getTypeID() == llvm::Type::IntegerTyID) { + uint32_t vector_cnt = 0; + Value *condition = selectInst->getCondition(); + Value *result; + auto t = condition->getType(); + IRBuilder<> IRB(selectInst->getNextNode()); - Value *val1 = ConstantInt::get(Int32Ty, ++afl_global_id); - Value *val2 = ConstantInt::get(Int32Ty, ++afl_global_id); - result = IRB.CreateSelect(condition, val1, val2); - skip_next = 1; - inst += 2; + ++select_cnt; + + if (t->getTypeID() == llvm::Type::IntegerTyID) { - } else + Value *val1 = ConstantInt::get(Int32Ty, ++afl_global_id); + Value *val2 = ConstantInt::get(Int32Ty, ++afl_global_id); + if (CTX_add) { + LoadInst *CTX_load = IRB.CreateLoad( #if LLVM_VERSION_MAJOR >= 14 - if (t->getTypeID() == llvm::Type::FixedVectorTyID) { + IRB.getInt32Ty(), +#endif + CTX_add); + val1 = IRB.CreateAdd(val1, CTX_load); + val2 = IRB.CreateAdd(val2, CTX_load); - FixedVectorType *tt = dyn_cast<FixedVectorType>(t); - if (tt) { + } - uint32_t elements = tt->getElementCount().getFixedValue(); - vector_cnt = elements; - inst += vector_cnt * 2; - if (elements) { + result = IRB.CreateSelect(condition, val1, val2); + skip_next = 1; + inst += 2; - FixedVectorType *GuardPtr1 = - FixedVectorType::get(Int32Ty, elements); - FixedVectorType *GuardPtr2 = - FixedVectorType::get(Int32Ty, elements); - Value *x, *y; + } else - Value *val1 = ConstantInt::get(Int32Ty, ++afl_global_id); - Value *val2 = ConstantInt::get(Int32Ty, ++afl_global_id); - x = IRB.CreateInsertElement(GuardPtr1, val1, (uint64_t)0); - y = IRB.CreateInsertElement(GuardPtr2, val2, (uint64_t)0); +#if LLVM_VERSION_MAJOR >= 14 + if (t->getTypeID() == llvm::Type::FixedVectorTyID) { - for (uint64_t i = 1; i < elements; i++) { + FixedVectorType *tt = dyn_cast<FixedVectorType>(t); + if (tt) { - val1 = ConstantInt::get(Int32Ty, ++afl_global_id); - val2 = ConstantInt::get(Int32Ty, ++afl_global_id); - x = IRB.CreateInsertElement(GuardPtr1, val1, i); - y = IRB.CreateInsertElement(GuardPtr2, val2, i); + uint32_t elements = tt->getElementCount().getFixedValue(); + vector_cnt = elements; + inst += vector_cnt * 2; + if (elements) { - } + FixedVectorType *GuardPtr1 = + FixedVectorType::get(Int32Ty, elements); + FixedVectorType *GuardPtr2 = + FixedVectorType::get(Int32Ty, elements); + Value *x, *y; - result = IRB.CreateSelect(condition, x, y); - skip_next = 1; + Value *val1 = ConstantInt::get(Int32Ty, ++afl_global_id); + Value *val2 = ConstantInt::get(Int32Ty, ++afl_global_id); + if (CTX_add) { - } + LoadInst *CTX_load = IRB.CreateLoad( + #if LLVM_VERSION_MAJOR >= 14 + IRB.getInt32Ty(), + #endif + CTX_add); + val1 = IRB.CreateAdd(val1, CTX_load); + val2 = IRB.CreateAdd(val2, CTX_load); - } + } - } else + x = IRB.CreateInsertElement(GuardPtr1, val1, (uint64_t)0); + y = IRB.CreateInsertElement(GuardPtr2, val2, (uint64_t)0); -#endif - { + for (uint64_t i = 1; i < elements; i++) { - unhandled++; - continue; + val1 = ConstantInt::get(Int32Ty, ++afl_global_id); + val2 = ConstantInt::get(Int32Ty, ++afl_global_id); + /*if (CTX_add) { // already loaded I guess - } + LoadInst *CTX_load = IRB.CreateLoad( + #if LLVM_VERSION_MAJOR >= 14 + IRB.getInt32Ty(), + #endif + CTX_add); + val1 = IRB.CreateAdd(val1, CTX_load); + val2 = IRB.CreateAdd(val2, CTX_load); - uint32_t vector_cur = 0; - /* Load SHM pointer */ - LoadInst *MapPtr = - IRB.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr); - ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(MapPtr); + }*/ - while (1) { + x = IRB.CreateInsertElement(GuardPtr1, val1, i); + y = IRB.CreateInsertElement(GuardPtr2, val2, i); - /* Get CurLoc */ - Value *MapPtrIdx = nullptr; + } - /* Load counter for CurLoc */ - if (!vector_cnt) { + result = IRB.CreateSelect(condition, x, y); + skip_next = 1; - MapPtrIdx = IRB.CreateGEP(Int8Ty, MapPtr, result); + } - } else { + } - auto element = IRB.CreateExtractElement(result, vector_cur++); - MapPtrIdx = IRB.CreateGEP(Int8Ty, MapPtr, element); + } else + +#endif + { + + ++unhandled; + continue; } - if (use_threadsafe_counters) { + uint32_t vector_cur = 0; + /* Load SHM pointer */ + LoadInst *MapPtr = + IRB.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr); + ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(MapPtr); + + while (1) { + + /* Get CurLoc */ + Value *MapPtrIdx = nullptr; + + /* Load counter for CurLoc */ + if (!vector_cnt) { + + MapPtrIdx = IRB.CreateGEP(Int8Ty, MapPtr, result); + + } else { + + auto element = IRB.CreateExtractElement(result, vector_cur++); + MapPtrIdx = IRB.CreateGEP(Int8Ty, MapPtr, element); + + } + + if (use_threadsafe_counters) { - IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, One, + IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, + One, #if LLVM_VERSION_MAJOR >= 13 - llvm::MaybeAlign(1), + llvm::MaybeAlign(1), #endif - llvm::AtomicOrdering::Monotonic); + llvm::AtomicOrdering::Monotonic); + + } else { - } else { + LoadInst *Counter = IRB.CreateLoad(IRB.getInt8Ty(), MapPtrIdx); + ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(Counter); - LoadInst *Counter = IRB.CreateLoad(IRB.getInt8Ty(), MapPtrIdx); - ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(Counter); + /* Update bitmap */ - /* Update bitmap */ + Value *Incr = IRB.CreateAdd(Counter, One); - Value *Incr = IRB.CreateAdd(Counter, One); + if (skip_nozero == NULL) { - if (skip_nozero == NULL) { + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); - auto cf = IRB.CreateICmpEQ(Incr, Zero); - auto carry = IRB.CreateZExt(cf, Int8Ty); - Incr = IRB.CreateAdd(Incr, carry); + } + + auto nosan = IRB.CreateStore(Incr, MapPtrIdx); + ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(nosan); } - auto nosan = IRB.CreateStore(Incr, MapPtrIdx); - ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(nosan); + if (!vector_cnt || vector_cnt == vector_cur) { break; } } - if (!vector_cnt || vector_cnt == vector_cur) { break; } - - } + skip_next = 1; + // fprintf(stderr, "Select out\n"); - skip_next = 1; + } else { - } else { + // fprintf(stderr, "Select skip\n"); + skip_next = 0; - skip_next = 0; + } } } - if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) - BlocksToInstrument.push_back(&BB); - for (auto &Inst : BB) { + if (!instrument_ctx) + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) + BlocksToInstrument.push_back(&BB); - if (Options.IndirectCalls) { + /* + for (auto &Inst : BB) { - CallBase *CB = dyn_cast<CallBase>(&Inst); - if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); + if (Options.IndirectCalls) { - } + CallBase *CB = dyn_cast<CallBase>(&Inst); + if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); - } + } + + }*/ } InjectCoverage(F, BlocksToInstrument, IsLeafFunc); - InjectCoverageForIndirectCalls(F, IndirCalls); + // InjectCoverageForIndirectCalls(F, IndirCalls); + + /*if (debug) + fprintf(stderr, "Done instrumentation (%u-%u=%u %u-%u=%u)\n", inst, + inst_save, inst - inst_save, afl_global_id, save_global, + afl_global_id - save_global);*/ + + if (inst_in_this_func && call_counter > 1) { + + if (inst_in_this_func != afl_global_id - save_global) { + + fprintf( + stderr, + "BUG! inst_in_this_func %u != afl_global_id %u - save_global %u\n", + inst_in_this_func, afl_global_id, save_global); + exit(-1); + + } + + extra_ctx_inst += inst_in_this_func * (call_counter - 1); + afl_global_id += extra_ctx_inst; + + } } @@ -1478,8 +1987,8 @@ GlobalVariable *ModuleSanitizerCoverageLTO::CreateFunctionLocalArrayInSection( ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); auto Array = new GlobalVariable( - *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, - Constant::getNullValue(ArrayTy), "__sancov_gen_"); + *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, + Constant::getNullValue(ArrayTy), "__sancov_gen_"); #if LLVM_VERSION_MAJOR >= 13 if (TargetTriple.supportsCOMDAT() && @@ -1493,7 +2002,7 @@ GlobalVariable *ModuleSanitizerCoverageLTO::CreateFunctionLocalArrayInSection( Array->setComdat(Comdat); #endif Array->setSection(getSectionName(Section)); - Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); + Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue())); GlobalsToAppendToUsed.push_back(Array); GlobalsToAppendToCompilerUsed.push_back(Array); MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F)); @@ -1595,6 +2104,34 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage( } +bool ModuleSanitizerCoverageLTO::Fake_InjectCoverage( + Function &F, ArrayRef<BasicBlock *> AllBlocks, bool IsLeafFunc) { + + if (AllBlocks.empty()) return false; + + for (size_t i = 0, N = AllBlocks.size(); i < N; i++) { + + if (BlockList.size()) { + + int skip = 0; + for (uint32_t k = 0; k < BlockList.size(); k++) { + + if (AllBlocks[i] == BlockList[k]) { skip = 1; } + + } + + if (skip) continue; + + } + + ++inst; // InjectCoverageAtBlock() + + } + + return true; + +} + // On every indirect call we call a run-time function // __sanitizer_cov_indir_call* with two parameters: // - callee address, @@ -1602,6 +2139,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage( // The cache is used to speed up recording the caller-callee pairs. // The address of the caller is passed implicitly via caller PC. // CacheSize is encoded in the name of the run-time function. +/* void ModuleSanitizerCoverageLTO::InjectCoverageForIndirectCalls( Function &F, ArrayRef<Instruction *> IndirCalls) { @@ -1620,6 +2158,8 @@ void ModuleSanitizerCoverageLTO::InjectCoverageForIndirectCalls( } +*/ + void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, @@ -1666,6 +2206,19 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F, /* Set the ID of the inserted basic block */ ConstantInt *CurLoc = ConstantInt::get(Int32Tyi, afl_global_id); + Value *val = CurLoc; + + if (CTX_add) { + + LoadInst *CTX_load = IRB.CreateLoad( +#if LLVM_VERSION_MAJOR >= 14 + IRB.getInt32Ty(), +#endif + CTX_add); + ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(CTX_load); + val = IRB.CreateAdd(CurLoc, CTX_load); + + } /* Load SHM pointer */ @@ -1673,13 +2226,13 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F, if (map_addr) { - MapPtrIdx = IRB.CreateGEP(Int8Ty, MapPtrFixed, CurLoc); + MapPtrIdx = IRB.CreateGEP(Int8Ty, MapPtrFixed, val); } else { LoadInst *MapPtr = IRB.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr); ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(MapPtr); - MapPtrIdx = IRB.CreateGEP(Int8Ty, MapPtr, CurLoc); + MapPtrIdx = IRB.CreateGEP(Int8Ty, MapPtr, val); } @@ -1714,12 +2267,10 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F, // done :) - inst++; + ++inst; // AFL++ END /* - XXXXXXXXXXXXXXXXXXX - auto GuardPtr = IRB.CreateIntToPtr( IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), ConstantInt::get(IntptrTy, Idx * 4)), diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index 41c38283..01881f28 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -13,42 +13,63 @@ #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" -#if LLVM_VERSION_MAJOR < 17 - #include "llvm/ADT/Triple.h" - #include "llvm/Analysis/EHPersonalities.h" -#else - #include "llvm/IR/EHPersonalities.h" +#if LLVM_VERSION_MAJOR >= 15 + #if LLVM_VERSION_MAJOR < 17 + #include "llvm/ADT/Triple.h" + #endif #endif #include "llvm/Analysis/PostDominators.h" -#include "llvm/IR/CFG.h" +#if LLVM_VERSION_MAJOR < 15 + #include "llvm/IR/CFG.h" +#endif #include "llvm/IR/Constant.h" #include "llvm/IR/DataLayout.h" -#include "llvm/IR/DebugInfo.h" +#if LLVM_VERSION_MAJOR < 15 + #include "llvm/IR/DebugInfo.h" +#endif #include "llvm/IR/Dominators.h" +#if LLVM_VERSION_MAJOR >= 17 + #include "llvm/IR/EHPersonalities.h" +#else + #include "llvm/Analysis/EHPersonalities.h" +#endif #include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" +#if LLVM_VERSION_MAJOR >= 16 + #include "llvm/IR/GlobalVariable.h" +#endif #include "llvm/IR/IRBuilder.h" -#include "llvm/IR/InlineAsm.h" +#if LLVM_VERSION_MAJOR < 15 + #include "llvm/IR/InlineAsm.h" +#endif #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/IR/MDBuilder.h" -#include "llvm/IR/Mangler.h" +#if LLVM_VERSION_MAJOR < 15 + #include "llvm/IR/MDBuilder.h" + #include "llvm/IR/Mangler.h" +#endif #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" #include "llvm/IR/Type.h" -#include "llvm/InitializePasses.h" +#if LLVM_VERSION_MAJOR < 17 + #include "llvm/InitializePasses.h" +#endif #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/VirtualFileSystem.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/Instrumentation.h" +#if LLVM_VERSION_MAJOR < 15 + #include "llvm/Support/raw_ostream.h" +#endif +#if LLVM_VERSION_MAJOR < 17 + #include "llvm/Transforms/Instrumentation.h" +#else + #include "llvm/TargetParser/Triple.h" +#endif #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/ModuleUtils.h" -#include "llvm/Passes/PassPlugin.h" -#include "llvm/Passes/PassBuilder.h" -#include "llvm/IR/PassManager.h" #include "config.h" #include "debug.h" @@ -58,7 +79,8 @@ using namespace llvm; #define DEBUG_TYPE "sancov" -const char SanCovTracePCIndirName[] = "__sanitizer_cov_trace_pc_indir"; +static const uint64_t SanCtorAndDtorPriority = 2; + const char SanCovTracePCName[] = "__sanitizer_cov_trace_pc"; const char SanCovTraceCmp1[] = "__sanitizer_cov_trace_cmp1"; const char SanCovTraceCmp2[] = "__sanitizer_cov_trace_cmp2"; @@ -68,22 +90,13 @@ const char SanCovTraceConstCmp1[] = "__sanitizer_cov_trace_const_cmp1"; const char SanCovTraceConstCmp2[] = "__sanitizer_cov_trace_const_cmp2"; const char SanCovTraceConstCmp4[] = "__sanitizer_cov_trace_const_cmp4"; const char SanCovTraceConstCmp8[] = "__sanitizer_cov_trace_const_cmp8"; -const char SanCovTraceDiv4[] = "__sanitizer_cov_trace_div4"; -const char SanCovTraceDiv8[] = "__sanitizer_cov_trace_div8"; -const char SanCovTraceGep[] = "__sanitizer_cov_trace_gep"; const char SanCovTraceSwitchName[] = "__sanitizer_cov_trace_switch"; + const char SanCovModuleCtorTracePcGuardName[] = "sancov.module_ctor_trace_pc_guard"; -const char SanCovModuleCtor8bitCountersName[] = - "sancov.module_ctor_8bit_counters"; -const char SanCovModuleCtorBoolFlagName[] = "sancov.module_ctor_bool_flag"; -static const uint64_t SanCtorAndDtorPriority = 2; +const char SanCovTracePCGuardInitName[] = "__sanitizer_cov_trace_pc_guard_init"; const char SanCovTracePCGuardName[] = "__sanitizer_cov_trace_pc_guard"; -const char SanCovTracePCGuardInitName[] = "__sanitizer_cov_trace_pc_guard_init"; -const char SanCov8bitCountersInitName[] = "__sanitizer_cov_8bit_counters_init"; -const char SanCovBoolFlagInitName[] = "__sanitizer_cov_bool_flag_init"; -const char SanCovPCsInitName[] = "__sanitizer_cov_pcs_init"; const char SanCovGuardsSectionName[] = "sancov_guards"; const char SanCovCountersSectionName[] = "sancov_cntrs"; @@ -99,27 +112,9 @@ namespace { SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { - // Sets CoverageType and IndirectCalls. - // SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel); - Options.CoverageType = - SanitizerCoverageOptions::SCK_Edge; // std::max(Options.CoverageType, - // CLOpts.CoverageType); - Options.IndirectCalls = false; // CLOpts.IndirectCalls; - Options.TraceCmp = false; //|= ClCMPTracing; - Options.TraceDiv = false; //|= ClDIVTracing; - Options.TraceGep = false; //|= ClGEPTracing; - Options.TracePC = false; //|= ClTracePC; - Options.TracePCGuard = true; // |= ClTracePCGuard; - Options.Inline8bitCounters = 0; //|= ClInline8bitCounters; - // Options.InlineBoolFlag = 0; //|= ClInlineBoolFlag; - Options.PCTable = false; //|= ClCreatePCTable; - Options.NoPrune = false; //|= !ClPruneBlocks; - Options.StackDepth = false; //|= ClStackDepth; - if (!Options.TracePCGuard && !Options.TracePC && - !Options.Inline8bitCounters && !Options.StackDepth /*&& - !Options.InlineBoolFlag*/) - Options.TracePCGuard = true; // TracePCGuard is default. - + Options.CoverageType = SanitizerCoverageOptions::SCK_Edge; + // Options.NoPrune = true; + Options.TracePCGuard = true; // TracePCGuard is default. return Options; } @@ -139,20 +134,13 @@ class ModuleSanitizerCoverageAFL } PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); - - bool instrumentModule(Module &M, DomTreeCallback DTCallback, - PostDomTreeCallback PDTCallback); + bool instrumentModule(Module &M, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); private: void instrumentFunction(Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback); - void InjectCoverageForIndirectCalls(Function &F, - ArrayRef<Instruction *> IndirCalls); void InjectTraceForCmp(Function &F, ArrayRef<Instruction *> CmpTraceTargets); - void InjectTraceForDiv(Function &F, - ArrayRef<BinaryOperator *> DivTraceTargets); - void InjectTraceForGep(Function &F, - ArrayRef<GetElementPtrInst *> GepTraceTargets); void InjectTraceForSwitch(Function &F, ArrayRef<Instruction *> SwitchTraceTargets); bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks, @@ -173,20 +161,21 @@ class ModuleSanitizerCoverageAFL void SetNoSanitizeMetadata(Instruction *I) { +#if LLVM_VERSION_MAJOR >= 16 + I->setMetadata(LLVMContext::MD_nosanitize, MDNode::get(*C, std::nullopt)); +#else I->setMetadata(I->getModule()->getMDKindID("nosanitize"), MDNode::get(*C, None)); +#endif } std::string getSectionName(const std::string &Section) const; std::string getSectionStart(const std::string &Section) const; std::string getSectionEnd(const std::string &Section) const; - FunctionCallee SanCovTracePCIndir; FunctionCallee SanCovTracePC, SanCovTracePCGuard; FunctionCallee SanCovTraceCmpFunction[4]; FunctionCallee SanCovTraceConstCmpFunction[4]; - FunctionCallee SanCovTraceDivFunction[2]; - FunctionCallee SanCovTraceGepFunction; FunctionCallee SanCovTraceSwitchFunction; GlobalVariable *SanCovLowestStack; Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, @@ -206,7 +195,7 @@ class ModuleSanitizerCoverageAFL SanitizerCoverageOptions Options; - uint32_t instr = 0, selects = 0, unhandled = 0; + uint32_t instr = 0, selects = 0, unhandled = 0, dump_cc = 0; GlobalVariable *AFLMapPtr = NULL; ConstantInt *One = NULL; ConstantInt *Zero = NULL; @@ -215,19 +204,21 @@ class ModuleSanitizerCoverageAFL } // namespace -#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ - extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK llvmGetPassPluginInfo() { - return {LLVM_PLUGIN_API_VERSION, "SanitizerCoveragePCGUARD", "v0.1", + return {LLVM_PLUGIN_API_VERSION, "SanitizerCoveragePCGUARD", "v0.2", /* lambda to insert our pass into the pass pipeline. */ [](PassBuilder &PB) { - #if LLVM_VERSION_MAJOR <= 13 +#if LLVM_VERSION_MAJOR == 13 using OptimizationLevel = typename PassBuilder::OptimizationLevel; - #endif +#endif +#if LLVM_VERSION_MAJOR >= 16 + PB.registerOptimizerEarlyEPCallback( +#else PB.registerOptimizerLastEPCallback( +#endif [](ModulePassManager &MPM, OptimizationLevel OL) { MPM.addPass(ModuleSanitizerCoverageAFL()); @@ -238,8 +229,6 @@ llvmGetPassPluginInfo() { } -#endif - PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M, ModuleAnalysisManager &MAM) { @@ -266,25 +255,18 @@ PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M, std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd( Module &M, const char *Section, Type *Ty) { - GlobalVariable *SecStart = - new GlobalVariable(M, -#if LLVM_VERSION_MAJOR >= 15 - Ty, -#else - Ty->getPointerElementType(), -#endif - false, GlobalVariable::ExternalWeakLinkage, nullptr, - getSectionStart(Section)); + // Use ExternalWeak so that if all sections are discarded due to section + // garbage collection, the linker will not report undefined symbol errors. + // Windows defines the start/stop symbols in compiler-rt so no need for + // ExternalWeak. + GlobalValue::LinkageTypes Linkage = TargetTriple.isOSBinFormatCOFF() + ? GlobalVariable::ExternalLinkage + : GlobalVariable::ExternalWeakLinkage; + GlobalVariable *SecStart = new GlobalVariable(M, Ty, false, Linkage, nullptr, + getSectionStart(Section)); SecStart->setVisibility(GlobalValue::HiddenVisibility); - GlobalVariable *SecEnd = - new GlobalVariable(M, -#if LLVM_VERSION_MAJOR >= 15 - Ty, -#else - Ty->getPointerElementType(), -#endif - false, GlobalVariable::ExternalWeakLinkage, nullptr, - getSectionEnd(Section)); + GlobalVariable *SecEnd = new GlobalVariable(M, Ty, false, Linkage, nullptr, + getSectionEnd(Section)); SecEnd->setVisibility(GlobalValue::HiddenVisibility); IRBuilder<> IRB(M.getContext()); if (!TargetTriple.isOSBinFormatCOFF()) @@ -295,7 +277,8 @@ std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd( auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy); auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr, ConstantInt::get(IntptrTy, sizeof(uint64_t))); - return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEnd); + return std::make_pair(IRB.CreatePointerCast(GEP, PointerType::getUnqual(Ty)), + SecEnd); } @@ -307,8 +290,9 @@ Function *ModuleSanitizerCoverageAFL::CreateInitCallsForSections( auto SecStart = SecStartEnd.first; auto SecEnd = SecStartEnd.second; Function *CtorFunc; + Type *PtrTy = PointerType::getUnqual(Ty); std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions( - M, CtorName, InitFunctionName, {Ty, Ty}, {SecStart, SecEnd}); + M, CtorName, InitFunctionName, {PtrTy, PtrTy}, {SecStart, SecEnd}); assert(CtorFunc->getName() == CtorName); if (TargetTriple.supportsCOMDAT()) { @@ -332,7 +316,6 @@ Function *ModuleSanitizerCoverageAFL::CreateInitCallsForSections( // to include the sancov constructor. This way the linker can deduplicate // the constructors but always leave one copy. CtorFunc->setLinkage(GlobalValue::WeakODRLinkage); - appendToUsed(M, CtorFunc); } @@ -344,37 +327,27 @@ bool ModuleSanitizerCoverageAFL::instrumentModule( Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { setvbuf(stdout, NULL, _IONBF, 0); - if (getenv("AFL_DEBUG")) debug = 1; + + if (getenv("AFL_DEBUG")) { debug = 1; } + + if (getenv("AFL_DUMP_CYCLOMATIC_COMPLEXITY")) { dump_cc = 1; } if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n"); - } else + } else { be_quiet = 1; + } + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST"); initInstrumentList(); scanForDangerousFunctions(&M); - if (debug) { - - fprintf(stderr, - "SANCOV: covtype:%u indirect:%d stack:%d noprune:%d " - "createtable:%d tracepcguard:%d tracepc:%d\n", - Options.CoverageType, Options.IndirectCalls == true ? 1 : 0, - Options.StackDepth == true ? 1 : 0, Options.NoPrune == true ? 1 : 0, - // Options.InlineBoolFlag == true ? 1 : 0, - Options.PCTable == true ? 1 : 0, - Options.TracePCGuard == true ? 1 : 0, - Options.TracePC == true ? 1 : 0); - - } - - if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false; C = &(M.getContext()); DL = &M.getDataLayout(); CurModule = &M; @@ -397,16 +370,14 @@ bool ModuleSanitizerCoverageAFL::instrumentModule( Int16Ty = IRB.getInt16Ty(); Int8Ty = IRB.getInt8Ty(); Int1Ty = IRB.getInt1Ty(); - LLVMContext &Ctx = M.getContext(); + LLVMContext &Ctx = M.getContext(); AFLMapPtr = new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); One = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 1); Zero = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 0); - SanCovTracePCIndir = - M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy); // Make sure smaller parameters are zero-extended to i64 if required by the // target ABI. AttributeList SanCovTraceCmpZeroExtAL; @@ -436,26 +407,13 @@ bool ModuleSanitizerCoverageAFL::instrumentModule( SanCovTraceConstCmpFunction[3] = M.getOrInsertFunction(SanCovTraceConstCmp8, VoidTy, Int64Ty, Int64Ty); - { - - AttributeList AL; - AL = AL.addParamAttribute(*C, 0, Attribute::ZExt); - SanCovTraceDivFunction[0] = - M.getOrInsertFunction(SanCovTraceDiv4, AL, VoidTy, IRB.getInt32Ty()); - - } - - SanCovTraceDivFunction[1] = - M.getOrInsertFunction(SanCovTraceDiv8, VoidTy, Int64Ty); - SanCovTraceGepFunction = - M.getOrInsertFunction(SanCovTraceGep, VoidTy, IntptrTy); SanCovTraceSwitchFunction = M.getOrInsertFunction(SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy); Constant *SanCovLowestStackConstant = M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy); SanCovLowestStack = dyn_cast<GlobalVariable>(SanCovLowestStackConstant); - if (!SanCovLowestStack) { + if (!SanCovLowestStack || SanCovLowestStack->getValueType() != IntptrTy) { C->emitError(StringRef("'") + SanCovLowestStackName + "' should not be declared by the user"); @@ -465,8 +423,6 @@ bool ModuleSanitizerCoverageAFL::instrumentModule( SanCovLowestStack->setThreadLocalMode( GlobalValue::ThreadLocalMode::InitialExecTLSModel); - if (Options.StackDepth && !SanCovLowestStack->isDeclaration()) - SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy)); SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy); SanCovTracePCGuard = @@ -481,40 +437,25 @@ bool ModuleSanitizerCoverageAFL::instrumentModule( Ctor = CreateInitCallsForSections(M, SanCovModuleCtorTracePcGuardName, SanCovTracePCGuardInitName, Int32PtrTy, SanCovGuardsSectionName); - if (Function8bitCounterArray) - Ctor = CreateInitCallsForSections(M, SanCovModuleCtor8bitCountersName, - SanCov8bitCountersInitName, Int8PtrTy, - SanCovCountersSectionName); - if (FunctionBoolArray) { - Ctor = CreateInitCallsForSections(M, SanCovModuleCtorBoolFlagName, - SanCovBoolFlagInitName, Int1PtrTy, - SanCovBoolFlagSectionName); - - } + if (Ctor && debug) { - if (Ctor && Options.PCTable) { - - auto SecStartEnd = CreateSecStartEnd(M, SanCovPCsSectionName, IntptrPtrTy); - FunctionCallee InitFunction = declareSanitizerInitFunction( - M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy}); - IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator()); - IRBCtor.CreateCall(InitFunction, {SecStartEnd.first, SecStartEnd.second}); + fprintf(stderr, "SANCOV: installed pcguard_init in ctor\n"); } - // We don't reference these arrays directly in any of our runtime functions, - // so we need to prevent them from being dead stripped. - if (TargetTriple.isOSBinFormatMachO()) appendToUsed(M, GlobalsToAppendToUsed); + appendToUsed(M, GlobalsToAppendToUsed); appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed); if (!be_quiet) { - if (!instr) + if (!instr) { + WARNF("No instrumentation targets found."); - else { - char modeline[100]; + } else { + + char modeline[128]; snprintf(modeline, sizeof(modeline), "%s%s%s%s%s%s", getenv("AFL_HARDEN") ? "hardened" : "non-hardened", getenv("AFL_USE_ASAN") ? ", ASAN" : "", @@ -535,39 +476,36 @@ bool ModuleSanitizerCoverageAFL::instrumentModule( } // True if block has successors and it dominates all of them. -bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) { +static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) { - if (succ_begin(BB) == succ_end(BB)) return false; + if (succ_empty(BB)) return false; - for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) { + return llvm::all_of(successors(BB), [&](const BasicBlock *SUCC) { - if (!DT->dominates(BB, SUCC)) return false; + return DT->dominates(BB, SUCC); - } - - return true; + }); } // True if block has predecessors and it postdominates all of them. -bool isFullPostDominator(const BasicBlock *BB, const PostDominatorTree *PDT) { +static bool isFullPostDominator(const BasicBlock *BB, + const PostDominatorTree *PDT) { - if (pred_begin(BB) == pred_end(BB)) return false; + if (pred_empty(BB)) return false; - for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) { + return llvm::all_of(predecessors(BB), [&](const BasicBlock *PRED) { - if (!PDT->dominates(BB, PRED)) return false; + return PDT->dominates(BB, PRED); - } - - return true; + }); } -bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, - const DominatorTree *DT, - const PostDominatorTree *PDT, - const SanitizerCoverageOptions &Options) { +static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, + const DominatorTree *DT, + const PostDominatorTree *PDT, + const SanitizerCoverageOptions &Options) { // Don't insert coverage for blocks containing nothing but unreachable: we // will never call __sanitizer_cov() for them, so counting them in @@ -582,10 +520,6 @@ bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, if (Options.NoPrune || &F.getEntryBlock() == BB) return true; - if (Options.CoverageType == SanitizerCoverageOptions::SCK_Function && - &F.getEntryBlock() != BB) - return false; - // Do not instrument full dominators, or full post-dominators with multiple // predecessors. return !isFullDominator(BB, DT) && @@ -597,41 +531,54 @@ bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, // A twist here is that we treat From->To as a backedge if // * To dominates From or // * To->UniqueSuccessor dominates From -bool IsBackEdge(BasicBlock *From, BasicBlock *To, const DominatorTree *DT) { +#if 0 +static bool IsBackEdge(BasicBlock *From, BasicBlock *To, + const DominatorTree *DT) { - if (DT->dominates(To, From)) return true; + if (DT->dominates(To, From)) + return true; if (auto Next = To->getUniqueSuccessor()) - if (DT->dominates(Next, From)) return true; + if (DT->dominates(Next, From)) + return true; return false; } +#endif + // Prunes uninteresting Cmp instrumentation: // * CMP instructions that feed into loop backedge branch. // // Note that Cmp pruning is controlled by the same flag as the // BB pruning. -bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT, - const SanitizerCoverageOptions &Options) { +#if 0 +static bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT, + const SanitizerCoverageOptions &Options) { if (!Options.NoPrune) if (CMP->hasOneUse()) if (auto BR = dyn_cast<BranchInst>(CMP->user_back())) for (BasicBlock *B : BR->successors()) - if (IsBackEdge(BR->getParent(), B, DT)) return false; + if (IsBackEdge(BR->getParent(), B, DT)) + return false; return true; } +#endif + void ModuleSanitizerCoverageAFL::instrumentFunction( Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { if (F.empty()) return; if (!isInInstrumentList(&F, FMNAME)) return; - if (F.getName().find(".module_ctor") != std::string::npos) return; // Should not instrument sanitizer init functions. +#if LLVM_VERSION_MAJOR >= 18 + if (F.getName().starts_with("__sanitizer_")) +#else if (F.getName().startswith("__sanitizer_")) +#endif return; // Don't instrument __sanitizer_* callbacks. // Don't touch available_externally functions, their actual body is elewhere. if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return; @@ -647,15 +594,13 @@ void ModuleSanitizerCoverageAFL::instrumentFunction( if (F.hasPersonalityFn() && isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) return; + if (F.hasFnAttribute(Attribute::NoSanitizeCoverage)) return; if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) SplitAllCriticalEdges( F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); - SmallVector<Instruction *, 8> IndirCalls; - SmallVector<BasicBlock *, 16> BlocksToInstrument; - SmallVector<Instruction *, 8> CmpTraceTargets; - SmallVector<Instruction *, 8> SwitchTraceTargets; - SmallVector<BinaryOperator *, 8> DivTraceTargets; - SmallVector<GetElementPtrInst *, 8> GepTraceTargets; + SmallVector<BasicBlock *, 16> BlocksToInstrument; + SmallVector<Instruction *, 8> CmpTraceTargets; + SmallVector<Instruction *, 8> SwitchTraceTargets; const DominatorTree *DT = DTCallback(F); const PostDominatorTree *PDT = PDTCallback(F); @@ -665,47 +610,37 @@ void ModuleSanitizerCoverageAFL::instrumentFunction( if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) BlocksToInstrument.push_back(&BB); - for (auto &Inst : BB) { + /* + for (auto &Inst : BB) { - if (Options.IndirectCalls) { + if (Options.TraceCmp) { - CallBase *CB = dyn_cast<CallBase>(&Inst); - if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); + if (ICmpInst *CMP = dyn_cast<ICmpInst>(&Inst)) + if (IsInterestingCmp(CMP, DT, Options)) + CmpTraceTargets.push_back(&Inst); + if (isa<SwitchInst>(&Inst)) + SwitchTraceTargets.push_back(&Inst); - } + } - if (Options.TraceCmp) { + } - if (ICmpInst *CMP = dyn_cast<ICmpInst>(&Inst)) - if (IsInterestingCmp(CMP, DT, Options)) - CmpTraceTargets.push_back(&Inst); - if (isa<SwitchInst>(&Inst)) SwitchTraceTargets.push_back(&Inst); + */ - } + } - if (Options.TraceDiv) - if (BinaryOperator *BO = dyn_cast<BinaryOperator>(&Inst)) - if (BO->getOpcode() == Instruction::SDiv || - BO->getOpcode() == Instruction::UDiv) - DivTraceTargets.push_back(BO); - if (Options.TraceGep) - if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(&Inst)) - GepTraceTargets.push_back(GEP); - if (Options.StackDepth) - if (isa<InvokeInst>(Inst) || - (isa<CallInst>(Inst) && !isa<IntrinsicInst>(Inst))) - IsLeafFunc = false; + if (debug) { - } + fprintf(stderr, "SanitizerCoveragePCGUARD: instrumenting %s in %s\n", + F.getName().str().c_str(), F.getParent()->getName().str().c_str()); } InjectCoverage(F, BlocksToInstrument, IsLeafFunc); - InjectCoverageForIndirectCalls(F, IndirCalls); - InjectTraceForCmp(F, CmpTraceTargets); - InjectTraceForSwitch(F, SwitchTraceTargets); - InjectTraceForDiv(F, DivTraceTargets); - InjectTraceForGep(F, GepTraceTargets); + // InjectTraceForCmp(F, CmpTraceTargets); + // InjectTraceForSwitch(F, SwitchTraceTargets); + + if (dump_cc) { calcCyclomaticComplexity(&F); } } @@ -714,36 +649,33 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreateFunctionLocalArrayInSection( ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); auto Array = new GlobalVariable( - *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, - Constant::getNullValue(ArrayTy), "__sancov_gen_"); + *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, + Constant::getNullValue(ArrayTy), "__sancov_gen_"); -#if LLVM_VERSION_MAJOR >= 13 if (TargetTriple.supportsCOMDAT() && (TargetTriple.isOSBinFormatELF() || !F.isInterposable())) if (auto Comdat = getOrCreateFunctionComdat(F, TargetTriple)) Array->setComdat(Comdat); -#else - if (TargetTriple.supportsCOMDAT() && !F.isInterposable()) - if (auto Comdat = - GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId)) - Array->setComdat(Comdat); -#endif - Array->setSection(getSectionName(Section)); -#if (LLVM_VERSION_MAJOR >= 11) || \ - (LLVM_VERSION_MAJOR == 10 && LLVM_VERSION_MINOR >= 1) - #if LLVM_VERSION_MAJOR >= 16 +#if LLVM_VERSION_MAJOR >= 16 Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue())); - #else - Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); - #endif #else - Array->setAlignment(Align(4)); // cheating + Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); #endif - GlobalsToAppendToUsed.push_back(Array); - GlobalsToAppendToCompilerUsed.push_back(Array); - MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F)); - Array->addMetadata(LLVMContext::MD_associated, *MD); + + // sancov_pcs parallels the other metadata section(s). Optimizers (e.g. + // GlobalOpt/ConstantMerge) may not discard sancov_pcs and the other + // section(s) as a unit, so we conservatively retain all unconditionally in + // the compiler. + // + // With comdat (COFF/ELF), the linker can guarantee the associated sections + // will be retained or discarded as a unit, so llvm.compiler.used is + // sufficient. Otherwise, conservatively make all of them retained by the + // linker. + if (Array->hasComdat()) + GlobalsToAppendToCompilerUsed.push_back(Array); + else + GlobalsToAppendToUsed.push_back(Array); return Array; @@ -768,8 +700,12 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreatePCArray( PCs.push_back((Constant *)IRB.CreatePointerCast( BlockAddress::get(AllBlocks[i]), IntptrPtrTy)); +#if LLVM_VERSION_MAJOR >= 16 + PCs.push_back(Constant::getNullValue(IntptrPtrTy)); +#else PCs.push_back((Constant *)IRB.CreateIntToPtr( ConstantInt::get(IntptrTy, 0), IntptrPtrTy)); +#endif } @@ -792,21 +728,13 @@ void ModuleSanitizerCoverageAFL::CreateFunctionLocalArrays( FunctionGuardArray = CreateFunctionLocalArrayInSection( AllBlocks.size() + special, F, Int32Ty, SanCovGuardsSectionName); - if (Options.Inline8bitCounters) - Function8bitCounterArray = CreateFunctionLocalArrayInSection( - AllBlocks.size(), F, Int8Ty, SanCovCountersSectionName); - /* - if (Options.InlineBoolFlag) - FunctionBoolArray = CreateFunctionLocalArrayInSection( - AllBlocks.size(), F, Int1Ty, SanCovBoolFlagSectionName); - */ - if (Options.PCTable) FunctionPCsArray = CreatePCArray(F, AllBlocks); - } bool ModuleSanitizerCoverageAFL::InjectCoverage( Function &F, ArrayRef<BasicBlock *> AllBlocks, bool IsLeafFunc) { + if (AllBlocks.empty()) return false; + uint32_t cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0; static uint32_t first = 1; @@ -855,7 +783,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( } -#if (LLVM_VERSION_MAJOR >= 12) else if (t->getTypeID() == llvm::Type::FixedVectorTyID) { FixedVectorType *tt = dyn_cast<FixedVectorType>(t); @@ -868,16 +795,14 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( } -#endif - } } } - /* Create PCGUARD array */ CreateFunctionLocalArrays(F, AllBlocks, first + cnt_cov + cnt_sel_inc); + if (first) { first = 0; } selects += cnt_sel; @@ -889,12 +814,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( CallInst *callInst = nullptr; - /* - std::string errMsg; - raw_string_ostream os(errMsg); - IN.print(os); - fprintf(stderr, "X: %s\n", os.str().c_str()); - */ if ((callInst = dyn_cast<CallInst>(&IN))) { Function *Callee = callInst->getCalledFunction(); @@ -953,7 +872,7 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), ConstantInt::get( IntptrTy, - (cnt_cov + ++local_selects + AllBlocks.size()) * 4)), + (cnt_cov + local_selects++ + AllBlocks.size()) * 4)), Int32PtrTy); auto GuardPtr2 = IRB.CreateIntToPtr( @@ -961,7 +880,7 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), ConstantInt::get( IntptrTy, - (cnt_cov + ++local_selects + AllBlocks.size()) * 4)), + (cnt_cov + local_selects++ + AllBlocks.size()) * 4)), Int32PtrTy); result = IRB.CreateSelect(condition, GuardPtr1, GuardPtr2); @@ -998,7 +917,7 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), ConstantInt::get( IntptrTy, - (cnt_cov + ++local_selects + AllBlocks.size()) * 4)), + (cnt_cov + local_selects++ + AllBlocks.size()) * 4)), Int32PtrTy); x = IRB.CreateInsertElement(GuardPtr1, val1, (uint64_t)0); @@ -1007,7 +926,7 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), ConstantInt::get( IntptrTy, - (cnt_cov + ++local_selects + AllBlocks.size()) * 4)), + (cnt_cov + local_selects++ + AllBlocks.size()) * 4)), Int32PtrTy); y = IRB.CreateInsertElement(GuardPtr2, val2, (uint64_t)0); @@ -1016,7 +935,7 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( val1 = IRB.CreateIntToPtr( IRB.CreateAdd( IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), - ConstantInt::get(IntptrTy, (cnt_cov + ++local_selects + + ConstantInt::get(IntptrTy, (cnt_cov + local_selects++ + AllBlocks.size()) * 4)), Int32PtrTy); @@ -1025,7 +944,7 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( val2 = IRB.CreateIntToPtr( IRB.CreateAdd( IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), - ConstantInt::get(IntptrTy, (cnt_cov + ++local_selects + + ConstantInt::get(IntptrTy, (cnt_cov + local_selects++ + AllBlocks.size()) * 4)), Int32PtrTy); @@ -1033,12 +952,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( } - /* - std::string errMsg; - raw_string_ostream os(errMsg); - x->print(os); - fprintf(stderr, "X: %s\n", os.str().c_str()); - */ result = IRB.CreateSelect(condition, x, y); } @@ -1050,6 +963,7 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( #endif { + // fprintf(stderr, "UNHANDLED: %u\n", t->getTypeID()); unhandled++; continue; @@ -1063,13 +977,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( IRB.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr); ModuleSanitizerCoverageAFL::SetNoSanitizeMetadata(MapPtr); - /* - std::string errMsg; - raw_string_ostream os(errMsg); - result->print(os); - fprintf(stderr, "X: %s\n", os.str().c_str()); - */ - while (1) { /* Get CurLoc */ @@ -1159,29 +1066,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( } -// On every indirect call we call a run-time function -// __sanitizer_cov_indir_call* with two parameters: -// - callee address, -// - global cache array that contains CacheSize pointers (zero-initialized). -// The cache is used to speed up recording the caller-callee pairs. -// The address of the caller is passed implicitly via caller PC. -// CacheSize is encoded in the name of the run-time function. -void ModuleSanitizerCoverageAFL::InjectCoverageForIndirectCalls( - Function &F, ArrayRef<Instruction *> IndirCalls) { - - if (IndirCalls.empty()) return; - for (auto I : IndirCalls) { - - IRBuilder<> IRB(I); - CallBase &CB = cast<CallBase>(*I); - Value *Callee = CB.getCalledOperand(); - if (isa<InlineAsm>(Callee)) continue; - IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy)); - - } - -} - // For every switch statement we insert a call: // __sanitizer_cov_trace_switch(CondValue, // {NumCases, ValueSizeInBits, Case0Value, Case1Value, Case2Value, ... }) @@ -1237,41 +1121,6 @@ void ModuleSanitizerCoverageAFL::InjectTraceForSwitch( } -void ModuleSanitizerCoverageAFL::InjectTraceForDiv( - Function &, ArrayRef<BinaryOperator *> DivTraceTargets) { - - for (auto BO : DivTraceTargets) { - - IRBuilder<> IRB(BO); - Value *A1 = BO->getOperand(1); - if (isa<ConstantInt>(A1)) continue; - if (!A1->getType()->isIntegerTy()) continue; - uint64_t TypeSize = DL->getTypeStoreSizeInBits(A1->getType()); - int CallbackIdx = TypeSize == 32 ? 0 : TypeSize == 64 ? 1 : -1; - if (CallbackIdx < 0) continue; - auto Ty = Type::getIntNTy(*C, TypeSize); - IRB.CreateCall(SanCovTraceDivFunction[CallbackIdx], - {IRB.CreateIntCast(A1, Ty, true)}); - - } - -} - -void ModuleSanitizerCoverageAFL::InjectTraceForGep( - Function &, ArrayRef<GetElementPtrInst *> GepTraceTargets) { - - for (auto GEP : GepTraceTargets) { - - IRBuilder<> IRB(GEP); - for (Use &Idx : GEP->indices()) - if (!isa<ConstantInt>(Idx) && Idx->getType()->isIntegerTy()) - IRB.CreateCall(SanCovTraceGepFunction, - {IRB.CreateIntCast(Idx, IntptrTy, true)}); - - } - -} - void ModuleSanitizerCoverageAFL::InjectTraceForCmp( Function &, ArrayRef<Instruction *> CmpTraceTargets) { @@ -1321,27 +1170,44 @@ void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function &F, BasicBlock::iterator IP = BB.getFirstInsertionPt(); bool IsEntryBB = &BB == &F.getEntryBlock(); + DebugLoc EntryLoc; if (IsEntryBB) { - // Keep allocas and llvm.localescape calls in the entry block. Even + if (auto SP = F.getSubprogram()) + EntryLoc = DILocation::get(SP->getContext(), SP->getScopeLine(), 0, SP); + // Keep static allocas and llvm.localescape calls in the entry block. Even // if we aren't splitting the block, it's nice for allocas to be before // calls. IP = PrepareToSplitEntryBlock(BB, IP); +#if LLVM_VERSION_MAJOR < 15 - } - - IRBuilder<> IRB(&*IP); - - if (Options.TracePC) { + } else { - IRB.CreateCall(SanCovTracePC); - // ->setCannotMerge(); // gets the PC using GET_CALLER_PC. + EntryLoc = IP->getDebugLoc(); + if (!EntryLoc) + if (auto *SP = F.getSubprogram()) + EntryLoc = DILocation::get(SP->getContext(), 0, 0, SP); +#endif } +#if LLVM_VERSION_MAJOR >= 16 + InstrumentationIRBuilder IRB(&*IP); +#else + IRBuilder<> IRB(&*IP); +#endif + if (EntryLoc) IRB.SetCurrentDebugLocation(EntryLoc); if (Options.TracePCGuard) { + /* + auto GuardPtr = IRB.CreateIntToPtr( + IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), + ConstantInt::get(IntptrTy, Idx * 4)), + Int32PtrTy); + IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge(); + */ + /* Get CurLoc */ Value *GuardPtr = IRB.CreateIntToPtr( @@ -1399,57 +1265,6 @@ void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function &F, } - if (Options.Inline8bitCounters) { - - auto CounterPtr = IRB.CreateGEP( - Function8bitCounterArray->getValueType(), Function8bitCounterArray, - {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); - auto Load = IRB.CreateLoad(Int8Ty, CounterPtr); - auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1)); - auto Store = IRB.CreateStore(Inc, CounterPtr); - SetNoSanitizeMetadata(Load); - SetNoSanitizeMetadata(Store); - - } - - /* - if (Options.InlineBoolFlag) { - - auto FlagPtr = IRB.CreateGEP( - FunctionBoolArray->getValueType(), FunctionBoolArray, - {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); - auto Load = IRB.CreateLoad(Int1Ty, FlagPtr); - auto ThenTerm = - SplitBlockAndInsertIfThen(IRB.CreateIsNull(Load), &*IP, false); - IRBuilder<> ThenIRB(ThenTerm); - auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr); - SetNoSanitizeMetadata(Load); - SetNoSanitizeMetadata(Store); - - } - - */ - - if (Options.StackDepth && IsEntryBB && !IsLeafFunc) { - - // Check stack depth. If it's the deepest so far, record it. - Module *M = F.getParent(); - Function *GetFrameAddr = Intrinsic::getDeclaration( - M, Intrinsic::frameaddress, - IRB.getInt8PtrTy(M->getDataLayout().getAllocaAddrSpace())); - auto FrameAddrPtr = - IRB.CreateCall(GetFrameAddr, {Constant::getNullValue(Int32Ty)}); - auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy); - auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack); - auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack); - auto ThenTerm = SplitBlockAndInsertIfThen(IsStackLower, &*IP, false); - IRBuilder<> ThenIRB(ThenTerm); - auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack); - SetNoSanitizeMetadata(LowestStack); - SetNoSanitizeMetadata(Store); - - } - } std::string ModuleSanitizerCoverageAFL::getSectionName( diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 5372fae0..e450dc45 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -3,7 +3,7 @@ ------------------------------------------------ Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2023 AFLplusplus Project. All rights reserved. + Copyright 2019-2024 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. @@ -22,6 +22,10 @@ #define __USE_GNU #endif #include <dlfcn.h> + +__attribute__((weak)) void __sanitizer_symbolize_pc(void *, const char *fmt, + char *out_buf, + size_t out_buf_size); #endif #ifdef __ANDROID__ @@ -48,7 +52,7 @@ #include <errno.h> #include <sys/mman.h> -#ifndef __HAIKU__ +#if !defined(__HAIKU__) && !defined(__OpenBSD__) #include <sys/syscall.h> #endif #ifndef USEMMAP @@ -83,21 +87,21 @@ #include <sys/mman.h> #include <fcntl.h> +#ifdef AFL_PERSISTENT_RECORD + #include "afl-persistent-replay.h" +#endif + /* 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. It will end up as .comm, so it shouldn't be too wasteful. */ -#if MAP_SIZE <= 65536 - #define MAP_INITIAL_SIZE 2097152 -#else - #define MAP_INITIAL_SIZE MAP_SIZE -#endif - #if defined(__HAIKU__) extern ssize_t _kern_write(int fd, off_t pos, const void *buffer, size_t bufferSize); #endif // HAIKU +char *strcasestr(const char *haystack, const char *needle); + static u8 __afl_area_initial[MAP_INITIAL_SIZE]; static u8 *__afl_area_ptr_dummy = __afl_area_initial; static u8 *__afl_area_ptr_backup = __afl_area_initial; @@ -128,8 +132,8 @@ struct afl_module_info_t { uintptr_t base_address; // PC Guard start/stop - u32 start; - u32 stop; + u32 *start; + u32 *stop; // PC Table begin/end const uintptr_t *pcs_beg; @@ -151,6 +155,18 @@ afl_module_info_t *__afl_module_info = NULL; u32 __afl_pcmap_size = 0; uintptr_t *__afl_pcmap_ptr = NULL; + +typedef struct { + + uintptr_t start; + u32 len; + +} FilterPCEntry; + +u32 __afl_filter_pcs_size = 0; +FilterPCEntry *__afl_filter_pcs = NULL; +u8 *__afl_filter_pcs_module = NULL; + #endif // __AFL_CODE_COVERAGE /* 1 if we are running in afl, and the forkserver was started, else 0 */ @@ -174,6 +190,8 @@ __thread u32 __afl_prev_ctx; struct cmp_map *__afl_cmp_map; struct cmp_map *__afl_cmp_map_backup; +static u8 __afl_cmplog_max_len = 32; // 16-32 + /* Child pid? */ static s32 child_pid; @@ -189,7 +207,7 @@ static u8 _is_sancov; /* Debug? */ -static u32 __afl_debug; +/*static*/ u32 __afl_debug; /* Already initialized markers */ @@ -252,7 +270,7 @@ static void send_forkserver_error(int error) { u32 status; if (!error || error > 0xffff) return; - status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + status = (FS_NEW_ERROR | error); if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) { return; } } @@ -355,32 +373,13 @@ static void __afl_map_shm(void) { if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { val = atoi(ptr); } if (val < __afl_final_loc) { - if (__afl_final_loc > FS_OPT_MAX_MAPSIZE) { - - if (!getenv("AFL_QUIET")) - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u " - "to be able to run this instrumented program!\n", - __afl_final_loc); - - if (id_str) { - - send_forkserver_error(FS_ERROR_MAP_SIZE); - exit(-1); - - } + if (__afl_final_loc > MAP_INITIAL_SIZE && !getenv("AFL_QUIET")) { - } else { - - if (__afl_final_loc > MAP_INITIAL_SIZE && !getenv("AFL_QUIET")) { - - fprintf(stderr, - "Warning: AFL++ tools might need to set AFL_MAP_SIZE to %u " - "to be able to run this instrumented program if this " - "crashes!\n", - __afl_final_loc); - - } + fprintf(stderr, + "Warning: AFL++ tools might need to set AFL_MAP_SIZE to %u " + "to be able to run this instrumented program if this " + "crashes!\n", + __afl_final_loc); } @@ -388,15 +387,6 @@ static void __afl_map_shm(void) { } - } else { - - if (getenv("AFL_DUMP_MAP_SIZE")) { - - printf("%u\n", MAP_SIZE); - exit(-1); - - } - } if (__afl_sharedmem_fuzzing && (!id_str || !getenv(SHM_FUZZ_ENV_VAR) || @@ -462,14 +452,13 @@ static void __afl_map_shm(void) { if (__afl_debug) { - fprintf( - stderr, - "DEBUG: (1) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " - "__afl_area_ptr_dummy %p, __afl_map_addr 0x%llx, MAP_SIZE %u, " - "__afl_final_loc %u, __afl_map_size %u, max_size_forkserver %u/0x%x\n", - id_str == NULL ? "<null>" : id_str, __afl_area_ptr, __afl_area_initial, - __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE, __afl_final_loc, - __afl_map_size, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); + fprintf(stderr, + "DEBUG: (1) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " + "__afl_area_ptr_dummy %p, __afl_map_addr 0x%llx, MAP_SIZE %u, " + "__afl_final_loc %u, __afl_map_size %u\n", + id_str == NULL ? "<null>" : id_str, __afl_area_ptr, + __afl_area_initial, __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE, + __afl_final_loc, __afl_map_size); } @@ -544,12 +533,12 @@ static void __afl_map_shm(void) { if (__afl_map_size && __afl_map_size > MAP_SIZE) { - u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE"); - if (!map_env || atoi((char *)map_env) < MAP_SIZE) { + u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE"); + if (!map_env || atoi((char *)map_env) < MAP_SIZE) { - fprintf(stderr, "FS_ERROR_MAP_SIZE\n"); - send_forkserver_error(FS_ERROR_MAP_SIZE); - _exit(1); + fprintf(stderr, "FS_ERROR_MAP_SIZE\n"); + send_forkserver_error(FS_ERROR_MAP_SIZE); + _exit(1); } @@ -561,13 +550,13 @@ static void __afl_map_shm(void) { if (!__afl_area_ptr || __afl_area_ptr == (void *)-1) { - if (__afl_map_addr) + if (__afl_map_addr) send_forkserver_error(FS_ERROR_MAP_ADDR); else send_forkserver_error(FS_ERROR_SHMAT); perror("shmat for map"); - _exit(1); + _exit(1); } @@ -627,12 +616,10 @@ static void __afl_map_shm(void) { fprintf(stderr, "DEBUG: (2) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " "__afl_area_ptr_dummy %p, __afl_map_addr 0x%llx, MAP_SIZE " - "%u, __afl_final_loc %u, __afl_map_size %u, " - "max_size_forkserver %u/0x%x\n", + "%u, __afl_final_loc %u, __afl_map_size %u", id_str == NULL ? "<null>" : id_str, __afl_area_ptr, __afl_area_initial, __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE, - __afl_final_loc, __afl_map_size, FS_OPT_MAX_MAPSIZE, - FS_OPT_MAX_MAPSIZE); + __afl_final_loc, __afl_map_size); } @@ -673,7 +660,8 @@ static void __afl_map_shm(void) { if (id_str) { - if ((__afl_dummy_fd[1] = open("/dev/null", O_WRONLY)) < 0) { + // /dev/null doesn't work so we use /dev/urandom + if ((__afl_dummy_fd[1] = open("/dev/urandom", O_WRONLY)) < 0) { if (pipe(__afl_dummy_fd) < 0) { __afl_dummy_fd[1] = 1; } @@ -748,6 +736,19 @@ static void __afl_map_shm(void) { #endif // __AFL_CODE_COVERAGE + if (!__afl_cmp_map && getenv("AFL_CMPLOG_DEBUG")) { + + __afl_cmp_map_backup = __afl_cmp_map = malloc(sizeof(struct cmp_map)); + + } + + if (getenv("AFL_CMPLOG_MAX_LEN")) { + + int tmp = atoi(getenv("AFL_CMPLOG_MAX_LEN")); + if (tmp >= 16 && tmp <= 32) { __afl_cmplog_max_len = tmp; } + + } + } /* unmap SHM. */ @@ -842,242 +843,6 @@ void write_error_with_location(char *text, char *filename, int linenumber) { } -#ifdef __linux__ -static void __afl_start_snapshots(void) { - - static u8 tmp[4] = {0, 0, 0, 0}; - u32 status = 0; - u32 already_read_first = 0; - u32 was_killed; - - u8 child_stopped = 0; - - void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); - - /* Phone home and tell the parent that we're OK. If parent isn't there, - assume we're not running in forkserver mode and just execute program. */ - - status |= (FS_OPT_ENABLED | FS_OPT_SNAPSHOT | FS_OPT_NEWCMPLOG); - if (__afl_sharedmem_fuzzing) { status |= FS_OPT_SHDMEM_FUZZ; } - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (__afl_dictionary_len && __afl_dictionary) { status |= FS_OPT_AUTODICT; } - memcpy(tmp, &status, 4); - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } - - if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { - - if (read(FORKSRV_FD, &was_killed, 4) != 4) { - - write_error("read to afl-fuzz"); - _exit(1); - - } - - if (__afl_debug) { - - fprintf(stderr, "target forkserver recv: %08x\n", was_killed); - - } - - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == - (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { - - __afl_map_shm_fuzz(); - - } - - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == - (FS_OPT_ENABLED | FS_OPT_AUTODICT) && - __afl_dictionary_len && __afl_dictionary) { - - // great lets pass the dictionary through the forkserver FD - u32 len = __afl_dictionary_len, offset = 0; - s32 ret; - - if (write(FORKSRV_FD + 1, &len, 4) != 4) { - - write(2, "Error: could not send dictionary len\n", - strlen("Error: could not send dictionary len\n")); - _exit(1); - - } - - while (len != 0) { - - ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len); - - if (ret < 1) { - - write(2, "Error: could not send dictionary\n", - strlen("Error: could not send dictionary\n")); - _exit(1); - - } - - len -= ret; - offset += ret; - - } - - } else { - - // uh this forkserver does not understand extended option passing - // or does not want the dictionary - if (!__afl_fuzz_ptr) already_read_first = 1; - - } - - } - - while (1) { - - int status; - - if (already_read_first) { - - already_read_first = 0; - - } else { - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - if (read(FORKSRV_FD, &was_killed, 4) != 4) { - - write_error("reading from afl-fuzz"); - _exit(1); - - } - - } - - #ifdef _AFL_DOCUMENT_MUTATIONS - if (__afl_fuzz_ptr) { - - static uint32_t counter = 0; - char fn[32]; - sprintf(fn, "%09u:forkserver", counter); - s32 fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_PERMISSION); - if (fd_doc >= 0) { - - if (write(fd_doc, __afl_fuzz_ptr, *__afl_fuzz_len) != *__afl_fuzz_len) { - - fprintf(stderr, "write of mutation file failed: %s\n", fn); - unlink(fn); - - } - - close(fd_doc); - - } - - counter++; - - } - - #endif - - /* If we stopped the child in persistent mode, but there was a race - condition and afl-fuzz already issued SIGKILL, write off the old - process. */ - - if (child_stopped && was_killed) { - - child_stopped = 0; - if (waitpid(child_pid, &status, 0) < 0) { - - write_error("child_stopped && was_killed"); - _exit(1); // TODO why exit? - - } - - } - - if (!child_stopped) { - - /* Once woken up, create a clone of our process. */ - - child_pid = fork(); - if (child_pid < 0) { - - write_error("fork"); - _exit(1); - - } - - /* In child process: close fds, resume execution. */ - - if (!child_pid) { - - //(void)nice(-20); // does not seem to improve - - signal(SIGCHLD, old_sigchld_handler); - signal(SIGTERM, old_sigterm_handler); - - close(FORKSRV_FD); - close(FORKSRV_FD + 1); - - if (!afl_snapshot_take(AFL_SNAPSHOT_MMAP | AFL_SNAPSHOT_FDS | - AFL_SNAPSHOT_REGS | AFL_SNAPSHOT_EXIT)) { - - raise(SIGSTOP); - - } - - __afl_area_ptr[0] = 1; - memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); - - return; - - } - - } else { - - /* Special handling for persistent mode: if the child is alive but - currently stopped, simply restart it with SIGCONT. */ - - kill(child_pid, SIGCONT); - child_stopped = 0; - - } - - /* In parent process: write PID to pipe, then wait for child. */ - - if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) { - - write_error("write to afl-fuzz"); - _exit(1); - - } - - if (waitpid(child_pid, &status, WUNTRACED) < 0) { - - write_error("waitpid"); - _exit(1); - - } - - /* In persistent mode, the child stops itself with SIGSTOP to indicate - a successful run. In this case, we want to wake it up without forking - again. */ - - if (WIFSTOPPED(status)) child_stopped = 1; - - /* Relay wait status to pipe, then loop back. */ - - if (write(FORKSRV_FD + 1, &status, 4) != 4) { - - write_error("writing to afl-fuzz"); - _exit(1); - - } - - } - -} - -#endif - /* Fork server logic. */ static void __afl_start_forkserver(void) { @@ -1090,113 +855,92 @@ static void __afl_start_forkserver(void) { old_sigterm_handler = orig_action.sa_handler; signal(SIGTERM, at_exit); -#ifdef __linux__ - if (/*!is_persistent &&*/ !__afl_cmp_map && !getenv("AFL_NO_SNAPSHOT") && - afl_snapshot_init() >= 0) { - - __afl_start_snapshots(); - return; - - } - -#endif - - u8 tmp[4] = {0, 0, 0, 0}; - u32 status_for_fsrv = 0; u32 already_read_first = 0; u32 was_killed; + u32 version = 0x41464c00 + FS_NEW_VERSION_MAX; + u32 tmp = version ^ 0xffffffff, status2, status = version; + u8 *msg = (u8 *)&status; + u8 *reply = (u8 *)&status2; u8 child_stopped = 0; void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) { + /* Phone home and tell the parent that we're OK. If parent isn't there, + assume we're not running in forkserver mode and just execute program. */ - status_for_fsrv |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + // return because possible non-forkserver usage + if (write(FORKSRV_FD + 1, msg, 4) != 4) { return; } - } + if (read(FORKSRV_FD, reply, 4) != 4) { _exit(1); } + if (tmp != status2) { - if (__afl_dictionary_len && __afl_dictionary) { - - status_for_fsrv |= FS_OPT_AUTODICT; + write_error("wrong forkserver message from AFL++ tool"); + _exit(1); } - if (__afl_sharedmem_fuzzing) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; } - if (status_for_fsrv) { + // send the set/requested options to forkserver + status = FS_NEW_OPT_MAPSIZE; // we always send the map size + if (__afl_sharedmem_fuzzing) { status |= FS_NEW_OPT_SHDMEM_FUZZ; } + if (__afl_dictionary_len && __afl_dictionary) { - status_for_fsrv |= (FS_OPT_ENABLED | FS_OPT_NEWCMPLOG); + status |= FS_NEW_OPT_AUTODICT; } - memcpy(tmp, &status_for_fsrv, 4); - - /* Phone home and tell the parent that we're OK. If parent isn't there, - assume we're not running in forkserver mode and just execute program. */ - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } - - __afl_connected = 1; + if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); } - if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { + // Now send the parameters for the set options, increasing by option number - if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); + // FS_NEW_OPT_MAPSIZE - we always send the map size + status = __afl_map_size; + if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); } - if (__afl_debug) { + // FS_NEW_OPT_SHDMEM_FUZZ - no data - fprintf(stderr, "target forkserver recv: %08x\n", was_killed); + // FS_NEW_OPT_AUTODICT - send autodictionary + if (__afl_dictionary_len && __afl_dictionary) { - } + // pass the dictionary through the forkserver FD + u32 len = __afl_dictionary_len, offset = 0; - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == - (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { + if (write(FORKSRV_FD + 1, &len, 4) != 4) { - __afl_map_shm_fuzz(); + write(2, "Error: could not send dictionary len\n", + strlen("Error: could not send dictionary len\n")); + _exit(1); } - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == - (FS_OPT_ENABLED | FS_OPT_AUTODICT) && - __afl_dictionary_len && __afl_dictionary) { + while (len != 0) { - // great lets pass the dictionary through the forkserver FD - u32 len = __afl_dictionary_len, offset = 0; + s32 ret; + ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len); - if (write(FORKSRV_FD + 1, &len, 4) != 4) { + if (ret < 1) { - write(2, "Error: could not send dictionary len\n", - strlen("Error: could not send dictionary len\n")); + write_error("could not send dictionary"); _exit(1); } - while (len != 0) { - - s32 ret; - ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len); - - if (ret < 1) { + len -= ret; + offset += ret; - write(2, "Error: could not send dictionary\n", - strlen("Error: could not send dictionary\n")); - _exit(1); - - } - - len -= ret; - offset += ret; + } - } + } - } else { + // send welcome message as final message + status = version; + if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); } - // uh this forkserver does not understand extended option passing - // or does not want the dictionary - if (!__afl_fuzz_ptr) already_read_first = 1; + // END forkserver handshake - } + __afl_connected = 1; - } + if (__afl_sharedmem_fuzzing) { __afl_map_shm_fuzz(); } while (1) { @@ -1212,7 +956,7 @@ static void __afl_start_forkserver(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) { - // write_error("read from afl-fuzz"); + write_error("read from AFL++ tool"); _exit(1); } @@ -1341,6 +1085,10 @@ int __afl_persistent_loop(unsigned int max_cnt) { static u8 first_pass = 1; static u32 cycle_cnt; +#ifdef AFL_PERSISTENT_RECORD + char tcase[PATH_MAX]; +#endif + if (first_pass) { /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate. @@ -1352,14 +1100,59 @@ int __afl_persistent_loop(unsigned int max_cnt) { __afl_area_ptr[0] = 1; memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); - cycle_cnt = max_cnt; first_pass = 0; __afl_selective_coverage_temp = 1; +#ifdef AFL_PERSISTENT_RECORD + if (unlikely(is_replay_record)) { + + cycle_cnt = replay_record_cnt; + goto persistent_record; + + } else + +#endif + { + + cycle_cnt = max_cnt; + + } + return 1; } else if (--cycle_cnt) { +#ifdef AFL_PERSISTENT_RECORD + if (unlikely(is_replay_record)) { + + persistent_record: + + snprintf(tcase, PATH_MAX, "%s/%s", + replay_record_dir ? replay_record_dir : "./", + record_list[replay_record_cnt - cycle_cnt]->d_name); + + #ifdef AFL_PERSISTENT_REPLAY_ARGPARSE + if (unlikely(record_arg)) { + + *record_arg = tcase; + + } else + + #endif // AFL_PERSISTENT_REPLAY_ARGPARSE + { + + int fd = open(tcase, O_RDONLY); + dup2(fd, 0); + close(fd); + + } + + return 1; + + } + +#endif + raise(SIGSTOP); __afl_area_ptr[0] = 1; @@ -1477,6 +1270,7 @@ __attribute__((constructor(1))) void __afl_auto_second(void) { __afl_debug = 1; fprintf(stderr, "DEBUG: debug enabled\n"); + fprintf(stderr, "DEBUG: AFL++ afl-compiler-rt" VERSION "\n"); } @@ -1589,15 +1383,116 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { } #ifdef __AFL_CODE_COVERAGE -void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, - const uintptr_t *pcs_end) { +void afl_read_pc_filter_file(const char *filter_file) { - if (__afl_debug) { + FILE *file; + char ch; + + file = fopen(filter_file, "r"); + if (file == NULL) { + + perror("Error opening file"); + return; + + } + + // Check how many PCs we expect to read + while ((ch = fgetc(file)) != EOF) { + + if (ch == '\n') { __afl_filter_pcs_size++; } + + } + + // Rewind to actually read the PCs + fseek(file, 0, SEEK_SET); + + __afl_filter_pcs = malloc(__afl_filter_pcs_size * sizeof(FilterPCEntry)); + if (!__afl_filter_pcs) { + + perror("Error allocating PC array"); + return; + + } + + for (size_t i = 0; i < __afl_filter_pcs_size; i++) { + + fscanf(file, "%lx", &(__afl_filter_pcs[i].start)); + ch = fgetc(file); // Read tab + fscanf(file, "%u", &(__afl_filter_pcs[i].len)); + ch = fgetc(file); // Read tab + + if (!__afl_filter_pcs_module) { + + // Read the module name and store it. + // TODO: We only support one module here right now although + // there is technically no reason to support multiple modules + // in one go. + size_t max_module_len = 255; + size_t i = 0; + __afl_filter_pcs_module = malloc(max_module_len); + while (i < max_module_len - 1 && + (__afl_filter_pcs_module[i] = fgetc(file)) != '\t') { + + ++i; + + } - fprintf(stderr, "DEBUG: __sanitizer_cov_pcs_init called\n"); + __afl_filter_pcs_module[i] = '\0'; + fprintf(stderr, "DEBUGXXX: Read module name %s\n", + __afl_filter_pcs_module); + + } + + while ((ch = fgetc(file)) != '\n' && ch != EOF) + ; } + fclose(file); + +} + +u32 locate_in_pcs(uintptr_t needle, u32 *index) { + + size_t lower_bound = 0; + size_t upper_bound = __afl_filter_pcs_size - 1; + + while (lower_bound < __afl_filter_pcs_size && lower_bound <= upper_bound) { + + size_t current_index = lower_bound + (upper_bound - lower_bound) / 2; + + if (__afl_filter_pcs[current_index].start <= needle) { + + if (__afl_filter_pcs[current_index].start + + __afl_filter_pcs[current_index].len > + needle) { + + // Hit + *index = current_index; + return 1; + + } else { + + lower_bound = current_index + 1; + + } + + } else { + + if (!current_index) { break; } + upper_bound = current_index - 1; + + } + + } + + return 0; + +} + +void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, + const uintptr_t *pcs_end) { + // If for whatever reason, we cannot get dlinfo here, then pc_guard_init also // couldn't get it and we'd end up attributing to the wrong module. Dl_info dlinfo; @@ -1610,6 +1505,16 @@ void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, } + if (__afl_debug) { + + fprintf( + stderr, + "DEBUG: (%u) __sanitizer_cov_pcs_init called for module %s with %ld " + "PCs\n", + getpid(), dlinfo.dli_fname, pcs_end - pcs_beg); + + } + afl_module_info_t *last_module_info = __afl_module_info; while (last_module_info && last_module_info->next) { @@ -1625,34 +1530,78 @@ void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, } + if (strcmp(dlinfo.dli_fname, last_module_info->name)) { + + // This can happen with modules being loaded after the forkserver + // where we decide to not track the module. In that case we must + // not track it here either. + fprintf( + stderr, + "WARNING: __sanitizer_cov_pcs_init module info mismatch: %s vs %s\n", + dlinfo.dli_fname, last_module_info->name); + return; + + } + last_module_info->pcs_beg = pcs_beg; last_module_info->pcs_end = pcs_end; + // This is a direct filter based on symbolizing inside the runtime. + // It should only be used with smaller binaries to avoid long startup + // times. Currently, this only supports a single token to scan for. + const char *pc_filter = getenv("AFL_PC_FILTER"); + + // This is a much faster PC filter based on pre-symbolized input data + // that is sorted for fast lookup through binary search. This method + // of filtering is suitable even for very large binaries. + const char *pc_filter_file = getenv("AFL_PC_FILTER_FILE"); + if (pc_filter_file && !__afl_filter_pcs) { + + afl_read_pc_filter_file(pc_filter_file); + + } + // Now update the pcmap. If this is the last module coming in, after all // pre-loaded code, then this will also map all of our delayed previous // modules. - - if (!__afl_pcmap_ptr) { return; } - + // for (afl_module_info_t *mod_info = __afl_module_info; mod_info; mod_info = mod_info->next) { if (mod_info->mapped) { continue; } + if (!mod_info->start) { + + fprintf(stderr, + "ERROR: __sanitizer_cov_pcs_init called with mod_info->start == " + "NULL (%s)\n", + mod_info->name); + abort(); + + } + PCTableEntry *start = (PCTableEntry *)(mod_info->pcs_beg); PCTableEntry *end = (PCTableEntry *)(mod_info->pcs_end); + if (!*mod_info->stop) { continue; } + u32 in_module_index = 0; while (start < end) { - if (mod_info->start + in_module_index >= __afl_map_size) { + if (*mod_info->start + in_module_index >= __afl_map_size) { - fprintf(stderr, "ERROR: __sanitizer_cov_pcs_init out of bounds?!\n"); + fprintf(stderr, + "ERROR: __sanitizer_cov_pcs_init out of bounds?! Start: %u " + "Stop: %u Map Size: %u (%s)\n", + *mod_info->start, *mod_info->stop, __afl_map_size, + mod_info->name); abort(); } + u32 orig_start_index = *mod_info->start; + uintptr_t PC = start->PC; // This is what `GetPreviousInstructionPc` in sanitizer runtime does @@ -1662,19 +1611,73 @@ void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, // Calculate relative offset in module PC = PC - mod_info->base_address; - __afl_pcmap_ptr[mod_info->start + in_module_index] = PC; + if (__afl_pcmap_ptr) { + + __afl_pcmap_ptr[orig_start_index + in_module_index] = PC; + + } + + if (pc_filter && !mod_info->next) { + + char PcDescr[1024]; + // This function is a part of the sanitizer run-time. + // To use it, link with AddressSanitizer or other sanitizer. + __sanitizer_symbolize_pc((void *)start->PC, "%p %F %L", PcDescr, + sizeof(PcDescr)); + + if (strstr(PcDescr, pc_filter)) { + + if (__afl_debug) + fprintf( + stderr, + "DEBUG: Selective instrumentation match: %s (PC %p Index %u)\n", + PcDescr, (void *)start->PC, + *(mod_info->start + in_module_index)); + // No change to guard needed + + } else { + + // Null out the guard to disable this edge + *(mod_info->start + in_module_index) = 0; + + } + + } + + if (__afl_filter_pcs && !mod_info->next && + strstr(mod_info->name, __afl_filter_pcs_module)) { + + u32 result_index; + if (locate_in_pcs(PC, &result_index)) { + + if (__afl_debug) + fprintf(stderr, + "DEBUG: Selective instrumentation match: (PC %lx File " + "Index %u PC Index %u)\n", + PC, result_index, in_module_index); + + } else { + + // Null out the guard to disable this edge + *(mod_info->start + in_module_index) = 0; + + } + + } start++; in_module_index++; } - mod_info->mapped = 1; + if (__afl_pcmap_ptr) { mod_info->mapped = 1; } if (__afl_debug) { - fprintf(stderr, "DEBUG: __sanitizer_cov_pcs_init initialized %u PCs\n", - in_module_index); + fprintf(stderr, + "DEBUG: __sanitizer_cov_pcs_init successfully mapped %s with %u " + "PCs\n", + mod_info->name, in_module_index); } @@ -1705,11 +1708,12 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { if (__afl_debug) { - fprintf(stderr, - "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges) " - "after_fs=%u\n", - start, stop, (unsigned long)(stop - start), - __afl_already_initialized_forkserver); + fprintf( + stderr, + "DEBUG: Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges) " + "after_fs=%u *start=%u\n", + start, stop, (unsigned long)(stop - start), + __afl_already_initialized_forkserver, *start); } @@ -1741,8 +1745,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { mod_info->id = last_module_info ? last_module_info->id + 1 : 0; mod_info->name = strdup(dlinfo.dli_fname); mod_info->base_address = (uintptr_t)dlinfo.dli_fbase; - mod_info->start = 0; - mod_info->stop = 0; + mod_info->start = NULL; + mod_info->stop = NULL; mod_info->pcs_beg = NULL; mod_info->pcs_end = NULL; mod_info->mapped = 0; @@ -1758,8 +1762,12 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } - fprintf(stderr, "[pcmap] Module: %s Base Address: %p\n", dlinfo.dli_fname, - dlinfo.dli_fbase); + if (__afl_debug) { + + fprintf(stderr, "[pcmap] Module: %s Base Address: %p\n", + dlinfo.dli_fname, dlinfo.dli_fbase); + + } } @@ -1807,7 +1815,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { u8 ignore_dso_after_fs = !!getenv("AFL_IGNORE_PROBLEMS_COVERAGE"); if (__afl_debug && ignore_dso_after_fs) { - fprintf(stderr, "Ignoring coverage from dynamically loaded code\n"); + fprintf(stderr, + "DEBUG: Ignoring coverage from dynamically loaded code\n"); } @@ -1861,12 +1870,17 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { #ifdef __AFL_CODE_COVERAGE if (mod_info) { - mod_info->start = *orig_start; - mod_info->stop = *(stop - 1); + if (!mod_info->start) { + + mod_info->start = orig_start; + mod_info->stop = stop - 1; + + } + if (__afl_debug) { fprintf(stderr, "DEBUG: [pcmap] Start Index: %u Stop Index: %u\n", - mod_info->start, mod_info->stop); + *(mod_info->start), *(mod_info->stop)); } @@ -1877,7 +1891,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { if (__afl_debug) { fprintf(stderr, - "Done __sanitizer_cov_trace_pc_guard_init: __afl_final_loc = %u\n", + "DEBUG: Done __sanitizer_cov_trace_pc_guard_init: __afl_final_loc " + "= %u\n", __afl_final_loc); } @@ -1888,7 +1903,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { if (__afl_debug) { - fprintf(stderr, "Reinit shm necessary (+%u)\n", + fprintf(stderr, "DEBUG: Reinit shm necessary (+%u)\n", __afl_final_loc - __afl_map_size); } @@ -1911,6 +1926,10 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2, uint8_t attr) { // fprintf(stderr, "hook1 arg0=%02x arg1=%02x attr=%u\n", // (u8) arg1, (u8) arg2, attr); + return; + + /* + if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); @@ -1937,11 +1956,14 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2, uint8_t attr) { __afl_cmp_map->log[k][hits].v0 = arg1; __afl_cmp_map->log[k][hits].v1 = arg2; + */ + } void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2, uint8_t attr) { - if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; + if (likely(!__afl_cmp_map)) return; + if (unlikely(arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -1979,7 +2001,8 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2, uint8_t attr) { // fprintf(stderr, "hook4 arg0=%x arg1=%x attr=%u\n", arg1, arg2, attr); - if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; + if (likely(!__afl_cmp_map)) return; + if (unlikely(arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -2017,7 +2040,8 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2, uint8_t attr) { // fprintf(stderr, "hook8 arg0=%lx arg1=%lx attr=%u\n", arg1, arg2, attr); - if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; + if (likely(!__afl_cmp_map)) return; + if (unlikely(arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -2060,7 +2084,8 @@ void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t attr, // (u64)(arg1 >> 64), (u64)arg1, (u64)(arg2 >> 64), (u64)arg2, size + 1, // attr); - if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; + if (likely(!__afl_cmp_map)) return; + if (unlikely(arg1 == arg2 || size > __afl_cmplog_max_len)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -2104,6 +2129,7 @@ void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t attr, void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2, uint8_t attr) { if (likely(!__afl_cmp_map)) return; + if (16 > __afl_cmplog_max_len) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -2143,13 +2169,13 @@ void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2, uint8_t attr) { void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { - __cmplog_ins_hook1(arg1, arg2, 0); + //__cmplog_ins_hook1(arg1, arg2, 0); } void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) { - __cmplog_ins_hook1(arg1, arg2, 0); + //__cmplog_ins_hook1(arg1, arg2, 0); } @@ -2258,11 +2284,13 @@ static int area_is_valid(void *ptr, size_t len) { if (unlikely(!ptr || __asan_region_is_poisoned(ptr, len))) { return 0; } -#ifndef __HAIKU__ - long r = syscall(SYS_write, __afl_dummy_fd[1], ptr, len); -#else +#ifdef __HAIKU__ long r = _kern_write(__afl_dummy_fd[1], -1, ptr, len); -#endif // HAIKU +#elif defined(__OpenBSD__) + long r = write(__afl_dummy_fd[1], ptr, len); +#else + long r = syscall(SYS_write, __afl_dummy_fd[1], ptr, len); +#endif // HAIKU, OPENBSD if (r <= 0 || r > len) return 0; @@ -2295,13 +2323,25 @@ void __cmplog_rtn_hook_strn(u8 *ptr1, u8 *ptr2, u64 len) { // fprintf(stderr, "RTN1 %p %p %u\n", ptr1, ptr2, len); if (likely(!__afl_cmp_map)) return; - if (unlikely(!len)) return; - int len0 = MIN(len, 31); + if (unlikely(!len || len > __afl_cmplog_max_len)) return; + + int len0 = MIN(len, 32); + int len1 = strnlen(ptr1, len0); - if (len1 < 31) len1 = area_is_valid(ptr1, len1 + 1); + if (len1 <= 32) len1 = area_is_valid(ptr1, len1 + 1); + if (len1 > __afl_cmplog_max_len) len1 = 0; + int len2 = strnlen(ptr2, len0); - if (len2 < 31) len2 = area_is_valid(ptr1, len2 + 1); - int l = MAX(len1, len2); + if (len2 <= 32) len2 = area_is_valid(ptr2, len2 + 1); + if (len2 > __afl_cmplog_max_len) len2 = 0; + + int l; + if (!len1) + l = len2; + else if (!len2) + l = len1; + else + l = MAX(len1, len2); if (l < 2) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); @@ -2345,10 +2385,18 @@ void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) { // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); if (likely(!__afl_cmp_map)) return; if (unlikely(!ptr1 || !ptr2)) return; - int len1 = strnlen(ptr1, 30) + 1; - int len2 = strnlen(ptr2, 30) + 1; - int l = MAX(len1, len2); - if (l < 3) return; + int len1 = strnlen(ptr1, 31) + 1; + int len2 = strnlen(ptr2, 31) + 1; + if (len1 > __afl_cmplog_max_len) len1 = 0; + if (len2 > __afl_cmplog_max_len) len2 = 0; + int l; + if (!len1) + l = len2; + else if (!len2) + l = len1; + else + l = MAX(len1, len2); + if (l < 2) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -2390,7 +2438,7 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { /* u32 i; - if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return; + if (area_is_valid(ptr1, 32) <= 0 || area_is_valid(ptr2, 32) <= 0) return; fprintf(stderr, "rtn arg0="); for (i = 0; i < 32; i++) fprintf(stderr, "%02x", ptr1[i]); @@ -2403,10 +2451,10 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); if (likely(!__afl_cmp_map)) return; int l1, l2; - if ((l1 = area_is_valid(ptr1, 31)) <= 0 || - (l2 = area_is_valid(ptr2, 31)) <= 0) + if ((l1 = area_is_valid(ptr1, 32)) <= 0 || + (l2 = area_is_valid(ptr2, 32)) <= 0) return; - int len = MIN(31, MIN(l1, l2)); + int len = MIN(__afl_cmplog_max_len, MIN(l1, l2)); // fprintf(stderr, "RTN2 %u\n", len); uintptr_t k = (uintptr_t)__builtin_return_address(0); @@ -2455,7 +2503,7 @@ void __cmplog_rtn_hook_n(u8 *ptr1, u8 *ptr2, u64 len) { #if 0 /* u32 i; - if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return; + if (area_is_valid(ptr1, 32) <= 0 || area_is_valid(ptr2, 32) <= 0) return; fprintf(stderr, "rtn_n len=%u arg0=", len); for (i = 0; i < len; i++) fprintf(stderr, "%02x", ptr1[i]); @@ -2467,12 +2515,15 @@ void __cmplog_rtn_hook_n(u8 *ptr1, u8 *ptr2, u64 len) { // fprintf(stderr, "RTN1 %p %p %u\n", ptr1, ptr2, len); if (likely(!__afl_cmp_map)) return; - if (unlikely(!len)) return; - int l = MIN(31, len); + if (!len) return; + int l = MIN(32, len), l1, l2; - if ((l = area_is_valid(ptr1, l)) <= 0 || (l = area_is_valid(ptr2, l)) <= 0) + if ((l1 = area_is_valid(ptr1, l)) <= 0 || (l2 = area_is_valid(ptr2, l)) <= 0) return; + len = MIN(l1, l2); + if (len > __afl_cmplog_max_len) return; + // fprintf(stderr, "RTN2 %u\n", l); uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -2663,5 +2714,52 @@ void __afl_set_persistent_mode(u8 mode) { } +// Marker: ADD_TO_INJECTIONS + +void __afl_injection_sql(u8 *buf) { + + if (likely(buf)) { + + if (unlikely(strstr((char *)buf, "'\"\"'"))) { + + fprintf(stderr, "ALERT: Detected SQL injection in query: %s\n", buf); + abort(); + + } + + } + +} + +void __afl_injection_ldap(u8 *buf) { + + if (likely(buf)) { + + if (unlikely(strstr((char *)buf, "*)(1=*))(|"))) { + + fprintf(stderr, "ALERT: Detected LDAP injection in query: %s\n", buf); + abort(); + + } + + } + +} + +void __afl_injection_xss(u8 *buf) { + + if (likely(buf)) { + + if (unlikely(strstr((char *)buf, "1\"><\""))) { + + fprintf(stderr, "ALERT: Detected XSS injection in content: %s\n", buf); + abort(); + + } + + } + +} + #undef write_error diff --git a/instrumentation/afl-gcc-cmplog-pass.so.cc b/instrumentation/afl-gcc-cmplog-pass.so.cc index b4e6fda9..774dd5fd 100644 --- a/instrumentation/afl-gcc-cmplog-pass.so.cc +++ b/instrumentation/afl-gcc-cmplog-pass.so.cc @@ -3,7 +3,7 @@ Copyright 2014-2019 Free Software Foundation, Inc Copyright 2015, 2016 Google Inc. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Copyright 2019-2023 AdaCore + Copyright 2019-2024 AdaCore Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ LLVM CmpLog pass by Andrea Fioraldi <andreafioraldi@gmail.com>, and diff --git a/instrumentation/afl-gcc-cmptrs-pass.so.cc b/instrumentation/afl-gcc-cmptrs-pass.so.cc index dbb408b0..96bd5ba8 100644 --- a/instrumentation/afl-gcc-cmptrs-pass.so.cc +++ b/instrumentation/afl-gcc-cmptrs-pass.so.cc @@ -3,7 +3,7 @@ Copyright 2014-2019 Free Software Foundation, Inc Copyright 2015, 2016 Google Inc. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Copyright 2019-2023 AdaCore + Copyright 2019-2024 AdaCore Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ LLVM CmpLog Routines pass by Andrea Fioraldi @@ -157,6 +157,9 @@ struct afl_cmptrs_pass : afl_base_pass { /* We expect it to be a record type. */ if (TREE_CODE(t) != RECORD_TYPE) return false; + /* The type has an identifier. */ + if (!TYPE_IDENTIFIER(t)) return false; + /* The type of the template is basic_string. */ if (strcmp(IDENTIFIER_POINTER(TYPE_IDENTIFIER(t)), "basic_string") != 0) return false; @@ -177,19 +180,19 @@ struct afl_cmptrs_pass : afl_base_pass { c = DECL_CONTEXT(c); if (c && TREE_CODE(c) != TRANSLATION_UNIT_DECL) return false; - /* Check that the first nonstatic data member of the record type + /* Check that the first nonstatic named data member of the record type is named _M_dataplus. */ for (c = TYPE_FIELDS(t); c; c = DECL_CHAIN(c)) - if (TREE_CODE(c) == FIELD_DECL) break; + if (TREE_CODE(c) == FIELD_DECL && DECL_NAME(c)) break; if (!c || !integer_zerop(DECL_FIELD_BIT_OFFSET(c)) || strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "_M_dataplus") != 0) return false; - /* Check that the second nonstatic data member of the record type + /* Check that the second nonstatic named data member of the record type is named _M_string_length. */ tree f2; for (f2 = DECL_CHAIN(c); f2; f2 = DECL_CHAIN(f2)) - if (TREE_CODE(f2) == FIELD_DECL) break; + if (TREE_CODE(f2) == FIELD_DECL && DECL_NAME(f2)) break; if (!f2 /* No need to check this field's offset. */ || strcmp(IDENTIFIER_POINTER(DECL_NAME(f2)), "_M_string_length") != 0) return false; @@ -201,13 +204,16 @@ struct afl_cmptrs_pass : afl_base_pass { /* Now go back to the first data member. Its type should be a record type named _Alloc_hider. */ c = TREE_TYPE(c); - if (!c || TREE_CODE(c) != RECORD_TYPE || + if (!c || TREE_CODE(c) != RECORD_TYPE || !TYPE_IDENTIFIER(t) || strcmp(IDENTIFIER_POINTER(TYPE_IDENTIFIER(c)), "_Alloc_hider") != 0) return false; - /* And its first data member is named _M_p. */ + /* And its first nonstatic named data member should be named _M_p. + There may be (unnamed) subobjects from empty base classes. We + skip the subobjects, then check the offset of the first data + member. */ for (c = TYPE_FIELDS(c); c; c = DECL_CHAIN(c)) - if (TREE_CODE(c) == FIELD_DECL) break; + if (TREE_CODE(c) == FIELD_DECL && DECL_NAME(c)) break; if (!c || !integer_zerop(DECL_FIELD_BIT_OFFSET(c)) || strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "_M_p") != 0) return false; diff --git a/instrumentation/afl-gcc-common.h b/instrumentation/afl-gcc-common.h index 1d5eb466..80ded57d 100644 --- a/instrumentation/afl-gcc-common.h +++ b/instrumentation/afl-gcc-common.h @@ -2,7 +2,7 @@ Copyright 2014-2019 Free Software Foundation, Inc Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2023 AdaCore + Copyright 2019-2024 AdaCore Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ GCC plugin. diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index 4d7fd0ef..41b1e5af 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -2,7 +2,7 @@ Copyright 2014-2019 Free Software Foundation, Inc Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2023 AdaCore + Copyright 2019-2024 AdaCore Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL LLVM pass by Laszlo Szekeres <lszekeres@google.com> and Michal diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 7f17b02d..ed9268dc 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -26,6 +26,51 @@ static std::list<std::string> allowListFunctions; static std::list<std::string> denyListFiles; static std::list<std::string> denyListFunctions; +unsigned int calcCyclomaticComplexity(llvm::Function *F) { + + unsigned int numBlocks = 0; + unsigned int numEdges = 0; + unsigned int numCalls = 0; + + // Iterate through each basic block in the function + for (BasicBlock &BB : *F) { + + // count all nodes == basic blocks + numBlocks++; + // Count the number of successors (outgoing edges) + for (BasicBlock *Succ : successors(&BB)) { + + // count edges for CC + numEdges++; + (void)(Succ); + + } + + for (Instruction &I : BB) { + + // every call is also an edge, so we need to count the calls too + if (isa<CallInst>(&I) || isa<InvokeInst>(&I)) { numCalls++; } + + } + + } + + // Cyclomatic Complexity V(G) = E - N + 2P + // For a single function, P (number of connected components) is 1 + // Calls are considered to be an edge + unsigned int CC = 2 + numCalls + numEdges - numBlocks; + + // if (debug) { + + fprintf(stderr, "CyclomaticComplexity for %s: %u\n", + F->getName().str().c_str(), CC); + + //} + + return CC; + +} + char *getBBName(const llvm::BasicBlock *BB) { static char *name; @@ -97,11 +142,15 @@ bool isIgnoreFunction(const llvm::Function *F) { static constexpr const char *ignoreSubstringList[] = { - "__asan", "__msan", "__ubsan", "__lsan", "__san", "__sanitize", - "__cxx", "DebugCounter", "DwarfDebug", "DebugLoc" + "__asan", "__msan", "__ubsan", "__lsan", "__san", + "__sanitize", "DebugCounter", "DwarfDebug", "DebugLoc" }; + // This check is very sensitive, we must be sure to not include patterns + // that are part of user-written C++ functions like the ones including + // std::string as parameter (see #1927) as the mangled type is inserted in the + // mangled name of the user-written function for (auto const &ignoreListFunc : ignoreSubstringList) { // hexcoder: F->getName().contains() not avaiilable in llvm 3.8.0 @@ -197,7 +246,7 @@ void initInstrumentList() { if (debug) DEBUGF("loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); + allowListFiles.size() / 4, allowListFunctions.size() / 4); } @@ -272,7 +321,7 @@ void initInstrumentList() { if (debug) DEBUGF("loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); + denyListFiles.size() / 4, denyListFunctions.size() / 4); } diff --git a/instrumentation/afl-llvm-common.h b/instrumentation/afl-llvm-common.h index c9324460..6b628d64 100644 --- a/instrumentation/afl-llvm-common.h +++ b/instrumentation/afl-llvm-common.h @@ -23,7 +23,7 @@ typedef long double max_align_t; #include "llvm/Support/Debug.h" #include "llvm/Support/MathExtras.h" #if LLVM_VERSION_MAJOR < 17 -#include "llvm/Transforms/IPO/PassManagerBuilder.h" + #include "llvm/Transforms/IPO/PassManagerBuilder.h" #endif #if LLVM_VERSION_MAJOR > 3 || \ @@ -55,6 +55,7 @@ void initInstrumentList(); bool isInInstrumentList(llvm::Function *F, std::string Filename); unsigned long long int calculateCollisions(uint32_t edges); void scanForDangerousFunctions(llvm::Module *M); +unsigned int calcCyclomaticComplexity(llvm::Function *F); #ifndef IS_EXTERN #define IS_EXTERN diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index 8ee13010..b93f61f0 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -4,7 +4,7 @@ Written by Marc Heuse <mh@mh-sec.de> - Copyright 2019-2023 AFLplusplus Project. All rights reserved. + Copyright 2019-2024 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. @@ -206,7 +206,18 @@ bool AFLdict2filePass::runOnModule(Module &M) { ptr = getenv("AFL_LLVM_DICT2FILE"); - if (!ptr || *ptr != '/') + if (!ptr) { + +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + auto PA = PreservedAnalyses::all(); + return PA; +#else + return true; +#endif + + } + + if (*ptr != '/') FATAL("AFL_LLVM_DICT2FILE is not set to an absolute path: %s", ptr); of.open(ptr, std::ofstream::out | std::ofstream::app); @@ -422,32 +433,35 @@ bool AFLdict2filePass::runOnModule(Module &M) { isStrstr &= FT->getNumParams() == 2 && FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0); isStrcmp &= FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0); isStrcasecmp &= FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0); isMemcmp &= FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) && FT->getParamType(0)->isPointerTy() && FT->getParamType(1)->isPointerTy() && FT->getParamType(2)->isIntegerTy(); - isStrncmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - isStrncasecmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); + isStrncmp &= + FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= + FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) && + FT->getParamType(2)->isIntegerTy(); isStdString &= FT->getNumParams() >= 2 && FT->getParamType(0)->isPointerTy() && FT->getParamType(1)->isPointerTy(); @@ -732,7 +746,7 @@ bool AFLdict2filePass::runOnModule(Module &M) { auto PA = PreservedAnalyses::all(); return PA; #else - return true; + return false; #endif } diff --git a/instrumentation/afl-llvm-lto-instrumentlist.so.cc b/instrumentation/afl-llvm-lto-instrumentlist.so.cc index db5bd55e..e0899cd3 100644 --- a/instrumentation/afl-llvm-lto-instrumentlist.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentlist.so.cc @@ -9,7 +9,7 @@ from afl-as.c are Michal's fault. Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2023 AFLplusplus Project. All rights reserved. + Copyright 2019-2024 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. @@ -45,7 +45,7 @@ #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Support/Debug.h" -//#include "llvm/Transforms/IPO/PassManagerBuilder.h" +// #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Passes/PassPlugin.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/IR/PassManager.h" diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index c59324fd..75b8532b 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -12,7 +12,7 @@ NGRAM previous location coverage comes from Adrian Herrera. Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2023 AFLplusplus Project. All rights reserved. + Copyright 2019-2024 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. @@ -128,7 +128,11 @@ llvmGetPassPluginInfo() { #if LLVM_VERSION_MAJOR <= 13 using OptimizationLevel = typename PassBuilder::OptimizationLevel; #endif + #if LLVM_VERSION_MAJOR >= 16 + PB.registerOptimizerEarlyEPCallback( + #else PB.registerOptimizerLastEPCallback( + #endif [](ModulePassManager &MPM, OptimizationLevel OL) { MPM.addPass(AFLCoverage()); @@ -212,10 +216,6 @@ bool AFLCoverage::runOnModule(Module &M) { u32 rand_seed; unsigned int cur_loc = 0; -#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ - auto PA = PreservedAnalyses::all(); -#endif - /* Setup random() so we get Actually Random(TM) outputs from AFL_R() */ gettimeofday(&tv, &tz); rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); @@ -552,7 +552,7 @@ bool AFLCoverage::runOnModule(Module &M) { #endif { - // load the context ID of the previous function and write to to a + // load the context ID of the previous function and write to a // local variable on the stack LoadInst *PrevCtxLoad = IRB.CreateLoad( #if LLVM_VERSION_MAJOR >= 14 @@ -634,7 +634,7 @@ bool AFLCoverage::runOnModule(Module &M) { /* There is a problem with Ubuntu 18.04 and llvm 6.0 (see issue #63). The inline function successors() is not inlined and also not found at runtime - :-( As I am unable to detect Ubuntu18.04 heree, the next best thing is to + :-( As I am unable to detect Ubuntu18.04 here, the next best thing is to disable this optional optimization for LLVM 6.0.0 and Linux */ #if !(LLVM_VERSION_MAJOR == 6 && LLVM_VERSION_MINOR == 0) || !defined __linux__ // only instrument if this basic block is the destination of a previous @@ -1081,7 +1081,7 @@ bool AFLCoverage::runOnModule(Module &M) { } #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ - return PA; + return PreservedAnalyses(); #else return true; #endif diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index bca1f927..fe5c2926 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -5,7 +5,7 @@ Written by Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2023 AFLplusplus Project. All rights reserved. + Copyright 2019-2024 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. @@ -90,7 +90,7 @@ class CmpLogInstructions : public ModulePass { #if LLVM_MAJOR >= 11 /* use new pass manager */ PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); #else - bool runOnModule(Module &M) override; + bool runOnModule(Module &M) override; #if LLVM_VERSION_MAJOR >= 4 StringRef getPassName() const override { @@ -165,23 +165,25 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int64Ty = IntegerType::getInt64Ty(C); IntegerType *Int128Ty = IntegerType::getInt128Ty(C); -#if LLVM_VERSION_MAJOR >= 9 - FunctionCallee -#else - Constant * -#endif - c1 = M.getOrInsertFunction("__cmplog_ins_hook1", VoidTy, Int8Ty, Int8Ty, - Int8Ty -#if LLVM_VERSION_MAJOR < 5 - , - NULL -#endif - ); -#if LLVM_VERSION_MAJOR >= 9 - FunctionCallee cmplogHookIns1 = c1; -#else - Function *cmplogHookIns1 = cast<Function>(c1); -#endif + /* + #if LLVM_VERSION_MAJOR >= 9 + FunctionCallee + #else + Constant * + #endif + c1 = M.getOrInsertFunction("__cmplog_ins_hook1", VoidTy, Int8Ty, Int8Ty, + Int8Ty + #if LLVM_VERSION_MAJOR < 5 + , + NULL + #endif + ); + #if LLVM_VERSION_MAJOR >= 9 + FunctionCallee cmplogHookIns1 = c1; + #else + Function *cmplogHookIns1 = cast<Function>(c1); + #endif + */ #if LLVM_VERSION_MAJOR >= 9 FunctionCallee @@ -619,7 +621,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { switch (cast_size) { case 8: - IRB.CreateCall(cmplogHookIns1, args); + // IRB.CreateCall(cmplogHookIns1, args); break; case 16: IRB.CreateCall(cmplogHookIns2, args); @@ -678,13 +680,16 @@ bool CmpLogInstructions::runOnModule(Module &M) { printf("Running cmplog-instructions-pass by andreafioraldi@gmail.com\n"); else be_quiet = 1; - hookInstrs(M); + bool ret = hookInstrs(M); verifyModule(M); #if LLVM_MAJOR >= 11 /* use new pass manager */ - return PreservedAnalyses::all(); + if (ret == false) + return PreservedAnalyses::all(); + else + return PreservedAnalyses(); #else - return true; + return ret; #endif } diff --git a/instrumentation/cmplog-routines-pass.cc b/instrumentation/cmplog-routines-pass.cc index 39db5aa4..560bd73b 100644 --- a/instrumentation/cmplog-routines-pass.cc +++ b/instrumentation/cmplog-routines-pass.cc @@ -5,7 +5,7 @@ Written by Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2023 AFLplusplus Project. All rights reserved. + Copyright 2019-2024 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. @@ -85,7 +85,7 @@ class CmpLogRoutines : public ModulePass { #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); #else - bool runOnModule(Module &M) override; + bool runOnModule(Module &M) override; #if LLVM_VERSION_MAJOR >= 4 StringRef getPassName() const override { @@ -385,7 +385,8 @@ bool CmpLogRoutines::hookRtns(Module &M) { isStrcmp &= FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0); bool isStrncmp = (!FuncName.compare("strncmp") || !FuncName.compare("xmlStrncmp") || @@ -398,12 +399,12 @@ bool CmpLogRoutines::hookRtns(Module &M) { !FuncName.compare("g_ascii_strncasecmp") || !FuncName.compare("Curl_strncasecompare") || !FuncName.compare("g_strncasecmp")); - isStrncmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); + isStrncmp &= + FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) && + FT->getParamType(2)->isIntegerTy(); bool isGccStdStringStdString = Callee->getName().find("__is_charIT_EE7__value") != @@ -542,7 +543,7 @@ bool CmpLogRoutines::hookRtns(Module &M) { Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); Value *v3Pbitcast = IRB.CreateBitCast( - v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits())); + v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits())); Value *v3Pcasted = IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false); args.push_back(v1Pcasted); @@ -608,7 +609,7 @@ bool CmpLogRoutines::hookRtns(Module &M) { Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); Value *v3Pbitcast = IRB.CreateBitCast( - v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits())); + v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits())); Value *v3Pcasted = IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false); args.push_back(v1Pcasted); @@ -757,16 +758,16 @@ bool CmpLogRoutines::runOnModule(Module &M) { printf("Running cmplog-routines-pass by andreafioraldi@gmail.com\n"); else be_quiet = 1; - hookRtns(M); -#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ - auto PA = PreservedAnalyses::all(); -#endif + bool ret = hookRtns(M); verifyModule(M); #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ - return PA; + if (ret == false) + return PreservedAnalyses::all(); + else + return PreservedAnalyses(); #else - return true; + return ret; #endif } diff --git a/instrumentation/cmplog-switches-pass.cc b/instrumentation/cmplog-switches-pass.cc index 38de669d..2b87ea8c 100644 --- a/instrumentation/cmplog-switches-pass.cc +++ b/instrumentation/cmplog-switches-pass.cc @@ -5,7 +5,7 @@ Written by Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2023 AFLplusplus Project. All rights reserved. + Copyright 2019-2024 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. @@ -85,7 +85,7 @@ class CmplogSwitches : public ModulePass { #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); #else - bool runOnModule(Module &M) override; + bool runOnModule(Module &M) override; #if LLVM_VERSION_MAJOR < 4 const char *getPassName() const override { @@ -442,16 +442,16 @@ bool CmplogSwitches::runOnModule(Module &M) { printf("Running cmplog-switches-pass by andreafioraldi@gmail.com\n"); else be_quiet = 1; - hookInstrs(M); -#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ - auto PA = PreservedAnalyses::all(); -#endif + bool ret = hookInstrs(M); verifyModule(M); #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ - return PA; + if (ret == false) + return PreservedAnalyses::all(); + else + return PreservedAnalyses(); #else - return true; + return ret; #endif } diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index efc99d20..496d69fc 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -89,7 +89,7 @@ class CompareTransform : public ModulePass { #endif - return "cmplog transform"; + return "compcov transform"; } @@ -123,7 +123,11 @@ llvmGetPassPluginInfo() { #if LLVM_VERSION_MAJOR <= 13 using OptimizationLevel = typename PassBuilder::OptimizationLevel; #endif + #if LLVM_VERSION_MAJOR >= 16 + PB.registerOptimizerEarlyEPCallback( + #else PB.registerOptimizerLastEPCallback( + #endif [](ModulePassManager &MPM, OptimizationLevel OL) { MPM.addPass(CompareTransform()); @@ -169,6 +173,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, DenseMap<Value *, std::string *> valueMap; std::vector<CallInst *> calls; LLVMContext &C = M.getContext(); + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); IntegerType *Int8Ty = IntegerType::getInt8Ty(C); IntegerType *Int32Ty = IntegerType::getInt32Ty(C); IntegerType *Int64Ty = IntegerType::getInt64Ty(C); @@ -225,38 +230,38 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, if (callInst->getCallingConv() != llvm::CallingConv::C) continue; StringRef FuncName = Callee->getName(); isStrcmp &= - (!FuncName.compare("strcmp") || !FuncName.compare("xmlStrcmp") || + (!FuncName.compare("strcmp") /*|| !FuncName.compare("xmlStrcmp") || !FuncName.compare("xmlStrEqual") || - !FuncName.compare("g_strcmp0") || !FuncName.compare("curl_strequal") || - !FuncName.compare("strcsequal")); + !FuncName.compare("strcsequal") || + !FuncName.compare("g_strcmp0")*/); isMemcmp &= (!FuncName.compare("memcmp") || !FuncName.compare("bcmp") || !FuncName.compare("CRYPTO_memcmp") || !FuncName.compare("OPENSSL_memcmp") || !FuncName.compare("memcmp_const_time") || !FuncName.compare("memcmpct")); - isStrncmp &= (!FuncName.compare("strncmp") || - !FuncName.compare("xmlStrncmp") || - !FuncName.compare("curl_strnequal")); + isStrncmp &= (!FuncName.compare("strncmp")/* || + !FuncName.compare("curl_strnequal") || + !FuncName.compare("xmlStrncmp")*/); isStrcasecmp &= (!FuncName.compare("strcasecmp") || !FuncName.compare("stricmp") || !FuncName.compare("ap_cstr_casecmp") || !FuncName.compare("OPENSSL_strcasecmp") || - !FuncName.compare("xmlStrcasecmp") || + /*!FuncName.compare("xmlStrcasecmp") || !FuncName.compare("g_strcasecmp") || !FuncName.compare("g_ascii_strcasecmp") || !FuncName.compare("Curl_strcasecompare") || - !FuncName.compare("Curl_safe_strcasecompare") || + !FuncName.compare("Curl_safe_strcasecompare") ||*/ !FuncName.compare("cmsstrcasecmp")); isStrncasecmp &= (!FuncName.compare("strncasecmp") || !FuncName.compare("strnicmp") || !FuncName.compare("ap_cstr_casecmpn") || - !FuncName.compare("OPENSSL_strncasecmp") || + !FuncName.compare("OPENSSL_strncasecmp") /*|| !FuncName.compare("xmlStrncasecmp") || !FuncName.compare("g_ascii_strncasecmp") || !FuncName.compare("Curl_strncasecompare") || - !FuncName.compare("g_strncasecmp")); + !FuncName.compare("g_strncasecmp")*/); isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && @@ -270,28 +275,30 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, isStrcmp &= FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0); isStrcasecmp &= FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0); isMemcmp &= FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) && FT->getParamType(0)->isPointerTy() && FT->getParamType(1)->isPointerTy() && FT->getParamType(2)->isIntegerTy(); - isStrncmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - isStrncasecmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); + isStrncmp &= + FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= + FT->getNumParams() == 3 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8Ty(M.getContext())->getPointerTo(0) && + FT->getParamType(2)->isIntegerTy(); if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp && !isIntMemcpy) @@ -457,8 +464,21 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, bool isSizedcmp = false; bool isCaseInsensitive = false; bool needs_null = false; + bool success_is_one = false; + bool nullCheck = false; Function *Callee = callInst->getCalledFunction(); + /* + fprintf(stderr, "%s - %s - %s\n", + callInst->getParent() + ->getParent() + ->getParent() + ->getName() + .str() + .c_str(), + callInst->getParent()->getParent()->getName().str().c_str(), + Callee ? Callee->getName().str().c_str() : "NULL");*/ + if (Callee) { if (!Callee->getName().compare("memcmp") || @@ -503,9 +523,20 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, !Callee->getName().compare("g_strncasecmp")) isCaseInsensitive = true; + if (!Callee->getName().compare("xmlStrEqual") || + !Callee->getName().compare("curl_strequal") || + !Callee->getName().compare("strcsequal") || + !Callee->getName().compare("curl_strnequal")) + success_is_one = true; + } if (!isSizedcmp) needs_null = true; + if (Callee->getName().startswith("g_") || + Callee->getName().startswith("curl_") || + Callee->getName().startswith("Curl_") || + Callee->getName().startswith("xml")) + nullCheck = true; Value *sizedValue = isSizedcmp ? callInst->getArgOperand(2) : NULL; bool isConstSized = sizedValue && isa<ConstantInt>(sizedValue); @@ -590,8 +621,10 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, /* split before the call instruction */ BasicBlock *bb = callInst->getParent(); BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(callInst)); - BasicBlock *next_lenchk_bb = NULL; + + if (nullCheck) { fprintf(stderr, "TODO: null check\n"); } + if (isSizedcmp && !isConstSized) { next_lenchk_bb = @@ -623,7 +656,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, IRBuilder<> cur_lenchk_IRB(&*(cur_lenchk_bb->getFirstInsertionPt())); Value *icmp = cur_lenchk_IRB.CreateICmpEQ( - sizedValue, ConstantInt::get(sizedValue->getType(), i)); + sizedValue, ConstantInt::get(sizedValue->getType(), i)); cur_lenchk_IRB.CreateCondBr(icmp, end_bb, cur_cmp_bb); cur_lenchk_bb->getTerminator()->eraseFromParent(); @@ -667,6 +700,14 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, else isub = cur_cmp_IRB.CreateSub(load, ConstantInt::get(Int8Ty, c)); + if (success_is_one && i == unrollLen - 1) { + + Value *isubsub = cur_cmp_IRB.CreateTrunc(isub, Int1Ty); + isub = cur_cmp_IRB.CreateSelect(isubsub, ConstantInt::get(Int8Ty, 0), + ConstantInt::get(Int8Ty, 1)); + + } + Value *sext = cur_cmp_IRB.CreateSExt(isub, Int32Ty); PN->addIncoming(sext, cur_cmp_bb); @@ -728,6 +769,8 @@ bool CompareTransform::runOnModule(Module &M) { #endif + bool ret = false; + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) printf( "Running compare-transform-pass by laf.intel@gmail.com, extended by " @@ -735,11 +778,7 @@ bool CompareTransform::runOnModule(Module &M) { else be_quiet = 1; -#if LLVM_MAJOR >= 11 /* use new pass manager */ - auto PA = PreservedAnalyses::all(); -#endif - - transformCmps(M, true, true, true, true, true); + if (transformCmps(M, true, true, true, true, true) == true) ret = true; verifyModule(M); #if LLVM_MAJOR >= 11 /* use new pass manager */ @@ -749,9 +788,18 @@ bool CompareTransform::runOnModule(Module &M) { }*/ - return PA; + if (ret == true) { + + return PreservedAnalyses(); + + } else { + + return PreservedAnalyses::all(); + + } + #else - return true; + return ret; #endif } diff --git a/instrumentation/injection-pass.cc b/instrumentation/injection-pass.cc new file mode 100644 index 00000000..47ddabd9 --- /dev/null +++ b/instrumentation/injection-pass.cc @@ -0,0 +1,369 @@ +/* + american fuzzy lop++ - LLVM Injection instrumentation + -------------------------------------------------- + + Written by Marc Heuse <mh@mh-sec.de> + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2024 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: + + https://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#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" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + #include "llvm/Passes/PassPlugin.h" + #include "llvm/Passes/PassBuilder.h" + #include "llvm/IR/PassManager.h" +#else + #include "llvm/IR/LegacyPassManager.h" + #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#endif +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#if LLVM_VERSION_MAJOR < 17 + #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#endif +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ValueTracking.h" + +#include "llvm/IR/IRBuilder.h" +#if LLVM_VERSION_MAJOR >= 4 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/DebugInfo.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/DebugInfo.h" + #define nullptr 0 +#endif + +#include <set> +#include "afl-llvm-common.h" + +using namespace llvm; + +namespace { + +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ +class InjectionRoutines : public PassInfoMixin<InjectionRoutines> { + + public: + InjectionRoutines() { + +#else +class InjectionRoutines : public ModulePass { + + public: + static char ID; + InjectionRoutines() : ModulePass(ID) { + +#endif + + initInstrumentList(); + + } + +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); +#else + bool runOnModule(Module &M) override; + + #if LLVM_VERSION_MAJOR >= 4 + StringRef getPassName() const override { + + #else + const char *getPassName() const override { + + #endif + return "Injection routines"; + + } + +#endif + + private: + bool hookRtns(Module &M); + + bool doSQL = false; + bool doLDAP = false; + bool doXSS = false; + +}; + +} // namespace + +#if LLVM_MAJOR >= 11 +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK +llvmGetPassPluginInfo() { + + return {LLVM_PLUGIN_API_VERSION, "Injectionroutines", "v0.1", + /* lambda to insert our pass into the pass pipeline. */ + [](PassBuilder &PB) { + + #if LLVM_VERSION_MAJOR <= 13 + using OptimizationLevel = typename PassBuilder::OptimizationLevel; + #endif + PB.registerOptimizerLastEPCallback( + [](ModulePassManager &MPM, OptimizationLevel OL) { + + MPM.addPass(InjectionRoutines()); + + }); + + }}; + +} + +#else +char InjectionRoutines::ID = 0; +#endif + +bool InjectionRoutines::hookRtns(Module &M) { + + std::vector<CallInst *> calls, llvmStdStd, llvmStdC, gccStdStd, gccStdC, + Memcmp, Strcmp, Strncmp; + LLVMContext &C = M.getContext(); + + Type *VoidTy = Type::getVoidTy(C); + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + PointerType *i8PtrTy = PointerType::get(Int8Ty, 0); + +#if LLVM_VERSION_MAJOR >= 9 + FunctionCallee +#else + Constant * +#endif + c1 = M.getOrInsertFunction("__afl_injection_sql", VoidTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR >= 9 + FunctionCallee sqlfunc = c1; +#else + Function *sqlfunc = cast<Function>(c1); +#endif + +#if LLVM_VERSION_MAJOR >= 9 + FunctionCallee +#else + Constant * +#endif + c2 = M.getOrInsertFunction("__afl_injection_ldap", VoidTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR >= 9 + FunctionCallee ldapfunc = c2; +#else + Function *ldapfunc = cast<Function>(c2); +#endif + +#if LLVM_VERSION_MAJOR >= 9 + FunctionCallee +#else + Constant * +#endif + c3 = M.getOrInsertFunction("__afl_injection_xss", VoidTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR >= 9 + FunctionCallee xssfunc = c3; +#else + Function *xssfunc = cast<Function>(c3); +#endif + +#if LLVM_VERSION_MAJOR >= 9 + FunctionCallee FuncPtr; +#else + Function *FuncPtr; +#endif + + bool ret = false; + + /* iterate over all functions, bbs and instruction and add suitable calls */ + for (auto &F : M) { + + if (!isInInstrumentList(&F, MNAME)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast<CallInst>(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + + std::string FuncName = Callee->getName().str(); + FuncPtr = nullptr; + size_t param = 0; + + // Marker: ADD_TO_INJECTIONS + // If you just need to add another function to test for SQL etc. + // then add them here. + // To add a new class or to work on e.g. std::string/Rust strings/... + // you will need to add a function to afl-compiler-rt.c.o and + // and upwards in this file add a pointer to that function to use + // here. + + if (doSQL && + (FuncName.compare("sqlite3_exec") == 0 || + FuncName.compare("PQexec") == 0 || FuncName.compare("") == 0 || + FuncName.compare("PQexecParams") == 0 || + FuncName.compare("mysql_query") == 0)) { + + if (!be_quiet) { + + errs() << "Injection SQL hook: " << FuncName << "\n"; + + } + + FuncPtr = sqlfunc; + param = 1; + + } + + if (doLDAP && (FuncName.compare("ldap_search_ext") == 0 || + FuncName.compare("ldap_search_ext_s") == 0)) { + + if (!be_quiet) { + + errs() << "Injection LDAP hook: " << FuncName << "\n"; + + } + + FuncPtr = ldapfunc; + param = 1; + + } + + if (doXSS && (FuncName.compare("htmlReadMemory") == 0)) { + + if (!be_quiet) { + + errs() << "Injection XSS hook: " << FuncName << "\n"; + + } + + FuncPtr = xssfunc; + param = 1; + + } + + if (FuncPtr) { + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + ret = true; + + Value *parameter = callInst->getArgOperand(param); + + std::vector<Value *> args; + Value *casted = IRB.CreatePointerCast(parameter, i8PtrTy); + args.push_back(casted); + IRB.CreateCall(FuncPtr, args); + + } + + } + + } + + } + + } + + return ret; + +} + +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ +PreservedAnalyses InjectionRoutines::run(Module &M, + ModuleAnalysisManager &MAM) { + +#else +bool InjectionRoutines::runOnModule(Module &M) { + +#endif + + if (getenv("AFL_QUIET") == NULL) + printf("Running injection-pass by Marc Heuse (mh@mh-sec.de)\n"); + else + be_quiet = 1; + if (getenv("AFL_LLVM_INJECTIONS_ALL")) { + + doSQL = true; + doLDAP = true; + doXSS = true; + + } + + if (getenv("AFL_LLVM_INJECTIONS_SQL")) { doSQL = true; } + if (getenv("AFL_LLVM_INJECTIONS_LDAP")) { doLDAP = true; } + if (getenv("AFL_LLVM_INJECTIONS_XSS")) { doXSS = true; } + + bool ret = hookRtns(M); + verifyModule(M); + +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + if (ret == false) + return PreservedAnalyses::all(); + else + return PreservedAnalyses(); +#else + return ret; +#endif + +} + +#if LLVM_VERSION_MAJOR < 11 /* use old pass manager */ +static void registerInjectionRoutinesPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new InjectionRoutines(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterInjectionRoutinesPass( + PassManagerBuilder::EP_OptimizerLast, registerInjectionRoutinesPass); + +static RegisterStandardPasses RegisterInjectionRoutinesPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerInjectionRoutinesPass); + + #if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterInjectionRoutinesPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerInjectionRoutinesPass); + #endif +#endif + diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index 8a07610c..effafe50 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -1,7 +1,7 @@ /* * Copyright 2016 laf-intel - * extended for floating point by Heiko Eißfeldt - * adapted to new pass manager by Heiko Eißfeldt + * extended for floating point by Heiko Eissfeldt + * adapted to new pass manager by Heiko Eissfeldt * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ using namespace llvm; // uncomment this toggle function verification at each step. horribly slow, but // helps to pinpoint a potential problem in the splitting code. -//#define VERIFY_TOO_MUCH 1 +// #define VERIFY_TOO_MUCH 1 namespace { @@ -189,7 +189,11 @@ llvmGetPassPluginInfo() { #if LLVM_VERSION_MAJOR <= 13 using OptimizationLevel = typename PassBuilder::OptimizationLevel; #endif + #if LLVM_VERSION_MAJOR >= 16 + PB.registerOptimizerEarlyEPCallback( + #else PB.registerOptimizerLastEPCallback( + #endif [](ModulePassManager &MPM, OptimizationLevel OL) { MPM.addPass(SplitComparesTransform()); @@ -262,8 +266,11 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) { /* this is probably not needed but we do it anyway */ if (TyOp0 != TyOp1) { continue; } - if (TyOp0->isArrayTy() || TyOp0->isVectorTy()) { continue; } + int constants = 0; + if (llvm::isa<llvm::Constant>(op0)) { ++constants; } + if (llvm::isa<llvm::Constant>(op1)) { ++constants; } + if (constants != 1) { continue; } fcomps.push_back(selectcmpInst); @@ -463,8 +470,12 @@ bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst *IcmpInst, #else ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); #endif + if (new_pred == CmpInst::ICMP_SGT || new_pred == CmpInst::ICMP_SLT) { + + simplifySignedCompare(icmp_np, M, worklist); + + } - worklist.push_back(icmp_np); worklist.push_back(icmp_eq); return true; @@ -740,17 +751,24 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, CmpInst *icmp_inv_cmp = nullptr; BasicBlock *inv_cmp_bb = BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb); - if (pred == CmpInst::ICMP_UGT || pred == CmpInst::ICMP_SGT || - pred == CmpInst::ICMP_UGE || pred == CmpInst::ICMP_SGE) { + if (pred == CmpInst::ICMP_UGT) { icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, op0_high, op1_high); - } else { + } else if (pred == CmpInst::ICMP_ULT) { icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, op0_high, op1_high); + } else { + + // Never gonna appen + if (!be_quiet) + fprintf(stderr, + "Error: split-compare: Equals or signed not removed: %d\n", + pred); + } #if LLVM_MAJOR >= 16 @@ -924,7 +942,7 @@ size_t SplitComparesTransform::nextPowerOfTwo(size_t in) { /* splits fcmps into two nested fcmps with sign compare and the rest */ size_t SplitComparesTransform::splitFPCompares(Module &M) { - size_t count = 0; + size_t counts = 0; LLVMContext &C = M.getContext(); @@ -940,7 +958,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { } else { - return count; + return counts; } @@ -993,7 +1011,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { } - if (!fcomps.size()) { return count; } + if (!fcomps.size()) { return counts; } IntegerType *Int1Ty = IntegerType::getInt1Ty(C); @@ -1573,7 +1591,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1); #if LLVM_MAJOR >= 16 icmp_fraction_result->insertInto(negative_bb, negative_bb->end()); - icmp_fraction_result2->insertInto(positive_bb, negative_bb->end()); + icmp_fraction_result2->insertInto(positive_bb, positive_bb->end()); #else negative_bb->getInstList().push_back(icmp_fraction_result); positive_bb->getInstList().push_back(icmp_fraction_result2); @@ -1587,7 +1605,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1); #if LLVM_MAJOR >= 16 icmp_fraction_result->insertInto(negative_bb, negative_bb->end()); - icmp_fraction_result2->insertInto(positive_bb, negative_bb->end()); + icmp_fraction_result2->insertInto(positive_bb, positive_bb->end()); #else negative_bb->getInstList().push_back(icmp_fraction_result); positive_bb->getInstList().push_back(icmp_fraction_result2); @@ -1679,11 +1697,11 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { #else ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); #endif - ++count; + ++counts; } - return count; + return counts; } @@ -1696,12 +1714,6 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif - char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); - if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); - if (bitw_env) { target_bitwidth = atoi(bitw_env); } - - enableFPSplit = getenv("AFL_LLVM_LAF_SPLIT_FLOATS") != NULL; - if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) { @@ -1717,10 +1729,27 @@ bool SplitComparesTransform::runOnModule(Module &M) { } -#if LLVM_MAJOR >= 11 - auto PA = PreservedAnalyses::all(); + char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); + if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); + if (bitw_env) { target_bitwidth = atoi(bitw_env); } + + if (getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { enableFPSplit = true; } + + bool split_comp = false; + + if (getenv("AFL_LLVM_LAF_SPLIT_COMPARES")) { + +#if LLVM_MAJOR == 17 + if (!be_quiet) + fprintf(stderr, + "WARNING: AFL++ splitting integer comparisons is disabled in " + "LLVM 17 due bugs, switch to 16 or 18!\n"); +#else + split_comp = true; #endif + } + if (enableFPSplit) { simplifyFPCompares(M); @@ -1729,42 +1758,44 @@ bool SplitComparesTransform::runOnModule(Module &M) { if (!be_quiet && !debug) { errs() << "Split-floatingpoint-compare-pass: " << count - << " FP comparisons splitted\n"; + << " FP comparisons split\n"; } } - std::vector<CmpInst *> worklist; - /* iterate over all functions, bbs and instruction search for all integer - * compare instructions. Save them into the worklist for later. */ - for (auto &F : M) { + if (split_comp) { - if (!isInInstrumentList(&F, MNAME)) continue; + std::vector<CmpInst *> worklist; + /* iterate over all functions, bbs and instruction search for all integer + * compare instructions. Save them into the worklist for later. */ + for (auto &F : M) { - for (auto &BB : F) { + if (!isInInstrumentList(&F, MNAME)) continue; - for (auto &IN : BB) { + for (auto &BB : F) { - if (auto CI = dyn_cast<CmpInst>(&IN)) { + for (auto &IN : BB) { - auto op0 = CI->getOperand(0); - auto op1 = CI->getOperand(1); - if (!op0 || !op1) { + if (auto CI = dyn_cast<CmpInst>(&IN)) { -#if LLVM_MAJOR >= 11 - return PA; -#else - return false; -#endif + auto op0 = CI->getOperand(0); + auto op1 = CI->getOperand(1); + // has to valid operands + if (!op0 || !op1) { continue; } + // has exactly one constant and one variable + int constants = 0; + if (dyn_cast<ConstantInt>(op0)) { ++constants; } + if (dyn_cast<ConstantInt>(op1)) { ++constants; } + if (constants != 1) { continue; } - } + auto iTy1 = dyn_cast<IntegerType>(op0->getType()); + if (iTy1 && isa<IntegerType>(op1->getType())) { - auto iTy1 = dyn_cast<IntegerType>(op0->getType()); - if (iTy1 && isa<IntegerType>(op1->getType())) { + unsigned bitw = iTy1->getBitWidth(); + if (isSupportedBitWidth(bitw)) { worklist.push_back(CI); } - unsigned bitw = iTy1->getBitWidth(); - if (isSupportedBitWidth(bitw)) { worklist.push_back(CI); } + } } @@ -1774,16 +1805,18 @@ bool SplitComparesTransform::runOnModule(Module &M) { } - } + // now that we have a list of all integer comparisons we can start replacing + // them with the splitted alternatives. + for (auto CI : worklist) { - // now that we have a list of all integer comparisons we can start replacing - // them with the splitted alternatives. - for (auto CI : worklist) { + simplifyAndSplit(CI, M); - simplifyAndSplit(CI, M); + } } + bool ret = count == 0 ? false : true; + bool brokenDebug = false; if (verifyModule(M, &errs() #if LLVM_VERSION_MAJOR >= 4 || \ @@ -1822,9 +1855,12 @@ bool SplitComparesTransform::runOnModule(Module &M) { }*/ - return PA; + if (ret == false) + return PreservedAnalyses::all(); + else + return PreservedAnalyses(); #else - return true; + return ret; #endif } diff --git a/instrumentation/split-switches-pass.so.cc b/instrumentation/split-switches-pass.so.cc index dcd89652..aa552a42 100644 --- a/instrumentation/split-switches-pass.so.cc +++ b/instrumentation/split-switches-pass.so.cc @@ -84,7 +84,7 @@ class SplitSwitchesTransform : public ModulePass { #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); #else - bool runOnModule(Module &M) override; + bool runOnModule(Module &M) override; #if LLVM_VERSION_MAJOR >= 4 StringRef getPassName() const override { @@ -137,7 +137,11 @@ llvmGetPassPluginInfo() { #if LLVM_VERSION_MAJOR <= 13 using OptimizationLevel = typename PassBuilder::OptimizationLevel; #endif + #if LLVM_VERSION_MAJOR >= 16 + PB.registerOptimizerEarlyEPCallback( + #else PB.registerOptimizerLastEPCallback( + #endif [](ModulePassManager &MPM, OptimizationLevel OL) { MPM.addPass(SplitSwitchesTransform()); @@ -516,11 +520,7 @@ bool SplitSwitchesTransform::runOnModule(Module &M) { else be_quiet = 1; -#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ - auto PA = PreservedAnalyses::all(); -#endif - - splitSwitches(M); + bool ret = splitSwitches(M); verifyModule(M); #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ @@ -530,9 +530,12 @@ bool SplitSwitchesTransform::runOnModule(Module &M) { }*/ - return PA; + if (ret == false) + return PreservedAnalyses::all(); + else + return PreservedAnalyses(); #else - return true; + return ret; #endif } |