about summary refs log tree commit diff
path: root/instrumentation
diff options
context:
space:
mode:
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
 
 }