aboutsummaryrefslogtreecommitdiff
path: root/instrumentation
diff options
context:
space:
mode:
authorAlexander Shvedov <60114847+a-shvedov@users.noreply.github.com>2024-05-30 10:43:01 +0300
committerGitHub <noreply@github.com>2024-05-30 10:43:01 +0300
commitf8a5f1cd9ea907654f42fa06ce6b6bfd4b8c1b13 (patch)
tree7aec2a095a30ed609ce96f85ec3c4e0a8b8eb74c /instrumentation
parent629edb1e78d791894ce9ee6d53259f95fe1a29af (diff)
parente7d871c8bf64962a658e447b90a1a3b43aaddc28 (diff)
downloadafl++-f8a5f1cd9ea907654f42fa06ce6b6bfd4b8c1b13.tar.gz
Merge branch 'AFLplusplus:stable' into stable
Diffstat (limited to 'instrumentation')
-rw-r--r--instrumentation/README.injections.md48
-rw-r--r--instrumentation/README.llvm.md2
-rw-r--r--instrumentation/README.lto.md12
-rw-r--r--instrumentation/README.persistent_mode.md32
-rw-r--r--instrumentation/SanitizerCoverageLTO.so.cc845
-rw-r--r--instrumentation/SanitizerCoveragePCGUARD.so.cc597
-rw-r--r--instrumentation/afl-compiler-rt.o.c950
-rw-r--r--instrumentation/afl-gcc-cmplog-pass.so.cc2
-rw-r--r--instrumentation/afl-gcc-cmptrs-pass.so.cc22
-rw-r--r--instrumentation/afl-gcc-common.h2
-rw-r--r--instrumentation/afl-gcc-pass.so.cc2
-rw-r--r--instrumentation/afl-llvm-common.cc57
-rw-r--r--instrumentation/afl-llvm-common.h3
-rw-r--r--instrumentation/afl-llvm-dict2file.so.cc50
-rw-r--r--instrumentation/afl-llvm-lto-instrumentlist.so.cc4
-rw-r--r--instrumentation/afl-llvm-pass.so.cc16
-rw-r--r--instrumentation/cmplog-instructions-pass.cc51
-rw-r--r--instrumentation/cmplog-routines-pass.cc35
-rw-r--r--instrumentation/cmplog-switches-pass.cc16
-rw-r--r--instrumentation/compare-transform-pass.so.cc116
-rw-r--r--instrumentation/injection-pass.cc369
-rw-r--r--instrumentation/split-compares-pass.so.cc140
-rw-r--r--instrumentation/split-switches-pass.so.cc19
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
}