aboutsummaryrefslogtreecommitdiff
path: root/instrumentation
diff options
context:
space:
mode:
Diffstat (limited to 'instrumentation')
-rw-r--r--instrumentation/README.gcc_plugin.md9
-rw-r--r--instrumentation/README.injections.md48
-rw-r--r--instrumentation/README.llvm.md34
-rw-r--r--instrumentation/README.lto.md129
-rw-r--r--instrumentation/SanitizerCoverageLTO.so.cc222
-rw-r--r--instrumentation/SanitizerCoveragePCGUARD.so.cc705
-rw-r--r--instrumentation/afl-compiler-rt.o.c901
-rw-r--r--instrumentation/afl-gcc-cmplog-pass.so.cc404
-rw-r--r--instrumentation/afl-gcc-cmptrs-pass.so.cc369
-rw-r--r--instrumentation/afl-gcc-common.h508
-rw-r--r--instrumentation/afl-llvm-common.cc20
-rw-r--r--instrumentation/afl-llvm-common.h11
-rw-r--r--instrumentation/afl-llvm-dict2file.so.cc81
-rw-r--r--instrumentation/afl-llvm-lto-instrumentlist.so.cc8
-rw-r--r--instrumentation/afl-llvm-pass.so.cc22
-rw-r--r--instrumentation/cmplog-instructions-pass.cc50
-rw-r--r--instrumentation/cmplog-routines-pass.cc59
-rw-r--r--instrumentation/cmplog-switches-pass.cc14
-rw-r--r--instrumentation/compare-transform-pass.so.cc76
-rw-r--r--instrumentation/injection-pass.cc366
-rw-r--r--instrumentation/split-compares-pass.so.cc424
-rw-r--r--instrumentation/split-switches-pass.so.cc38
22 files changed, 3465 insertions, 1033 deletions
diff --git a/instrumentation/README.gcc_plugin.md b/instrumentation/README.gcc_plugin.md
index ed39af9d..011a574a 100644
--- a/instrumentation/README.gcc_plugin.md
+++ b/instrumentation/README.gcc_plugin.md
@@ -99,4 +99,11 @@ See
## 6) Bonus feature #3: selective instrumentation
It can be more effective to fuzzing to only instrument parts of the code. For
-details, see [README.instrument_list.md](README.instrument_list.md). \ No newline at end of file
+details, see [README.instrument_list.md](README.instrument_list.md).
+
+## 7) Bonus feature #4: CMPLOG
+
+The gcc_plugin also support CMPLOG/Redqueen, just set `AFL_GCC_CMPLOG` before
+instrumenting the target.
+Read more about this in the llvm document.
+
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 7855a987..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
@@ -116,7 +116,7 @@ PCGUARD analysis.
Several options are present to make llvm_mode faster or help it rearrange the
code to make afl-fuzz path discovery easier.
-If you need just to instrument specific parts of the code, you can the
+If you need just to instrument specific parts of the code, you can create the
instrument file list which C/C++ files to actually instrument. See
[README.instrument_list.md](README.instrument_list.md)
@@ -167,6 +167,10 @@ Just specify `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` and during compilation
all constant string compare parameters will be written to this file to be used
with afl-fuzz' `-x` option.
+Adding `AFL_LLVM_DICT2FILE_NO_MAIN=1` will skip parsing `main()` which often
+does command line parsing which has string comparisons that are not helpful
+for fuzzing.
+
## 6) AFL++ Context Sensitive Branch Coverage
### What is this?
@@ -275,4 +279,28 @@ then this can give a small performance boost.
Please note that the default counter implementations are not thread safe!
Support for thread safe counters in mode LLVM CLASSIC can be activated with
-setting `AFL_LLVM_THREADSAFE_INST=1`. \ No newline at end of file
+setting `AFL_LLVM_THREADSAFE_INST=1`.
+
+## 8) Source code coverage through instrumentation
+
+Measuring source code coverage is a common task in fuzzing, but it is very
+difficut to do in some situations (e.g. when using snapshot fuzzing).
+
+When using the `AFL_LLVM_INSTRUMENT=llvm-codecov` option, afl-cc will use
+native trace-pc-guard instrumentation but additionally select options that
+are required to utilize the instrumentation for source code coverage.
+
+In particular, it will switch the instrumentation to be per basic block
+instead of instrumenting edges, disable all guard pruning and enable the
+experimental pc-table support that allows the runtime to gather 100% of
+instrumented basic blocks at start, including their locations.
+
+Note: You must compile AFL with the `CODE_COVERAGE=1` option to enable the
+respective parts in the AFL compiler runtime. Support is currently only
+implemented for Nyx, but can in theory also work without Nyx.
+
+Note: You might have to adjust `MAP_SIZE_POW2` in include/config.h to ensure
+that your coverage map is large enough to hold all basic blocks of your
+target program without any collisions.
+
+More documentation on how to utilize this with Nyx will follow.
diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md
index a20175b1..bd479c26 100644
--- a/instrumentation/README.lto.md
+++ b/instrumentation/README.lto.md
@@ -2,52 +2,53 @@
## TL;DR:
-This version requires a current llvm 11+ compiled from the GitHub master.
+This version requires a LLVM 12 or newer.
-1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better
- coverage than anything else that is out there in the AFL world.
+1. Use afl-clang-lto/afl-clang-lto++ because the resulting binaries run
+ slightly faster and give better coverage.
-2. You can use it together with llvm_mode: laf-intel and the instrument file
- listing features and can be combined with cmplog/Redqueen.
+2. You can use it together with COMPCOV, COMPLOG and the instrument file
+ listing features.
-3. It only works with llvm 11+.
+3. It only works with LLVM 12 or newer.
-4. AUTODICTIONARY feature (see below)!
+4. AUTODICTIONARY feature (see below)
-5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. Some
- targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
+5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib AS=llvm-as`.
+ Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
## Introduction and problem description
-A big issue with how AFL++ works is that the basic block IDs that are set during
-compilation are random - and hence naturally the larger the number of
-instrumented locations, the higher the number of edge collisions are in the map.
-This can result in not discovering new paths and therefore degrade the
+A big issue with how vanilla AFL worked was that the basic block IDs that are
+set during compilation are random - and hence naturally the larger the number
+of instrumented locations, the higher the number of edge collisions are in the
+map. This can result in not discovering new paths and therefore degrade the
efficiency of the fuzzing process.
-*This issue is underestimated in the fuzzing community!* With a 2^16 = 64kb
+*This issue is underestimated in the fuzzing community* With a 2^16 = 64kb
standard map at already 256 instrumented blocks, there is on average one
collision. On average, a target has 10.000 to 50.000 instrumented blocks, hence
the real collisions are between 750-18.000!
-To reach a solution that prevents any collisions took several approaches and
-many dead ends until we got to this:
+Note that PCGUARD (our own modified implementation and the SANCOV PCGUARD
+implementation from libfuzzer) also provides collision free coverage.
+It is a bit slower though and can a few targets with very early constructors.
* We instrument at link time when we have all files pre-compiled.
* To instrument at link time, we compile in LTO (link time optimization) mode.
* Our compiler (afl-clang-lto/afl-clang-lto++) takes care of setting the correct
LTO options and runs our own afl-ld linker instead of the system linker.
* The LLVM linker collects all LTO files to link and instruments them so that we
- have non-colliding edge overage.
+ have non-colliding edge coverage.
* We use a new (for afl) edge coverage - which is the same as in llvm
-fsanitize=coverage edge coverage mode. :)
The result:
* 10-25% speed gain compared to llvm_mode
-* guaranteed non-colliding edge coverage :-)
+* guaranteed non-colliding edge coverage
* The compile time, especially for binaries to an instrumented library, can be
- much longer.
+ much (and sometimes much much) longer.
Example build output from a libtiff build:
@@ -59,70 +60,30 @@ 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 version 11 or 12
+### Installing llvm
-llvm 11 or even 12 should be available in all current Linux repositories. If you
-use an outdated Linux distribution, read the next section.
-
-### Installing llvm from the llvm repository (version 12+)
-
-Installing the llvm snapshot builds is easy and mostly painless:
-
-In the following line, change `NAME` for your Debian or Ubuntu release name
-(e.g., buster, focal, eon, etc.):
+The best way to install LLVM is to follow [https://apt.llvm.org/](https://apt.llvm.org/)
+e.g. for LLVM 15:
```
-echo deb http://apt.llvm.org/NAME/ llvm-toolchain-NAME NAME >> /etc/apt/sources.list
+wget https://apt.llvm.org/llvm.sh
+chmod +x llvm.sh
+sudo ./llvm.sh 15 all
```
-Then add the pgp key of llvm and install the packages:
+LLVM 12 to 18 should be available in all current Linux repositories.
-```
-wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
-apt-get update && apt-get upgrade -y
-apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \
- libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \
- libclang-common-12-dev libclang-cpp12 libclang-cpp12-dev liblld-12 \
- liblld-12-dev liblldb-12 liblldb-12-dev libllvm12 libomp-12-dev \
- libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools
-```
+## How to build afl-clang-lto
+
+That part is easy.
+Just set `LLVM_CONFIG` to the llvm-config-VERSION and build AFL++, e.g. for
+LLVM 15:
-### Building llvm yourself (version 12+)
-
-Building llvm from GitHub takes quite some time and is not painless:
-
-```sh
-sudo apt install binutils-dev # this is *essential*!
-git clone --depth=1 https://github.com/llvm/llvm-project
-cd llvm-project
-mkdir build
-cd build
-
-# Add -G Ninja if ninja-build installed
-# "Building with ninja significantly improves your build time, especially with
-# incremental builds, and improves your memory usage."
-cmake \
- -DCLANG_INCLUDE_DOCS="OFF" \
- -DCMAKE_BUILD_TYPE=Release \
- -DLLVM_BINUTILS_INCDIR=/usr/include/ \
- -DLLVM_BUILD_LLVM_DYLIB="ON" \
- -DLLVM_ENABLE_BINDINGS="OFF" \
- -DLLVM_ENABLE_PROJECTS='clang;compiler-rt;libcxx;libcxxabi;libunwind;lld' \
- -DLLVM_ENABLE_WARNINGS="OFF" \
- -DLLVM_INCLUDE_BENCHMARKS="OFF" \
- -DLLVM_INCLUDE_DOCS="OFF" \
- -DLLVM_INCLUDE_EXAMPLES="OFF" \
- -DLLVM_INCLUDE_TESTS="OFF" \
- -DLLVM_LINK_LLVM_DYLIB="ON" \
- -DLLVM_TARGETS_TO_BUILD="host" \
- ../llvm/
-cmake --build . -j4
-export PATH="$(pwd)/bin:$PATH"
-export LLVM_CONFIG="$(pwd)/bin/llvm-config"
-export LD_LIBRARY_PATH="$(llvm-config --libdir)${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
-cd /path/to/AFLplusplus/
+```
+cd ~/AFLplusplus
+export LLVM_CONFIG=llvm-config-15
make
sudo make install
```
@@ -135,10 +96,10 @@ Also, the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST ->
[README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov
(AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work.
-Example:
+Example (note that you might need to add the version, e.g. `llvm-ar-15`:
```
-CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure
+CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar AS=llvm-as ./configure
make
```
@@ -316,13 +277,13 @@ 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.
Hence, if building a target with afl-clang-lto fails, try to build it with
-llvm12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`,
+LLVM 12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`,
and `CXXFLAGS=-flto=full`).
If this succeeds, then there is an issue with afl-clang-lto. Please report at
@@ -340,7 +301,7 @@ knows what this is doing. And the developer who implemented this didn't respond
to emails.)
In December then came the idea to implement this as a pass that is run via the
-llvm "opt" program, which is performed via an own linker that afterwards calls
+LLVM "opt" program, which is performed via an own linker that afterwards calls
the real linker. This was first implemented in January and work ... kinda. The
LTO time instrumentation worked, however, "how" the basic blocks were
instrumented was a problem, as reducing duplicates turned out to be very, very
@@ -352,13 +313,13 @@ dead-end too.
The final idea to solve this came from domenukk who proposed to insert a block
into an edge and then just use incremental counters ... and this worked! After
some trials and errors to implement this vanhauser-thc found out that there is
-actually an llvm function for this: SplitEdge() :-)
+actually an LLVM function for this: SplitEdge() :-)
-Still more problems came up though as this only works without bugs from llvm 9
+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 :) \ No newline at end of file
+Happy end :)
diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc
index 9a48ae6d..68423029 100644
--- a/instrumentation/SanitizerCoverageLTO.so.cc
+++ b/instrumentation/SanitizerCoverageLTO.so.cc
@@ -1,4 +1,4 @@
-/* SanitizeCoverage.cpp ported to afl++ LTO :-) */
+/* SanitizeCoverage.cpp ported to AFL++ LTO :-) */
#define AFL_LLVM_PASS
@@ -17,8 +17,12 @@
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Analysis/EHPersonalities.h"
+#if LLVM_VERSION_MAJOR < 17
+ #include "llvm/ADT/Triple.h"
+ #include "llvm/Analysis/EHPersonalities.h"
+#else
+ #include "llvm/IR/EHPersonalities.h"
+#endif
#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
@@ -47,7 +51,9 @@
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Instrumentation.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR < 17
+ #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
@@ -111,6 +117,12 @@ static cl::opt<bool> ClPruneBlocks(
cl::desc("Reduce the number of instrumented blocks"), cl::Hidden,
cl::init(true));
+namespace llvm {
+
+void initializeModuleSanitizerCoverageLTOLegacyPassPass(PassRegistry &PB);
+
+}
+
namespace {
SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) {
@@ -182,7 +194,7 @@ class ModuleSanitizerCoverageLTO
private:
void instrumentFunction(Function &F, DomTreeCallback DTCallback,
PostDomTreeCallback PDTCallback);
- void InjectCoverageForIndirectCalls(Function & F,
+ void InjectCoverageForIndirectCalls(Function &F,
ArrayRef<Instruction *> IndirCalls);
bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks,
bool IsLeafFunc = true);
@@ -211,10 +223,10 @@ class ModuleSanitizerCoverageLTO
FunctionCallee SanCovTracePC /*, SanCovTracePCGuard*/;
Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy,
*Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy;
- Module * CurModule;
+ Module *CurModule;
std::string CurModuleUniqueId;
Triple TargetTriple;
- LLVMContext * C;
+ LLVMContext *C;
const DataLayout *DL;
GlobalVariable *FunctionGuardArray; // for trace-pc-guard.
@@ -226,42 +238,43 @@ class ModuleSanitizerCoverageLTO
SanitizerCoverageOptions Options;
- // afl++ START
+ // AFL++ START
// const SpecialCaseList * Allowlist;
// const SpecialCaseList * Blocklist;
uint32_t autodictionary = 1;
+ uint32_t autodictionary_no_main = 0;
uint32_t inst = 0;
uint32_t afl_global_id = 0;
uint32_t unhandled = 0;
uint32_t select_cnt = 0;
uint64_t map_addr = 0;
- const char * skip_nozero = NULL;
- const char * use_threadsafe_counters = nullptr;
+ const char *skip_nozero = NULL;
+ const char *use_threadsafe_counters = nullptr;
std::vector<BasicBlock *> BlockList;
DenseMap<Value *, std::string *> valueMap;
std::vector<std::string> dictionary;
- IntegerType * Int8Tyi = NULL;
- IntegerType * Int32Tyi = NULL;
- IntegerType * Int64Tyi = NULL;
- ConstantInt * Zero = NULL;
- ConstantInt * One = NULL;
- LLVMContext * Ct = NULL;
- Module * Mo = NULL;
- GlobalVariable * AFLMapPtr = NULL;
- Value * MapPtrFixed = NULL;
+ IntegerType *Int8Tyi = NULL;
+ IntegerType *Int32Tyi = NULL;
+ IntegerType *Int64Tyi = NULL;
+ ConstantInt *Zero = NULL;
+ ConstantInt *One = NULL;
+ LLVMContext *Ct = NULL;
+ Module *Mo = NULL;
+ GlobalVariable *AFLMapPtr = NULL;
+ Value *MapPtrFixed = NULL;
std::ofstream dFile;
size_t found = 0;
- // afl++ END
+ // AFL++ END
};
-class ModuleSanitizerCoverageLegacyPass : public ModulePass {
+class ModuleSanitizerCoverageLTOLegacyPass : public ModulePass {
public:
static char ID;
StringRef getPassName() const override {
- return "sancov";
+ return "sancov-lto";
}
@@ -272,11 +285,11 @@ class ModuleSanitizerCoverageLegacyPass : public ModulePass {
}
- ModuleSanitizerCoverageLegacyPass(
+ ModuleSanitizerCoverageLTOLegacyPass(
const SanitizerCoverageOptions &Options = SanitizerCoverageOptions())
: ModulePass(ID), Options(Options) {
- initializeModuleSanitizerCoverageLegacyPassPass(
+ initializeModuleSanitizerCoverageLTOLegacyPassPass(
*PassRegistry::getPassRegistry());
}
@@ -318,8 +331,11 @@ llvmGetPassPluginInfo() {
#if LLVM_VERSION_MAJOR <= 13
using OptimizationLevel = typename PassBuilder::OptimizationLevel;
#endif
- // PB.registerFullLinkTimeOptimizationLastEPCallback(
+#if LLVM_VERSION_MAJOR >= 15
+ PB.registerFullLinkTimeOptimizationLastEPCallback(
+#else
PB.registerOptimizerLastEPCallback(
+#endif
[](ModulePassManager &MPM, OptimizationLevel OL) {
MPM.addPass(ModuleSanitizerCoverageLTO());
@@ -330,12 +346,12 @@ llvmGetPassPluginInfo() {
}
-PreservedAnalyses ModuleSanitizerCoverageLTO::run(Module & M,
+PreservedAnalyses ModuleSanitizerCoverageLTO::run(Module &M,
ModuleAnalysisManager &MAM) {
ModuleSanitizerCoverageLTO ModuleSancov(Options);
auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
- auto DTCallback = [&FAM](Function &F) -> const DominatorTree * {
+ auto DTCallback = [&FAM](Function &F) -> const DominatorTree *{
return &FAM.getResult<DominatorTreeAnalysis>(F);
@@ -380,7 +396,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
FunctionPCsArray = nullptr;
IntptrTy = Type::getIntNTy(*C, DL->getPointerSizeInBits());
IntptrPtrTy = PointerType::getUnqual(IntptrTy);
- Type * VoidTy = Type::getVoidTy(*C);
+ Type *VoidTy = Type::getVoidTy(*C);
IRBuilder<> IRB(*C);
Int64PtrTy = PointerType::getUnqual(IRB.getInt64Ty());
Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty());
@@ -392,8 +408,8 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
Int8Ty = IRB.getInt8Ty();
Int1Ty = IRB.getInt1Ty();
- /* afl++ START */
- char * ptr;
+ /* AFL++ START */
+ char *ptr;
LLVMContext &Ctx = M.getContext();
Ct = &Ctx;
Int8Tyi = IntegerType::getInt8Ty(Ctx);
@@ -402,7 +418,8 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
/* Show a banner */
setvbuf(stdout, NULL, _IONBF, 0);
- if (getenv("AFL_DEBUG")) debug = 1;
+ if (getenv("AFL_DEBUG")) { debug = 1; }
+ if (getenv("AFL_LLVM_DICT2FILE_NO_MAIN")) { autodictionary_no_main = 1; }
if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
@@ -420,6 +437,8 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
if ((afl_global_id = atoi(ptr)) < 0)
FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr);
+ if (afl_global_id < 4) { afl_global_id = 4; }
+
if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) {
dFile.open(ptr, std::ofstream::out | std::ofstream::app);
@@ -494,16 +513,23 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
if (!isInInstrumentList(&F, MNAME) || !F.size()) { continue; }
+ if (autodictionary_no_main &&
+ (!F.getName().compare("main") || !F.getName().compare("_main"))) {
+
+ continue;
+
+ }
+
for (auto &BB : F) {
for (auto &IN : BB) {
CallInst *callInst = nullptr;
- CmpInst * cmpInst = nullptr;
+ CmpInst *cmpInst = nullptr;
if ((cmpInst = dyn_cast<CmpInst>(&IN))) {
- Value * op = cmpInst->getOperand(1);
+ Value *op = cmpInst->getOperand(1);
ConstantInt *ilen = dyn_cast<ConstantInt>(op);
if (ilen && ilen->uge(0xffffffffffffffff) == false) {
@@ -666,33 +692,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();
@@ -759,7 +789,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
if (HasStr2 == true) {
- Value * op2 = callInst->getArgOperand(2);
+ Value *op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
if (ilen) {
@@ -866,7 +896,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
if (isMemcmp || isStrncmp || isStrncasecmp) {
- Value * op2 = callInst->getArgOperand(2);
+ Value *op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
if (ilen) {
@@ -921,7 +951,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
std::string outstring;
fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen,
thestring.length());
- for (uint8_t i = 0; i < thestring.length(); i++) {
+ for (uint16_t i = 0; i < (uint16_t)thestring.length(); i++) {
uint8_t c = thestring[i];
if (c <= 32 || c >= 127)
@@ -956,7 +986,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
}
- // afl++ END
+ // AFL++ END
SanCovTracePCIndir =
M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy);
@@ -980,10 +1010,11 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
for (auto &F : M)
instrumentFunction(F, DTCallback, PDTCallback);
- // afl++ START
+ // AFL++ START
if (dFile.is_open()) dFile.close();
- if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) {
+ if (!getenv("AFL_LLVM_LTO_SKIPINIT") &&
+ (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr)) {
// yes we could create our own function, insert it into ctors ...
// but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o
@@ -1017,7 +1048,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
GlobalVariable *AFLMapAddrFixed = new GlobalVariable(
M, Int64Tyi, true, GlobalValue::ExternalLinkage, 0, "__afl_map_addr");
ConstantInt *MapAddr = ConstantInt::get(Int64Tyi, map_addr);
- StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed);
+ StoreInst *StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed);
ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(StoreMapAddr);
}
@@ -1032,7 +1063,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
new GlobalVariable(M, Int32Tyi, true, GlobalValue::ExternalLinkage, 0,
"__afl_final_loc");
ConstantInt *const_loc = ConstantInt::get(Int32Tyi, write_loc);
- StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
+ StoreInst *StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
ModuleSanitizerCoverageLTO::SetNoSanitizeMetadata(StoreFinalLoc);
}
@@ -1054,7 +1085,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
}
if (!be_quiet)
- printf("AUTODICTIONARY: %lu string%s found\n", count,
+ printf("AUTODICTIONARY: %zu string%s found\n", count,
count == 1 ? "" : "s");
if (count) {
@@ -1133,7 +1164,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
}
- // afl++ END
+ // AFL++ END
// We don't reference these arrays directly in any of our runtime functions,
// so we need to prevent them from being dead stripped.
@@ -1159,7 +1190,7 @@ static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) {
}
// True if block has predecessors and it postdominates all of them.
-static bool isFullPostDominator(const BasicBlock * BB,
+static bool isFullPostDominator(const BasicBlock *BB,
const PostDominatorTree *PDT) {
if (pred_begin(BB) == pred_end(BB)) return false;
@@ -1175,8 +1206,8 @@ static bool isFullPostDominator(const BasicBlock * BB,
}
static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
- const DominatorTree * DT,
- const PostDominatorTree * PDT,
+ const DominatorTree *DT,
+ const PostDominatorTree *PDT,
const SanitizerCoverageOptions &Options) {
// Don't insert coverage for blocks containing nothing but unreachable: we
@@ -1190,10 +1221,10 @@ static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
// (catchswitch blocks).
if (BB->getFirstInsertionPt() == BB->end()) return false;
- // afl++ START
+ // AFL++ START
if (!Options.NoPrune && &F.getEntryBlock() == BB && F.size() > 1)
return false;
- // afl++ END
+ // AFL++ END
if (Options.NoPrune || &F.getEntryBlock() == BB) return true;
@@ -1214,7 +1245,11 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
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;
@@ -1235,10 +1270,10 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
// if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName()))
// return;
- // afl++ START
+ // AFL++ START
if (!F.size()) return;
if (!isInInstrumentList(&F, FMNAME)) return;
- // afl++ END
+ // AFL++ END
if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
SplitAllCriticalEdges(
@@ -1246,7 +1281,7 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
SmallVector<Instruction *, 8> IndirCalls;
SmallVector<BasicBlock *, 16> BlocksToInstrument;
- const DominatorTree * DT = DTCallback(F);
+ const DominatorTree *DT = DTCallback(F);
const PostDominatorTree *PDT = PDTCallback(F);
bool IsLeafFunc = true;
uint32_t skip_next = 0;
@@ -1294,8 +1329,8 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
if (!skip_next && (selectInst = dyn_cast<SelectInst>(&IN))) {
uint32_t vector_cnt = 0;
- Value * condition = selectInst->getCondition();
- Value * result;
+ Value *condition = selectInst->getCondition();
+ Value *result;
auto t = condition->getType();
IRBuilder<> IRB(selectInst->getNextNode());
@@ -1466,7 +1501,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));
@@ -1536,7 +1571,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage(
for (size_t i = 0, N = AllBlocks.size(); i < N; i++) {
- // afl++ START
+ // AFL++ START
if (BlockList.size()) {
int skip = 0;
@@ -1558,7 +1593,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage(
}
- // afl++ END
+ // AFL++ END
InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc);
@@ -1584,8 +1619,8 @@ void ModuleSanitizerCoverageLTO::InjectCoverageForIndirectCalls(
for (auto I : IndirCalls) {
IRBuilder<> IRB(I);
- CallBase & CB = cast<CallBase>(*I);
- Value * Callee = CB.getCalledOperand();
+ CallBase &CB = cast<CallBase>(*I);
+ Value *Callee = CB.getCalledOperand();
if (isa<InlineAsm>(Callee)) continue;
IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy));
@@ -1593,7 +1628,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageForIndirectCalls(
}
-void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function & F,
+void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F,
BasicBlock &BB,
size_t Idx,
bool IsLeafFunc) {
@@ -1624,7 +1659,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function & F,
if (Options.TracePCGuard) {
- // afl++ START
+ // AFL++ START
++afl_global_id;
if (dFile.is_open()) {
@@ -1688,7 +1723,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function & F,
// done :)
inst++;
- // afl++ END
+ // AFL++ END
/*
XXXXXXXXXXXXXXXXXXX
@@ -1750,30 +1785,22 @@ std::string ModuleSanitizerCoverageLTO::getSectionName(
}
-char ModuleSanitizerCoverageLegacyPass::ID = 0;
+char ModuleSanitizerCoverageLTOLegacyPass::ID = 0;
-INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov",
+INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLTOLegacyPass, "sancov-lto",
"Pass for instrumenting coverage on functions", false,
false)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass)
-INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov",
+INITIALIZE_PASS_END(ModuleSanitizerCoverageLTOLegacyPass, "sancov-lto",
"Pass for instrumenting coverage on functions", false,
false)
-ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass(
- const SanitizerCoverageOptions &Options,
- const std::vector<std::string> &AllowlistFiles,
- const std::vector<std::string> &BlocklistFiles) {
-
- return new ModuleSanitizerCoverageLegacyPass(Options);
-
-}
-
+#if LLVM_VERSION_MAJOR < 16
static void registerLTOPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
- auto p = new ModuleSanitizerCoverageLegacyPass();
+ auto p = new ModuleSanitizerCoverageLTOLegacyPass();
PM.add(p);
}
@@ -1784,8 +1811,9 @@ static RegisterStandardPasses RegisterCompTransPass(
static RegisterStandardPasses RegisterCompTransPass0(
PassManagerBuilder::EP_EnabledOnOptLevel0, registerLTOPass);
-#if LLVM_VERSION_MAJOR >= 11
+ #if LLVM_VERSION_MAJOR >= 11
static RegisterStandardPasses RegisterCompTransPassLTO(
PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerLTOPass);
+ #endif
#endif
diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc
index 408353b3..f88ce126 100644
--- a/instrumentation/SanitizerCoveragePCGUARD.so.cc
+++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc
@@ -13,38 +13,63 @@
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Analysis/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"
@@ -54,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";
@@ -64,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";
@@ -95,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;
}
@@ -135,21 +134,14 @@ 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,
+ void InjectTraceForSwitch(Function &F,
ArrayRef<Instruction *> SwitchTraceTargets);
bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks,
bool IsLeafFunc = true);
@@ -169,28 +161,29 @@ 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,
*Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy;
- Module * CurModule;
+ Module *CurModule;
std::string CurModuleUniqueId;
Triple TargetTriple;
- LLVMContext * C;
+ LLVMContext *C;
const DataLayout *DL;
GlobalVariable *FunctionGuardArray; // for trace-pc-guard.
@@ -204,77 +197,28 @@ class ModuleSanitizerCoverageAFL
uint32_t instr = 0, selects = 0, unhandled = 0;
GlobalVariable *AFLMapPtr = NULL;
- ConstantInt * One = NULL;
- ConstantInt * Zero = NULL;
-
-};
-
-class ModuleSanitizerCoverageLegacyPass : public ModulePass {
-
- public:
- ModuleSanitizerCoverageLegacyPass(
- const SanitizerCoverageOptions &Options = SanitizerCoverageOptions())
- : ModulePass(ID), Options(Options) {
-
- initializeModuleSanitizerCoverageLegacyPassPass(
- *PassRegistry::getPassRegistry());
-
- }
-
- bool runOnModule(Module &M) override {
-
- ModuleSanitizerCoverageAFL ModuleSancov(Options);
- auto DTCallback = [this](Function &F) -> const DominatorTree * {
-
- return &this->getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
-
- };
-
- auto PDTCallback = [this](Function &F) -> const PostDominatorTree * {
-
- return &this->getAnalysis<PostDominatorTreeWrapperPass>(F)
- .getPostDomTree();
-
- };
-
- return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback);
-
- }
-
- /*static*/ char ID; // Pass identification, replacement for typeid
- StringRef getPassName() const override {
-
- return "ModuleSanitizerCoverage";
-
- }
-
- void getAnalysisUsage(AnalysisUsage &AU) const override {
-
- AU.addRequired<DominatorTreeWrapperPass>();
- AU.addRequired<PostDominatorTreeWrapperPass>();
-
- }
-
- private:
- SanitizerCoverageOptions Options;
+ ConstantInt *One = NULL;
+ ConstantInt *Zero = NULL;
};
} // namespace
-#if 1
-
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());
@@ -285,14 +229,12 @@ llvmGetPassPluginInfo() {
}
-#endif
-
-PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module & M,
+PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
ModuleAnalysisManager &MAM) {
ModuleSanitizerCoverageAFL ModuleSancov(Options);
auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
- auto DTCallback = [&FAM](Function &F) -> const DominatorTree * {
+ auto DTCallback = [&FAM](Function &F) -> const DominatorTree *{
return &FAM.getResult<DominatorTreeAnalysis>(F);
@@ -313,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())
@@ -342,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);
}
@@ -354,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()) {
@@ -379,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);
}
@@ -391,37 +327,25 @@ 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 ((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;
@@ -433,7 +357,7 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
FunctionPCsArray = nullptr;
IntptrTy = Type::getIntNTy(*C, DL->getPointerSizeInBits());
IntptrPtrTy = PointerType::getUnqual(IntptrTy);
- Type * VoidTy = Type::getVoidTy(*C);
+ Type *VoidTy = Type::getVoidTy(*C);
IRBuilder<> IRB(*C);
Int64PtrTy = PointerType::getUnqual(IRB.getInt64Ty());
Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty());
@@ -444,16 +368,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;
@@ -483,26 +405,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");
@@ -512,8 +421,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 =
@@ -528,40 +435,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" : "",
@@ -582,39 +474,36 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
}
// True if block has successors and it dominates all of them.
-bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) {
-
- if (succ_begin(BB) == succ_end(BB)) return false;
+static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) {
- for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) {
+ if (succ_empty(BB)) return false;
- if (!DT->dominates(BB, SUCC)) return false;
+ return llvm::all_of(successors(BB), [&](const BasicBlock *SUCC) {
- }
+ 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) {
-
- if (pred_begin(BB) == pred_end(BB)) return false;
+static bool isFullPostDominator(const BasicBlock *BB,
+ const PostDominatorTree *PDT) {
- for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) {
+ if (pred_empty(BB)) return false;
- if (!PDT->dominates(BB, PRED)) return false;
+ return llvm::all_of(predecessors(BB), [&](const BasicBlock *PRED) {
- }
+ 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
@@ -629,10 +518,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) &&
@@ -644,41 +529,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;
@@ -694,17 +592,15 @@ 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;
-
- const DominatorTree * DT = DTCallback(F);
+ SmallVector<BasicBlock *, 16> BlocksToInstrument;
+ SmallVector<Instruction *, 8> CmpTraceTargets;
+ SmallVector<Instruction *, 8> SwitchTraceTargets;
+
+ const DominatorTree *DT = DTCallback(F);
const PostDominatorTree *PDT = PDTCallback(F);
bool IsLeafFunc = true;
@@ -712,47 +608,35 @@ 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);
}
@@ -764,29 +648,30 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreateFunctionLocalArrayInSection(
*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)
- Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize()));
+#if LLVM_VERSION_MAJOR >= 16
+ Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue()));
#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;
@@ -811,8 +696,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
}
@@ -835,22 +724,15 @@ 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) {
- uint32_t cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0;
+ if (AllBlocks.empty()) return false;
+
+ uint32_t cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0;
+ static uint32_t first = 1;
for (auto &BB : F) {
@@ -876,9 +758,11 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
}
- if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue;
+ if (!FuncName.compare(StringRef("__afl_coverage_interesting"))) {
+
+ cnt_cov++;
- cnt_cov++;
+ }
}
@@ -895,29 +779,27 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
}
-#if (LLVM_VERSION_MAJOR >= 12)
else if (t->getTypeID() == llvm::Type::FixedVectorTyID) {
FixedVectorType *tt = dyn_cast<FixedVectorType>(t);
if (tt) {
cnt_sel++;
- cnt_sel_inc += tt->getElementCount().getKnownMinValue();
+ cnt_sel_inc += (tt->getElementCount().getKnownMinValue() * 2);
}
}
-#endif
-
}
}
}
- /* Create PCGUARD array */
- CreateFunctionLocalArrays(F, AllBlocks, cnt_cov + cnt_sel_inc);
+ CreateFunctionLocalArrays(F, AllBlocks, first + cnt_cov + cnt_sel_inc);
+
+ if (first) { first = 0; }
selects += cnt_sel;
uint32_t special = 0, local_selects = 0, skip_next = 0;
@@ -928,12 +810,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();
@@ -971,8 +847,8 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
if (!skip_next && (selectInst = dyn_cast<SelectInst>(&IN))) {
uint32_t vector_cnt = 0;
- Value * condition = selectInst->getCondition();
- Value * result;
+ Value *condition = selectInst->getCondition();
+ Value *result;
auto t = condition->getType();
IRBuilder<> IRB(selectInst->getNextNode());
@@ -992,7 +868,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(
@@ -1000,7 +876,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);
@@ -1037,7 +913,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);
@@ -1046,7 +922,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);
@@ -1055,7 +931,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);
@@ -1064,7 +940,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);
@@ -1072,12 +948,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);
}
@@ -1089,6 +959,7 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
#endif
{
+ // fprintf(stderr, "UNHANDLED: %u\n", t->getTypeID());
unhandled++;
continue;
@@ -1102,18 +973,11 @@ 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 */
LoadInst *CurLoc = nullptr;
- Value * MapPtrIdx = nullptr;
+ Value *MapPtrIdx = nullptr;
/* Load counter for CurLoc */
if (!vector_cnt) {
@@ -1198,29 +1062,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, ... })
@@ -1234,7 +1075,7 @@ void ModuleSanitizerCoverageAFL::InjectTraceForSwitch(
IRBuilder<> IRB(I);
SmallVector<Constant *, 16> Initializers;
- Value * Cond = SI->getCondition();
+ Value *Cond = SI->getCondition();
if (Cond->getType()->getScalarSizeInBits() >
Int64Ty->getScalarSizeInBits())
continue;
@@ -1276,41 +1117,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) {
@@ -1319,8 +1125,8 @@ void ModuleSanitizerCoverageAFL::InjectTraceForCmp(
if (ICmpInst *ICMP = dyn_cast<ICmpInst>(I)) {
IRBuilder<> IRB(ICMP);
- Value * A0 = ICMP->getOperand(0);
- Value * A1 = ICMP->getOperand(1);
+ Value *A0 = ICMP->getOperand(0);
+ Value *A1 = ICMP->getOperand(1);
if (!A0->getType()->isIntegerTy()) continue;
uint64_t TypeSize = DL->getTypeStoreSizeInBits(A0->getType());
int CallbackIdx = TypeSize == 8 ? 0
@@ -1353,34 +1159,51 @@ void ModuleSanitizerCoverageAFL::InjectTraceForCmp(
}
-void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function & F,
+void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function &F,
BasicBlock &BB,
size_t Idx,
bool IsLeafFunc) {
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(
@@ -1438,57 +1261,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(
@@ -1526,26 +1298,3 @@ std::string ModuleSanitizerCoverageAFL::getSectionEnd(
}
-#if 0
-
-char ModuleSanitizerCoverageLegacyPass::ID = 0;
-INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov",
- "Pass for instrumenting coverage on functions", false,
- false)
-INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
-INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass)
-INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov",
- "Pass for instrumenting coverage on functions", false,
- false)
-ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass(
- const SanitizerCoverageOptions &Options,
- const std::vector<std::string> &AllowlistFiles,
- const std::vector<std::string> &BlocklistFiles) {
-
- return new ModuleSanitizerCoverageLegacyPass(Options, AllowlistFiles,
- BlocklistFiles);
-
-}
-
-#endif
-
diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c
index db7ac7b0..caa3c3a8 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-2022 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.
@@ -14,6 +14,20 @@
*/
+#ifdef __AFL_CODE_COVERAGE
+ #ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
+ #endif
+ #ifndef __USE_GNU
+ #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__
#include "android-ashmem.h"
#endif
@@ -38,7 +52,7 @@
#include <errno.h>
#include <sys/mman.h>
-#ifndef __HAIKU__
+#if !defined(__HAIKU__) && !defined(__OpenBSD__)
#include <sys/syscall.h>
#endif
#ifndef USEMMAP
@@ -77,38 +91,89 @@
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;
-u8 * __afl_area_ptr = __afl_area_initial;
-u8 * __afl_dictionary;
-u8 * __afl_fuzz_ptr;
+u8 *__afl_area_ptr = __afl_area_initial;
+u8 *__afl_dictionary;
+u8 *__afl_fuzz_ptr;
static u32 __afl_fuzz_len_dummy;
-u32 * __afl_fuzz_len = &__afl_fuzz_len_dummy;
+u32 *__afl_fuzz_len = &__afl_fuzz_len_dummy;
+int __afl_sharedmem_fuzzing __attribute__((weak));
u32 __afl_final_loc;
u32 __afl_map_size = MAP_SIZE;
u32 __afl_dictionary_len;
u64 __afl_map_addr;
+u32 __afl_first_final_loc;
+
+#ifdef __AFL_CODE_COVERAGE
+typedef struct afl_module_info_t afl_module_info_t;
+
+struct afl_module_info_t {
+
+ // A unique id starting with 0
+ u32 id;
+
+ // Name and base address of the module
+ char *name;
+ uintptr_t base_address;
+
+ // PC Guard start/stop
+ u32 *start;
+ u32 *stop;
+
+ // PC Table begin/end
+ const uintptr_t *pcs_beg;
+ const uintptr_t *pcs_end;
+
+ u8 mapped;
+
+ afl_module_info_t *next;
+
+};
+
+typedef struct {
+
+ uintptr_t PC, PCFlags;
+
+} PCTableEntry;
+
+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 */
+u32 __afl_connected = 0;
// for the __AFL_COVERAGE_ON/__AFL_COVERAGE_OFF features to work:
int __afl_selective_coverage __attribute__((weak));
int __afl_selective_coverage_start_off __attribute__((weak));
static int __afl_selective_coverage_temp = 1;
-#if defined(__ANDROID__) || defined(__HAIKU__)
+#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX];
PREV_LOC_T __afl_prev_caller[CTX_MAX_K];
u32 __afl_prev_ctx;
@@ -118,8 +183,6 @@ __thread PREV_LOC_T __afl_prev_caller[CTX_MAX_K];
__thread u32 __afl_prev_ctx;
#endif
-int __afl_sharedmem_fuzzing __attribute__((weak));
-
struct cmp_map *__afl_cmp_map;
struct cmp_map *__afl_cmp_map_backup;
@@ -138,7 +201,7 @@ static u8 _is_sancov;
/* Debug? */
-static u32 __afl_debug;
+/*static*/ u32 __afl_debug;
/* Already initialized markers */
@@ -146,6 +209,7 @@ u32 __afl_already_initialized_shm;
u32 __afl_already_initialized_forkserver;
u32 __afl_already_initialized_first;
u32 __afl_already_initialized_second;
+u32 __afl_already_initialized_early;
u32 __afl_already_initialized_init;
/* Dummy pipe for area_is_valid() */
@@ -159,6 +223,7 @@ static void at_exit(int signal) {
if (unlikely(child_pid > 0)) {
kill(child_pid, SIGKILL);
+ waitpid(child_pid, NULL, 0);
child_pid = -1;
}
@@ -288,11 +353,18 @@ static void __afl_map_shm(void) {
__afl_map_size = ++__afl_final_loc; // as we count starting 0
+ if (getenv("AFL_DUMP_MAP_SIZE")) {
+
+ printf("%u\n", __afl_map_size);
+ exit(-1);
+
+ }
+
if (__afl_final_loc > MAP_SIZE) {
char *ptr;
u32 val = 0;
- if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) val = atoi(ptr);
+ if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { val = atoi(ptr); }
if (val < __afl_final_loc) {
if (__afl_final_loc > FS_OPT_MAX_MAPSIZE) {
@@ -312,19 +384,88 @@ static void __afl_map_shm(void) {
} else {
- if (!getenv("AFL_QUIET"))
+ 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);
+ }
+
}
}
}
+ } 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) ||
+ fcntl(FORKSRV_FD, F_GETFD) == -1 ||
+ fcntl(FORKSRV_FD + 1, F_GETFD) == -1)) {
+
+ if (__afl_debug) {
+
+ fprintf(stderr,
+ "DEBUG: running not inside afl-fuzz, disabling shared memory "
+ "testcases\n");
+
+ }
+
+ __afl_sharedmem_fuzzing = 0;
+
+ }
+
+ if (!id_str) {
+
+ u32 val = 0;
+ u8 *ptr;
+
+ if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { val = atoi(ptr); }
+
+ if (val > MAP_INITIAL_SIZE) {
+
+ __afl_map_size = val;
+
+ } else {
+
+ if (__afl_first_final_loc > MAP_INITIAL_SIZE) {
+
+ // done in second stage constructor
+ __afl_map_size = __afl_first_final_loc;
+
+ } else {
+
+ __afl_map_size = MAP_INITIAL_SIZE;
+
+ }
+
+ }
+
+ if (__afl_map_size > MAP_INITIAL_SIZE && __afl_final_loc < __afl_map_size) {
+
+ __afl_final_loc = __afl_map_size;
+
+ }
+
+ if (__afl_debug) {
+
+ fprintf(stderr, "DEBUG: (0) init map size is %u to %p\n", __afl_map_size,
+ __afl_area_ptr_dummy);
+
+ }
+
}
/* If we're running under AFL, attach to the appropriate region, replacing the
@@ -364,7 +505,7 @@ static void __afl_map_shm(void) {
}
#ifdef USEMMAP
- const char * shm_file_path = id_str;
+ const char *shm_file_path = id_str;
int shm_fd = -1;
unsigned char *shm_base = NULL;
@@ -418,6 +559,7 @@ static void __afl_map_shm(void) {
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);
@@ -465,20 +607,30 @@ static void __afl_map_shm(void) {
}
- } else if (_is_sancov && __afl_area_ptr != __afl_area_initial) {
+ } else if (__afl_final_loc > MAP_INITIAL_SIZE &&
- free(__afl_area_ptr);
- __afl_area_ptr = NULL;
+ __afl_final_loc > __afl_first_final_loc) {
- if (__afl_final_loc > MAP_INITIAL_SIZE) {
+ if (__afl_area_initial != __afl_area_ptr_dummy) {
- __afl_area_ptr = (u8 *)malloc(__afl_final_loc);
+ free(__afl_area_ptr_dummy);
}
- if (!__afl_area_ptr) { __afl_area_ptr = __afl_area_ptr_dummy; }
+ __afl_area_ptr_dummy = (u8 *)malloc(__afl_final_loc);
+ __afl_area_ptr = __afl_area_ptr_dummy;
+ __afl_map_size = __afl_final_loc;
+
+ if (!__afl_area_ptr_dummy) {
- }
+ fprintf(stderr,
+ "Error: AFL++ could not acquire %u bytes of memory, exiting!\n",
+ __afl_final_loc);
+ exit(-1);
+
+ }
+
+ } // else: nothing to be done
__afl_area_ptr_backup = __afl_area_ptr;
@@ -487,7 +639,7 @@ 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,"
+ "%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,
@@ -533,14 +685,15 @@ 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; }
}
#ifdef USEMMAP
- const char * shm_file_path = id_str;
+ const char *shm_file_path = id_str;
int shm_fd = -1;
struct cmp_map *shm_base = NULL;
@@ -587,6 +740,27 @@ static void __afl_map_shm(void) {
}
+#ifdef __AFL_CODE_COVERAGE
+ char *pcmap_id_str = getenv("__AFL_PCMAP_SHM_ID");
+
+ if (pcmap_id_str) {
+
+ __afl_pcmap_size = __afl_map_size * sizeof(void *);
+ u32 shm_id = atoi(pcmap_id_str);
+
+ __afl_pcmap_ptr = (uintptr_t *)shmat(shm_id, NULL, 0);
+
+ if (__afl_debug) {
+
+ fprintf(stderr, "DEBUG: Received %p via shmat for pcmap\n",
+ __afl_pcmap_ptr);
+
+ }
+
+ }
+
+#endif // __AFL_CODE_COVERAGE
+
}
/* unmap SHM. */
@@ -595,6 +769,17 @@ static void __afl_unmap_shm(void) {
if (!__afl_already_initialized_shm) return;
+#ifdef __AFL_CODE_COVERAGE
+ if (__afl_pcmap_size) {
+
+ shmdt((void *)__afl_pcmap_ptr);
+ __afl_pcmap_ptr = NULL;
+ __afl_pcmap_size = 0;
+
+ }
+
+#endif // __AFL_CODE_COVERAGE
+
char *id_str = getenv(SHM_ENV_VAR);
if (id_str) {
@@ -646,7 +831,7 @@ static void __afl_unmap_shm(void) {
void write_error_with_location(char *text, char *filename, int linenumber) {
- u8 * o = getenv("__AFL_OUT_DIR");
+ u8 *o = getenv("__AFL_OUT_DIR");
char *e = strerror(errno);
if (o) {
@@ -686,10 +871,10 @@ static void __afl_start_snapshots(void) {
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 != 0) status |= FS_OPT_SHDMEM_FUZZ;
+ 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;
+ if (__afl_dictionary_len && __afl_dictionary) { status |= FS_OPT_AUTODICT; }
memcpy(tmp, &status, 4);
if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; }
@@ -705,7 +890,7 @@ static void __afl_start_snapshots(void) {
if (__afl_debug) {
- fprintf(stderr, "target forkserver recv: %08x\n", was_killed);
+ fprintf(stderr, "DEBUG: target forkserver recv: %08x\n", was_killed);
}
@@ -950,7 +1135,7 @@ static void __afl_start_forkserver(void) {
}
- if (__afl_sharedmem_fuzzing != 0) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; }
+ if (__afl_sharedmem_fuzzing) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; }
if (status_for_fsrv) {
status_for_fsrv |= (FS_OPT_ENABLED | FS_OPT_NEWCMPLOG);
@@ -964,13 +1149,15 @@ static void __afl_start_forkserver(void) {
if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; }
+ __afl_connected = 1;
+
if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) {
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
if (__afl_debug) {
- fprintf(stderr, "target forkserver recv: %08x\n", was_killed);
+ fprintf(stderr, "DEBUG: target forkserver recv: %08x\n", was_killed);
}
@@ -1174,13 +1361,9 @@ int __afl_persistent_loop(unsigned int max_cnt) {
iteration, it's our job to erase any trace of whatever happened
before the loop. */
- if (is_persistent) {
-
- memset(__afl_area_ptr, 0, __afl_map_size);
- __afl_area_ptr[0] = 1;
- memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T));
-
- }
+ memset(__afl_area_ptr, 0, __afl_map_size);
+ __afl_area_ptr[0] = 1;
+ memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T));
cycle_cnt = max_cnt;
first_pass = 0;
@@ -1188,34 +1371,28 @@ int __afl_persistent_loop(unsigned int max_cnt) {
return 1;
- }
-
- if (is_persistent) {
+ } else if (--cycle_cnt) {
- if (--cycle_cnt) {
+ raise(SIGSTOP);
- raise(SIGSTOP);
+ __afl_area_ptr[0] = 1;
+ memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T));
+ __afl_selective_coverage_temp = 1;
- __afl_area_ptr[0] = 1;
- memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T));
- __afl_selective_coverage_temp = 1;
+ return 1;
- return 1;
+ } else {
- } else {
+ /* When exiting __AFL_LOOP(), make sure that the subsequent code that
+ follows the loop is not traced. We do that by pivoting back to the
+ dummy output region. */
- /* When exiting __AFL_LOOP(), make sure that the subsequent code that
- follows the loop is not traced. We do that by pivoting back to the
- dummy output region. */
+ __afl_area_ptr = __afl_area_ptr_dummy;
- __afl_area_ptr = __afl_area_ptr_dummy;
-
- }
+ return 0;
}
- return 0;
-
}
/* This one can be called from user code when deferred forkserver mode
@@ -1291,6 +1468,9 @@ __attribute__((constructor(EARLY_FS_PRIO))) void __early_forkserver(void) {
__attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) {
+ if (__afl_already_initialized_early) return;
+ __afl_already_initialized_early = 1;
+
is_persistent = !!getenv(PERSIST_ENV_VAR);
if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
@@ -1310,27 +1490,31 @@ __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");
}
if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
u8 *ptr;
- if (__afl_final_loc) {
+ if (__afl_final_loc > MAP_INITIAL_SIZE) {
+
+ __afl_first_final_loc = __afl_final_loc + 1;
if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial)
free(__afl_area_ptr);
if (__afl_map_addr)
- ptr = (u8 *)mmap((void *)__afl_map_addr, __afl_final_loc,
+ ptr = (u8 *)mmap((void *)__afl_map_addr, __afl_first_final_loc,
PROT_READ | PROT_WRITE,
MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
else
- ptr = (u8 *)malloc(__afl_final_loc);
+ ptr = (u8 *)malloc(__afl_first_final_loc);
if (ptr && (ssize_t)ptr != -1) {
__afl_area_ptr = ptr;
+ __afl_area_ptr_dummy = __afl_area_ptr;
__afl_area_ptr_backup = __afl_area_ptr;
}
@@ -1348,14 +1532,18 @@ __attribute__((constructor(0))) void __afl_auto_first(void) {
__afl_already_initialized_first = 1;
if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
- u8 *ptr = (u8 *)malloc(MAP_INITIAL_SIZE);
- if (ptr && (ssize_t)ptr != -1) {
+ /*
+ u8 *ptr = (u8 *)malloc(MAP_INITIAL_SIZE);
- __afl_area_ptr = ptr;
- __afl_area_ptr_backup = __afl_area_ptr;
+ if (ptr && (ssize_t)ptr != -1) {
- }
+ __afl_area_ptr = ptr;
+ __afl_area_ptr_backup = __afl_area_ptr;
+
+ }
+
+ */
} // ptr memleak report is a false positive
@@ -1414,6 +1602,310 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
}
+#ifdef __AFL_CODE_COVERAGE
+void afl_read_pc_filter_file(const char *filter_file) {
+
+ 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;
+
+ }
+
+ __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;
+ if (!dladdr(__builtin_return_address(0), &dlinfo)) {
+
+ fprintf(stderr,
+ "WARNING: Ignoring __sanitizer_cov_pcs_init callback due to "
+ "missing module info\n");
+ return;
+
+ }
+
+ 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) {
+
+ last_module_info = last_module_info->next;
+
+ }
+
+ if (!last_module_info) {
+
+ fprintf(stderr,
+ "ERROR: __sanitizer_cov_pcs_init called with no module info?!\n");
+ abort();
+
+ }
+
+ 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.
+ //
+ 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) {
+
+ 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
+ // for x86/x86-64. Needs more work for ARM and other archs.
+ PC = PC - 1;
+
+ // Calculate relative offset in module
+ PC = PC - mod_info->base_address;
+
+ if (__afl_pcmap_ptr) {
+
+ __afl_pcmap_ptr[orig_start_index + in_module_index] = PC;
+
+ }
+
+ if (pc_filter) {
+
+ 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 && 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_debug) {
+
+ fprintf(stderr,
+ "DEBUG: __sanitizer_cov_pcs_init successfully mapped %s with %u "
+ "PCs\n",
+ mod_info->name, in_module_index);
+
+ }
+
+ }
+
+}
+
+#endif // __AFL_CODE_COVERAGE
+
/* Init callback. Populates instrumentation IDs. Note that we're using
ID of 0 as a special value to indicate non-instrumented bits. That may
still touch the bitmap, but in a fairly harmless way. */
@@ -1425,17 +1917,100 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
_is_sancov = 1;
+ if (!getenv("AFL_DUMP_MAP_SIZE")) {
+
+ __afl_auto_first();
+ __afl_auto_second();
+ __afl_auto_early();
+
+ }
+
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);
}
- if (start == stop || *start) return;
+ if (start == stop || *start) { return; }
+
+#ifdef __AFL_CODE_COVERAGE
+ u32 *orig_start = start;
+ afl_module_info_t *mod_info = NULL;
+
+ Dl_info dlinfo;
+ if (dladdr(__builtin_return_address(0), &dlinfo)) {
+
+ if (__afl_already_initialized_forkserver) {
+
+ fprintf(stderr, "[pcmap] Error: Module was not preloaded: %s\n",
+ dlinfo.dli_fname);
+
+ } else {
+
+ afl_module_info_t *last_module_info = __afl_module_info;
+ while (last_module_info && last_module_info->next) {
+
+ last_module_info = last_module_info->next;
+
+ }
+
+ mod_info = malloc(sizeof(afl_module_info_t));
+
+ 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 = NULL;
+ mod_info->stop = NULL;
+ mod_info->pcs_beg = NULL;
+ mod_info->pcs_end = NULL;
+ mod_info->mapped = 0;
+ mod_info->next = NULL;
+
+ if (last_module_info) {
+
+ last_module_info->next = mod_info;
+
+ } else {
+
+ __afl_module_info = mod_info;
+
+ }
+
+ if (__afl_debug) {
+
+ fprintf(stderr, "[pcmap] Module: %s Base Address: %p\n",
+ dlinfo.dli_fname, dlinfo.dli_fbase);
+
+ }
+
+ }
+
+ } else {
+
+ fprintf(stderr, "[pcmap] dladdr call failed\n");
+
+ }
+
+#endif // __AFL_CODE_COVERAGE
+
+ x = getenv("AFL_INST_RATIO");
+ if (x) {
+
+ inst_ratio = (u32)atoi(x);
+
+ if (!inst_ratio || inst_ratio > 100) {
+
+ fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n");
+ abort();
+
+ }
+
+ }
// If a dlopen of an instrumented library happens after the forkserver then
// we have a problem as we cannot increase the coverage map anymore.
@@ -1448,107 +2023,116 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
"[-] FATAL: forkserver is already up, but an instrumented dlopen() "
"library loaded afterwards. You must AFL_PRELOAD such libraries to "
"be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.\n"
- "To ignore this set AFL_IGNORE_PROBLEMS=1.\n");
+ "To ignore this set AFL_IGNORE_PROBLEMS=1 but this will lead to "
+ "ambiguous coverage data.\n"
+ "In addition, you can set AFL_IGNORE_PROBLEMS_COVERAGE=1 to "
+ "ignore the additional coverage instead (use with caution!).\n");
abort();
} else {
- static u32 offset = 4;
+ u8 ignore_dso_after_fs = !!getenv("AFL_IGNORE_PROBLEMS_COVERAGE");
+ if (__afl_debug && ignore_dso_after_fs) {
- while (start < stop) {
-
- *(start++) = offset;
- if (unlikely(++offset >= __afl_final_loc)) { offset = 4; }
+ fprintf(stderr,
+ "DEBUG: Ignoring coverage from dynamically loaded code\n");
}
- }
+ static u32 offset = 5;
- }
+ while (start < stop) {
- x = getenv("AFL_INST_RATIO");
- if (x) { inst_ratio = (u32)atoi(x); }
+ if (!ignore_dso_after_fs &&
+ (likely(inst_ratio == 100) || R(100) < inst_ratio)) {
- if (!inst_ratio || inst_ratio > 100) {
+ *(start++) = offset;
- fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n");
- abort();
+ } else {
- }
+ *(start++) = 0; // write to map[0]
- /* instrumented code is loaded *after* our forkserver is up. this is a
- problem. We cannot prevent collisions then :( */
- /*
- if (__afl_already_initialized_forkserver &&
- __afl_final_loc + 1 + stop - start > __afl_map_size) {
+ }
- if (__afl_debug) {
+ if (unlikely(++offset >= __afl_final_loc)) { offset = 5; }
- fprintf(stderr, "Warning: new instrumented code after the forkserver!\n");
+ }
}
- __afl_final_loc = 2;
+ return; // we are done for this special case
- if (1 + stop - start > __afl_map_size) {
+ }
- *(start++) = ++__afl_final_loc;
+ /* Make sure that the first element in the range is always set - we use that
+ to avoid duplicate calls (which can happen as an artifact of the underlying
+ implementation in LLVM). */
- while (start < stop) {
+ if (__afl_final_loc < 5) __afl_final_loc = 5; // we skip the first 5 entries
- if (R(100) < inst_ratio)
- *start = ++__afl_final_loc % __afl_map_size;
- else
- *start = 4;
+ *(start++) = ++__afl_final_loc;
- start++;
+ while (start < stop) {
- }
+ if (likely(inst_ratio == 100) || R(100) < inst_ratio) {
- return;
+ *(start++) = ++__afl_final_loc;
+
+ } else {
+
+ *(start++) = 0; // write to map[0]
}
}
- */
+#ifdef __AFL_CODE_COVERAGE
+ if (mod_info) {
- /* Make sure that the first element in the range is always set - we use that
- to avoid duplicate calls (which can happen as an artifact of the underlying
- implementation in LLVM). */
+ if (!mod_info->start) {
- *(start++) = ++__afl_final_loc;
+ mod_info->start = orig_start;
+ mod_info->stop = stop - 1;
- while (start < stop) {
+ }
- if (R(100) < inst_ratio)
- *start = ++__afl_final_loc;
- else
- *start = 4;
+ if (__afl_debug) {
- start++;
+ fprintf(stderr, "DEBUG: [pcmap] Start Index: %u Stop Index: %u\n",
+ *(mod_info->start), *(mod_info->stop));
+
+ }
}
+#endif // __AFL_CODE_COVERAGE
+
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);
}
- if (__afl_already_initialized_shm && __afl_final_loc > __afl_map_size) {
+ if (__afl_already_initialized_shm) {
- if (__afl_debug) {
+ if (__afl_final_loc > __afl_map_size) {
+
+ if (__afl_debug) {
+
+ fprintf(stderr, "DEBUG: Reinit shm necessary (+%u)\n",
+ __afl_final_loc - __afl_map_size);
+
+ }
- fprintf(stderr, "Reinit shm necessary (+%u)\n",
- __afl_final_loc - __afl_map_size);
+ __afl_unmap_shm();
+ __afl_map_shm();
}
- __afl_unmap_shm();
- __afl_map_shm();
+ __afl_map_size = __afl_final_loc + 1;
}
@@ -1561,6 +2145,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);
@@ -1587,6 +2175,8 @@ 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) {
@@ -1793,13 +2383,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);
}
@@ -1908,11 +2498,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;
@@ -1950,7 +2542,7 @@ void __cmplog_rtn_hook_strn(u8 *ptr1, u8 *ptr2, u64 len) {
int len1 = strnlen(ptr1, len0);
if (len1 < 31) len1 = area_is_valid(ptr1, len1 + 1);
int len2 = strnlen(ptr2, len0);
- if (len2 < 31) len2 = area_is_valid(ptr1, len2 + 1);
+ if (len2 < 31) len2 = area_is_valid(ptr2, len2 + 1);
int l = MAX(len1, len2);
if (l < 2) return;
@@ -2313,5 +2905,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
new file mode 100644
index 00000000..774dd5fd
--- /dev/null
+++ b/instrumentation/afl-gcc-cmplog-pass.so.cc
@@ -0,0 +1,404 @@
+/* GCC plugin for cmplog instrumentation of code for AFL++.
+
+ 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-2024 AdaCore
+
+ Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++
+ LLVM CmpLog pass by Andrea Fioraldi <andreafioraldi@gmail.com>, and
+ on the AFL GCC pass.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ */
+
+#include "afl-gcc-common.h"
+
+/* This plugin, being under the same license as GCC, satisfies the
+ "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY
+ EXCEPTION, so it can be part of an "Eligible" "Compilation
+ Process". */
+int plugin_is_GPL_compatible = 1;
+
+namespace {
+
+static const struct pass_data afl_cmplog_pass_data = {
+
+ .type = GIMPLE_PASS,
+ .name = "aflcmplog",
+ .optinfo_flags = OPTGROUP_NONE,
+ .tv_id = TV_NONE,
+ .properties_required = 0,
+ .properties_provided = 0,
+ .properties_destroyed = 0,
+ .todo_flags_start = 0,
+ .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il |
+ TODO_rebuild_cgraph_edges),
+
+};
+
+struct afl_cmplog_pass : afl_base_pass {
+
+ afl_cmplog_pass(bool quiet)
+ : afl_base_pass(quiet, /*debug=*/false, afl_cmplog_pass_data),
+ t8u(),
+ cmplog_hooks() {
+
+ }
+
+ /* An unsigned 8-bit integral type. */
+ tree t8u;
+
+ /* Declarations for the various cmplog hook functions, allocated on demand..
+ [0] is for __cmplog_ins_hookN, that accepts non-power-of-2 sizes.
+ [n in 1..5] are for unsigned ints of 2^{n-1} bytes. */
+ tree cmplog_hooks[6];
+
+ tree cmplog_hook(unsigned i) {
+
+ tree t, fnt;
+
+ if (!t8u) {
+
+ if (BITS_PER_UNIT == 8)
+ t8u = unsigned_char_type_node;
+ else
+ t8u = build_nonstandard_integer_type(8, 1);
+
+ }
+
+ if (i <= ARRAY_SIZE(cmplog_hooks) && cmplog_hooks[i])
+ return cmplog_hooks[i];
+
+ switch (i) {
+
+ case 0:
+#ifdef uint128_type_node
+ t = uint128_type_node;
+#else
+ t = build_nonstandard_integer_type(128, 1);
+#endif
+ fnt =
+ build_function_type_list(void_type_node, t, t, t8u, t8u, NULL_TREE);
+ t = cmplog_hooks[0] = build_fn_decl("__cmplog_ins_hookN", fnt);
+ break;
+
+ case 1:
+ t = t8u;
+ fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
+ t = cmplog_hooks[1] = build_fn_decl("__cmplog_ins_hook1", fnt);
+ break;
+
+ case 2:
+ t = uint16_type_node;
+ fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
+ t = cmplog_hooks[2] = build_fn_decl("__cmplog_ins_hook2", fnt);
+ break;
+
+ case 3:
+ t = uint32_type_node;
+ fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
+ t = cmplog_hooks[3] = build_fn_decl("__cmplog_ins_hook4", fnt);
+ break;
+
+ case 4:
+ t = uint64_type_node;
+ fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
+ t = cmplog_hooks[4] = build_fn_decl("__cmplog_ins_hook8", fnt);
+ break;
+
+ case 5:
+#ifdef uint128_type_node
+ t = uint128_type_node;
+#else
+ t = build_nonstandard_integer_type(128, 1);
+#endif
+ fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE);
+ t = cmplog_hooks[5] = build_fn_decl("__cmplog_ins_hook16", fnt);
+ break;
+
+ default:
+ gcc_unreachable();
+
+ }
+
+ /* Mark the newly-created decl as non-throwing, so that we can
+ insert call within basic blocks. */
+ TREE_NOTHROW(t) = 1;
+
+ return t;
+
+ }
+
+ /* Insert a cmplog hook call before GSI for a CODE compare between
+ LHS and RHS. */
+ void insert_cmplog_call(gimple_stmt_iterator gsi, tree_code code, tree lhs,
+ tree rhs) {
+
+ gcc_checking_assert(TYPE_MAIN_VARIANT(TREE_TYPE(lhs)) ==
+ TYPE_MAIN_VARIANT(TREE_TYPE(rhs)));
+
+ tree fn;
+ bool pass_n = false;
+
+ /* Obtain the compare operand size as a constant. */
+ tree st = TREE_TYPE(lhs);
+ tree szt = TYPE_SIZE(st);
+
+ if (!tree_fits_uhwi_p(szt)) return;
+
+ unsigned HOST_WIDE_INT sz = tree_to_uhwi(szt);
+
+ /* Round it up. */
+ if (sz % 8) sz = (((sz - 1) / 8) + 1) * 8;
+
+ /* Select the hook function to call, based on the size. */
+ switch (sz) {
+
+ default:
+ fn = cmplog_hook(0);
+ pass_n = true;
+ break;
+
+ case 8:
+ fn = cmplog_hook(1);
+ break;
+
+ case 16:
+ fn = cmplog_hook(2);
+ break;
+
+ case 32:
+ fn = cmplog_hook(3);
+ break;
+
+ case 64:
+ fn = cmplog_hook(4);
+ break;
+
+ case 128:
+ fn = cmplog_hook(5);
+ break;
+
+ }
+
+ /* Set attr according to the compare operation. */
+ unsigned char attr = 0;
+
+ switch (code) {
+
+ case UNORDERED_EXPR:
+ case ORDERED_EXPR:
+ /* ??? */
+ /* Fallthrough. */
+ case NE_EXPR:
+ case LTGT_EXPR:
+ break;
+
+ case EQ_EXPR:
+ case UNEQ_EXPR:
+ attr += 1;
+ break;
+
+ case GT_EXPR:
+ case UNGT_EXPR:
+ attr += 2;
+ break;
+
+ case GE_EXPR:
+ case UNGE_EXPR:
+ attr += 3;
+ break;
+
+ case LT_EXPR:
+ case UNLT_EXPR:
+ attr += 4;
+ break;
+
+ case LE_EXPR:
+ case UNLE_EXPR:
+ attr += 5;
+ break;
+
+ default:
+ gcc_unreachable();
+
+ }
+
+ if (FLOAT_TYPE_P(TREE_TYPE(lhs))) {
+
+ attr += 8;
+
+ tree t = build_nonstandard_integer_type(sz, 1);
+
+ tree s = make_ssa_name(t);
+ gimple g = gimple_build_assign(s, VIEW_CONVERT_EXPR,
+ build1(VIEW_CONVERT_EXPR, t, lhs));
+ lhs = s;
+ gsi_insert_before(&gsi, g, GSI_SAME_STMT);
+
+ s = make_ssa_name(t);
+ g = gimple_build_assign(s, VIEW_CONVERT_EXPR,
+ build1(VIEW_CONVERT_EXPR, t, rhs));
+ rhs = s;
+ gsi_insert_before(&gsi, g, GSI_SAME_STMT);
+
+ }
+
+ /* Convert the operands to the hook arg type, if needed. */
+ tree t = TREE_VALUE(TYPE_ARG_TYPES(TREE_TYPE(fn)));
+
+ lhs = fold_convert_loc(UNKNOWN_LOCATION, t, lhs);
+ if (!is_gimple_val(lhs)) {
+
+ tree s = make_ssa_name(t);
+ gimple g = gimple_build_assign(s, lhs);
+ lhs = s;
+ gsi_insert_before(&gsi, g, GSI_SAME_STMT);
+
+ }
+
+ rhs = fold_convert_loc(UNKNOWN_LOCATION, t, rhs);
+ if (!is_gimple_val(rhs)) {
+
+ tree s = make_ssa_name(t);
+ gimple g = gimple_build_assign(s, rhs);
+ rhs = s;
+ gsi_insert_before(&gsi, g, GSI_SAME_STMT);
+
+ }
+
+ /* Insert the call. */
+ tree att = build_int_cst(t8u, attr);
+ gimple call;
+ if (pass_n)
+ call = gimple_build_call(fn, 4, lhs, rhs, att,
+ build_int_cst(t8u, sz / 8 - 1));
+ else
+ call = gimple_build_call(fn, 3, lhs, rhs, att);
+
+ gsi_insert_before(&gsi, call, GSI_SAME_STMT);
+
+ }
+
+ virtual unsigned int execute(function *fn) {
+
+ if (!isInInstrumentList(fn)) return 0;
+
+ basic_block bb;
+ FOR_EACH_BB_FN(bb, fn) {
+
+ /* A GIMPLE_COND or GIMPLE_SWITCH will always be the last stmt
+ in a BB. */
+ gimple_stmt_iterator gsi = gsi_last_bb(bb);
+ if (gsi_end_p(gsi)) continue;
+
+ gimple stmt = gsi_stmt(gsi);
+
+ if (gimple_code(stmt) == GIMPLE_COND) {
+
+ tree_code code = gimple_cond_code(stmt);
+ tree lhs = gimple_cond_lhs(stmt);
+ tree rhs = gimple_cond_rhs(stmt);
+
+ insert_cmplog_call(gsi, code, lhs, rhs);
+
+ } else if (gimple_code(stmt) == GIMPLE_SWITCH) {
+
+ gswitch *sw = as_a<gswitch *>(stmt);
+ tree lhs = gimple_switch_index(sw);
+
+ for (int i = 0, e = gimple_switch_num_labels(sw); i < e; i++) {
+
+ tree clx = gimple_switch_label(sw, i);
+ tree rhsl = CASE_LOW(clx);
+ /* Default case labels exprs don't have a CASE_LOW. */
+ if (!rhsl) continue;
+ tree rhsh = CASE_HIGH(clx);
+ /* If there is a CASE_HIGH, issue range compares. */
+ if (rhsh) {
+
+ insert_cmplog_call(gsi, GE_EXPR, lhs, rhsl);
+ insert_cmplog_call(gsi, LE_EXPR, lhs, rhsh);
+
+ }
+
+ /* Otherwise, use a single equality compare. */
+ else
+ insert_cmplog_call(gsi, EQ_EXPR, lhs, rhsl);
+
+ }
+
+ } else
+
+ continue;
+
+ }
+
+ return 0;
+
+ }
+
+};
+
+static struct plugin_info afl_cmplog_plugin = {
+
+ .version = "20220420",
+ .help = G_("AFL gcc cmplog plugin\n\
+\n\
+Set AFL_QUIET in the environment to silence it.\n\
+"),
+
+};
+
+} // namespace
+
+/* This is the function GCC calls when loading a plugin. Initialize
+ and register further callbacks. */
+int plugin_init(struct plugin_name_args *info,
+ struct plugin_gcc_version *version) {
+
+ if (!plugin_default_version_check(version, &gcc_version))
+ FATAL(G_("GCC and plugin have incompatible versions, expected GCC %s, "
+ "is %s"),
+ gcc_version.basever, version->basever);
+
+ /* Show a banner. */
+ bool quiet = false;
+ if (isatty(2) && !getenv("AFL_QUIET"))
+ SAYF(cCYA "afl-gcc-cmplog-pass " cBRI VERSION cRST
+ " by <oliva@adacore.com>\n");
+ else
+ quiet = true;
+
+ const char *name = info->base_name;
+ register_callback(name, PLUGIN_INFO, NULL, &afl_cmplog_plugin);
+
+ afl_cmplog_pass *aflp = new afl_cmplog_pass(quiet);
+ struct register_pass_info pass_info = {
+
+ .pass = aflp,
+ .reference_pass_name = "ssa",
+ .ref_pass_instance_number = 1,
+ .pos_op = PASS_POS_INSERT_AFTER,
+
+ };
+
+ register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info);
+
+ return 0;
+
+}
+
diff --git a/instrumentation/afl-gcc-cmptrs-pass.so.cc b/instrumentation/afl-gcc-cmptrs-pass.so.cc
new file mode 100644
index 00000000..929a9d7a
--- /dev/null
+++ b/instrumentation/afl-gcc-cmptrs-pass.so.cc
@@ -0,0 +1,369 @@
+/* GCC plugin for cmplog routines instrumentation of code for AFL++.
+
+ 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-2024 AdaCore
+
+ Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++
+ LLVM CmpLog Routines pass by Andrea Fioraldi
+ <andreafioraldi@gmail.com>, and on the AFL GCC CmpLog pass.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ */
+
+#include "afl-gcc-common.h"
+
+/* This plugin, being under the same license as GCC, satisfies the
+ "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY
+ EXCEPTION, so it can be part of an "Eligible" "Compilation
+ Process". */
+int plugin_is_GPL_compatible = 1;
+
+namespace {
+
+static const struct pass_data afl_cmptrs_pass_data = {
+
+ .type = GIMPLE_PASS,
+ .name = "aflcmptrs",
+ .optinfo_flags = OPTGROUP_NONE,
+ .tv_id = TV_NONE,
+ .properties_required = 0,
+ .properties_provided = 0,
+ .properties_destroyed = 0,
+ .todo_flags_start = 0,
+ .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il |
+ TODO_rebuild_cgraph_edges),
+
+};
+
+struct afl_cmptrs_pass : afl_base_pass {
+
+ afl_cmptrs_pass(bool quiet)
+ : afl_base_pass(quiet, /*debug=*/false, afl_cmptrs_pass_data),
+ tp8u(),
+ cmptrs_hooks() {
+
+ }
+
+ /* A pointer type to a unsigned 8-bit integral type. */
+ tree tp8u;
+
+ /* Declarations for the various cmptrs hook functions, allocated on
+ demand.. [0] is for compares between any pointers, [1] is for
+ compares between G++ std::string, [2] is for compares between G++
+ std::string and GCC C strings, [3] and [4] are analogous to [1]
+ and [2] but for LLVM C++ strings. */
+ tree cmptrs_hooks[5];
+
+ tree cmptrs_hook(unsigned i) {
+
+ if (!tp8u) {
+
+ tree t8u;
+ if (BITS_PER_UNIT == 8)
+ t8u = unsigned_char_type_node;
+ else
+ t8u = build_nonstandard_integer_type(8, 1);
+ tp8u = build_pointer_type(t8u);
+
+ }
+
+ if (i <= ARRAY_SIZE(cmptrs_hooks) && cmptrs_hooks[i])
+ return cmptrs_hooks[i];
+
+ const char *n = NULL;
+
+ switch (i) {
+
+ case 0:
+ n = "__cmplog_rtn_hook";
+ break;
+
+ case 1:
+ n = "__cmplog_rtn_gcc_stdstring_stdstring";
+ break;
+
+ case 2:
+ n = "__cmplog_rtn_gcc_stdstring_cstring";
+ break;
+
+ case 3:
+ n = "__cmplog_rtn_llvm_stdstring_stdstring";
+ break;
+
+ case 4:
+ n = "__cmplog_rtn_llvm_stdstring_cstring";
+ break;
+
+ default:
+ gcc_unreachable();
+
+ }
+
+ tree fnt = build_function_type_list(void_type_node, tp8u, tp8u, NULL_TREE);
+ tree t = cmptrs_hooks[i] = build_fn_decl(n, fnt);
+
+ /* Mark the newly-created decl as non-throwing, so that we can
+ insert call within basic blocks. */
+ TREE_NOTHROW(t) = 1;
+
+ return t;
+
+ }
+
+ /* Return true if T is the char* type. */
+ bool is_c_string(tree t) {
+
+ return (POINTER_TYPE_P(t) &&
+ TYPE_MAIN_VARIANT(TREE_TYPE(t)) == char_type_node);
+
+ }
+
+ /* Return true if T is an indirect std::string type. The LLVM pass
+ tests portions of the mangled name of the callee. We could do
+ that in GCC too, but computing the mangled name may cause
+ template instantiations and get symbols defined that could
+ otherwise be considered unused. We check for compatible layout,
+ and class, namespace, and field names. These have been unchanged
+ since at least GCC 7, probably longer, up to GCC 11. Odds are
+ that, if it were to change in significant ways, mangling would
+ also change to flag the incompatibility, and we'd have to use a
+ different hook anyway. */
+ bool is_gxx_std_string(tree t) {
+
+ /* We need a pointer or reference type. */
+ if (!POINTER_TYPE_P(t)) return false;
+
+ /* Get to the pointed-to type. */
+ t = TREE_TYPE(t);
+ if (!t) return false;
+
+ /* Select the main variant, so that can compare types with pointers. */
+ t = TYPE_MAIN_VARIANT(t);
+
+ /* 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;
+
+ /* It's declared in an internal namespace named __cxx11. */
+ tree c = DECL_CONTEXT(TYPE_NAME(t));
+ if (!c || TREE_CODE(c) != NAMESPACE_DECL ||
+ strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "__cxx11") != 0)
+ return false;
+
+ /* The __cxx11 namespace is a member of namespace std. */
+ c = DECL_CONTEXT(c);
+ if (!c || TREE_CODE(c) != NAMESPACE_DECL ||
+ strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "std") != 0)
+ return false;
+
+ /* And the std namespace is in the global namespace. */
+ 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
+ is named _M_dataplus. */
+ for (c = TYPE_FIELDS(t); c; c = DECL_CHAIN(c))
+ if (TREE_CODE(c) == FIELD_DECL) 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
+ 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 (!f2 /* No need to check this field's offset. */
+ || strcmp(IDENTIFIER_POINTER(DECL_NAME(f2)), "_M_string_length") != 0)
+ return false;
+
+ /* The type of the second data member is size_t. */
+ if (!TREE_TYPE(f2) || TYPE_MAIN_VARIANT(TREE_TYPE(f2)) != size_type_node)
+ return false;
+
+ /* 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 || !TYPE_IDENTIFIER(t) ||
+ strcmp(IDENTIFIER_POINTER(TYPE_IDENTIFIER(c)), "_Alloc_hider") != 0)
+ return false;
+
+ /* And its first data member is named _M_p. */
+ for (c = TYPE_FIELDS(c); c; c = DECL_CHAIN(c))
+ if (TREE_CODE(c) == FIELD_DECL) break;
+ if (!c || !integer_zerop(DECL_FIELD_BIT_OFFSET(c)) ||
+ strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "_M_p") != 0)
+ return false;
+
+ /* For the basic_string<char> type we're interested in, the type
+ of the data member is the C string type. */
+ if (!is_c_string(TREE_TYPE(c))) return false;
+
+ /* This might not be the real thing, but the bits that matter for
+ the hook are there. */
+
+ return true;
+
+ }
+
+ /* ??? This is not implemented. What would the point be of
+ recognizing LLVM's string type in GCC? */
+ bool is_llvm_std_string(tree t) {
+
+ return false;
+
+ }
+
+ virtual unsigned int execute(function *fn) {
+
+ if (!isInInstrumentList(fn)) return 0;
+
+ basic_block bb;
+ FOR_EACH_BB_FN(bb, fn) {
+
+ for (gimple_stmt_iterator gsi = gsi_after_labels(bb); !gsi_end_p(gsi);
+ gsi_next(&gsi)) {
+
+ gimple stmt = gsi_stmt(gsi);
+
+ /* We're only interested in GIMPLE_CALLs. */
+ if (gimple_code(stmt) != GIMPLE_CALL) continue;
+
+ if (gimple_call_num_args(stmt) < 2) continue;
+
+ gcall *c = as_a<gcall *>(stmt);
+
+ tree callee_type = gimple_call_fntype(c);
+
+ if (!callee_type || !TYPE_ARG_TYPES(callee_type) ||
+ !TREE_CHAIN(TYPE_ARG_TYPES(callee_type)))
+ continue;
+
+ tree arg_type[2] = {
+
+ TYPE_MAIN_VARIANT(TREE_VALUE(TYPE_ARG_TYPES(callee_type))),
+ TYPE_MAIN_VARIANT(
+ TREE_VALUE(TREE_CHAIN(TYPE_ARG_TYPES(callee_type))))};
+
+ tree fn = NULL;
+ /* Callee arglist starts with two GCC std::string arguments. */
+ if (arg_type[0] == arg_type[1] && is_gxx_std_string(arg_type[0]))
+ fn = cmptrs_hook(1);
+ /* Callee arglist starts with GCC std::string and C string. */
+ else if (is_gxx_std_string(arg_type[0]) && is_c_string(arg_type[1]))
+ fn = cmptrs_hook(2);
+ /* Callee arglist starts with two LLVM std::string arguments. */
+ else if (arg_type[0] == arg_type[1] && is_llvm_std_string(arg_type[0]))
+ fn = cmptrs_hook(3);
+ /* Callee arglist starts with LLVM std::string and C string. */
+ else if (is_llvm_std_string(arg_type[0]) && is_c_string(arg_type[1]))
+ fn = cmptrs_hook(4);
+ /* Callee arglist starts with two pointers to the same type,
+ and callee returns a value. */
+ else if (arg_type[0] == arg_type[1] && POINTER_TYPE_P(arg_type[0]) &&
+ (TYPE_MAIN_VARIANT(gimple_call_return_type(c)) !=
+ void_type_node))
+ fn = cmptrs_hook(0);
+ else
+ continue;
+
+ tree arg[2] = {gimple_call_arg(c, 0), gimple_call_arg(c, 1)};
+
+ for (unsigned i = 0; i < ARRAY_SIZE(arg); i++) {
+
+ tree c = fold_convert_loc(UNKNOWN_LOCATION, tp8u, arg[i]);
+ if (!is_gimple_val(c)) {
+
+ tree s = make_ssa_name(tp8u);
+ gimple g = gimple_build_assign(s, c);
+ c = s;
+ gsi_insert_before(&gsi, g, GSI_SAME_STMT);
+
+ }
+
+ arg[i] = c;
+
+ }
+
+ gimple call = gimple_build_call(fn, 2, arg[0], arg[1]);
+ gsi_insert_before(&gsi, call, GSI_SAME_STMT);
+
+ }
+
+ }
+
+ return 0;
+
+ }
+
+};
+
+static struct plugin_info afl_cmptrs_plugin = {
+
+ .version = "20220420",
+ .help = G_("AFL gcc cmptrs plugin\n\
+\n\
+Set AFL_QUIET in the environment to silence it.\n\
+"),
+
+};
+
+} // namespace
+
+/* This is the function GCC calls when loading a plugin. Initialize
+ and register further callbacks. */
+int plugin_init(struct plugin_name_args *info,
+ struct plugin_gcc_version *version) {
+
+ if (!plugin_default_version_check(version, &gcc_version))
+ FATAL(G_("GCC and plugin have incompatible versions, expected GCC %s, "
+ "is %s"),
+ gcc_version.basever, version->basever);
+
+ /* Show a banner. */
+ bool quiet = false;
+ if (isatty(2) && !getenv("AFL_QUIET"))
+ SAYF(cCYA "afl-gcc-cmptrs-pass " cBRI VERSION cRST
+ " by <oliva@adacore.com>\n");
+ else
+ quiet = true;
+
+ const char *name = info->base_name;
+ register_callback(name, PLUGIN_INFO, NULL, &afl_cmptrs_plugin);
+
+ afl_cmptrs_pass *aflp = new afl_cmptrs_pass(quiet);
+ struct register_pass_info pass_info = {
+
+ .pass = aflp,
+ .reference_pass_name = "ssa",
+ .ref_pass_instance_number = 1,
+ .pos_op = PASS_POS_INSERT_AFTER,
+
+ };
+
+ register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info);
+
+ return 0;
+
+}
+
diff --git a/instrumentation/afl-gcc-common.h b/instrumentation/afl-gcc-common.h
new file mode 100644
index 00000000..80ded57d
--- /dev/null
+++ b/instrumentation/afl-gcc-common.h
@@ -0,0 +1,508 @@
+/* GCC plugin common infrastructure for AFL++ instrumentation passes.
+
+ Copyright 2014-2019 Free Software Foundation, Inc
+ Copyright 2015, 2016 Google Inc. All rights reserved.
+ Copyright 2019-2024 AdaCore
+
+ Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++
+ GCC plugin.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ */
+
+#include "../include/config.h"
+#include "../include/debug.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef likely
+ #undef likely
+#endif
+#ifdef unlikely
+ #undef unlikely
+#endif
+
+#include <list>
+#include <string>
+#include <fstream>
+
+#include <algorithm>
+#include <fnmatch.h>
+
+#include <gcc-plugin.h>
+#include <plugin-version.h>
+#include <toplev.h>
+#include <tree-pass.h>
+#include <context.h>
+#include <tree.h>
+#include <gimplify.h>
+#include <basic-block.h>
+#include <tree-ssa-alias.h>
+#include <gimple-expr.h>
+#include <gimple.h>
+#include <gimple-iterator.h>
+#include <stringpool.h>
+#include <gimple-ssa.h>
+#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \
+ 60200 /* >= version 6.2.0 */
+ #include <tree-vrp.h>
+#endif
+#include <tree-ssanames.h>
+#include <tree-phinodes.h>
+#include <ssa-iterators.h>
+
+#include <intl.h>
+
+namespace {
+
+struct afl_base_pass : gimple_opt_pass {
+
+ afl_base_pass(bool quiet, bool debug, struct pass_data const &pd)
+ : gimple_opt_pass(pd, g), be_quiet(quiet), debug(debug) {
+
+ initInstrumentList();
+
+ }
+
+ /* Are we outputting to a non-terminal, or running with AFL_QUIET
+ set? */
+ const bool be_quiet;
+
+ /* Are we running with AFL_DEBUG set? */
+ const bool debug;
+
+#define report_fatal_error(msg) BADF(msg)
+
+ std::list<std::string> allowListFiles;
+ std::list<std::string> allowListFunctions;
+ std::list<std::string> denyListFiles;
+ std::list<std::string> denyListFunctions;
+
+ /* Note: this ignore check is also called in isInInstrumentList() */
+ bool isIgnoreFunction(function *F) {
+
+ // Starting from "LLVMFuzzer" these are functions used in libfuzzer based
+ // fuzzing campaign installations, e.g. oss-fuzz
+
+ static constexpr const char *ignoreList[] = {
+
+ "asan.",
+ "llvm.",
+ "sancov.",
+ "__ubsan_",
+ "ign.",
+ "__afl_",
+ "_fini",
+ "__libc_csu",
+ "__asan",
+ "__msan",
+ "__cmplog",
+ "__sancov",
+ "msan.",
+ "LLVMFuzzerM",
+ "LLVMFuzzerC",
+ "LLVMFuzzerI",
+ "__decide_deferred",
+ "maybe_duplicate_stderr",
+ "discard_output",
+ "close_stdout",
+ "dup_and_close_stderr",
+ "maybe_close_fd_mask",
+ "ExecuteFilesOnyByOne"
+
+ };
+
+ const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl));
+ int len = IDENTIFIER_LENGTH(DECL_NAME(F->decl));
+
+ for (auto const &ignoreListFunc : ignoreList) {
+
+ if (strncmp(name, ignoreListFunc, len) == 0) { return true; }
+
+ }
+
+ return false;
+
+ }
+
+ void initInstrumentList() {
+
+ char *allowlist = getenv("AFL_GCC_ALLOWLIST");
+ if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE");
+ if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST");
+ if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST");
+ if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE");
+ if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST");
+ char *denylist = getenv("AFL_GCC_DENYLIST");
+ if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST");
+ if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST");
+ if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST");
+
+ if (allowlist && denylist)
+ FATAL(
+ "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST "
+ "but not both!");
+
+ if (allowlist) {
+
+ std::string line;
+ std::ifstream fileStream;
+ fileStream.open(allowlist);
+ if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST");
+ getline(fileStream, line);
+
+ while (fileStream) {
+
+ int is_file = -1;
+ std::size_t npos;
+ std::string original_line = line;
+
+ line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
+ line.end());
+
+ // remove # and following
+ if ((npos = line.find("#")) != std::string::npos)
+ line = line.substr(0, npos);
+
+ if (line.compare(0, 4, "fun:") == 0) {
+
+ is_file = 0;
+ line = line.substr(4);
+
+ } else if (line.compare(0, 9, "function:") == 0) {
+
+ is_file = 0;
+ line = line.substr(9);
+
+ } else if (line.compare(0, 4, "src:") == 0) {
+
+ is_file = 1;
+ line = line.substr(4);
+
+ } else if (line.compare(0, 7, "source:") == 0) {
+
+ is_file = 1;
+ line = line.substr(7);
+
+ }
+
+ if (line.find(":") != std::string::npos) {
+
+ FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str());
+
+ }
+
+ if (line.length() > 0) {
+
+ // if the entry contains / or . it must be a file
+ if (is_file == -1)
+ if (line.find("/") != std::string::npos ||
+ line.find(".") != std::string::npos)
+ is_file = 1;
+ // otherwise it is a function
+
+ if (is_file == 1)
+ allowListFiles.push_back(line);
+ else
+ allowListFunctions.push_back(line);
+
+ }
+
+ getline(fileStream, line);
+
+ }
+
+ if (debug)
+ DEBUGF("loaded allowlist with %zu file and %zu function entries\n",
+ allowListFiles.size(), allowListFunctions.size());
+
+ }
+
+ if (denylist) {
+
+ std::string line;
+ std::ifstream fileStream;
+ fileStream.open(denylist);
+ if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST");
+ getline(fileStream, line);
+
+ while (fileStream) {
+
+ int is_file = -1;
+ std::size_t npos;
+ std::string original_line = line;
+
+ line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
+ line.end());
+
+ // remove # and following
+ if ((npos = line.find("#")) != std::string::npos)
+ line = line.substr(0, npos);
+
+ if (line.compare(0, 4, "fun:") == 0) {
+
+ is_file = 0;
+ line = line.substr(4);
+
+ } else if (line.compare(0, 9, "function:") == 0) {
+
+ is_file = 0;
+ line = line.substr(9);
+
+ } else if (line.compare(0, 4, "src:") == 0) {
+
+ is_file = 1;
+ line = line.substr(4);
+
+ } else if (line.compare(0, 7, "source:") == 0) {
+
+ is_file = 1;
+ line = line.substr(7);
+
+ }
+
+ if (line.find(":") != std::string::npos) {
+
+ FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str());
+
+ }
+
+ if (line.length() > 0) {
+
+ // if the entry contains / or . it must be a file
+ if (is_file == -1)
+ if (line.find("/") != std::string::npos ||
+ line.find(".") != std::string::npos)
+ is_file = 1;
+ // otherwise it is a function
+
+ if (is_file == 1)
+ denyListFiles.push_back(line);
+ else
+ denyListFunctions.push_back(line);
+
+ }
+
+ getline(fileStream, line);
+
+ }
+
+ if (debug)
+ DEBUGF("loaded denylist with %zu file and %zu function entries\n",
+ denyListFiles.size(), denyListFunctions.size());
+
+ }
+
+ }
+
+ /* Returns the source file name attached to the function declaration F. If
+ there is no source location information, returns an empty string. */
+ std::string getSourceName(function *F) {
+
+ return DECL_SOURCE_FILE(F->decl) ? DECL_SOURCE_FILE(F->decl) : "";
+
+ }
+
+ bool isInInstrumentList(function *F) {
+
+ bool return_default = true;
+
+ // is this a function with code? If it is external we don't instrument it
+ // anyway and it can't be in the instrument file list. Or if it is it is
+ // ignored.
+ if (isIgnoreFunction(F)) return false;
+
+ if (!denyListFiles.empty() || !denyListFunctions.empty()) {
+
+ if (!denyListFunctions.empty()) {
+
+ std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl));
+
+ for (std::list<std::string>::iterator it = denyListFunctions.begin();
+ it != denyListFunctions.end(); ++it) {
+
+ /* We don't check for filename equality here because
+ * filenames might actually be full paths. Instead we
+ * check that the actual filename ends in the filename
+ * specified in the list. We also allow UNIX-style pattern
+ * matching */
+
+ if (instFunction.length() >= it->length()) {
+
+ if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) {
+
+ if (debug)
+ DEBUGF(
+ "Function %s is in the deny function list, not "
+ "instrumenting ... \n",
+ instFunction.c_str());
+ return false;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if (!denyListFiles.empty()) {
+
+ std::string source_file = getSourceName(F);
+
+ if (!source_file.empty()) {
+
+ for (std::list<std::string>::iterator it = denyListFiles.begin();
+ it != denyListFiles.end(); ++it) {
+
+ /* We don't check for filename equality here because
+ * filenames might actually be full paths. Instead we
+ * check that the actual filename ends in the filename
+ * specified in the list. We also allow UNIX-style pattern
+ * matching */
+
+ if (source_file.length() >= it->length()) {
+
+ if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) {
+
+ return false;
+
+ }
+
+ }
+
+ }
+
+ } else {
+
+ // we could not find out the location. in this case we say it is not
+ // in the instrument file list
+ if (!be_quiet)
+ WARNF(
+ "No debug information found for function %s, will be "
+ "instrumented (recompile with -g -O[1-3]).",
+ IDENTIFIER_POINTER(DECL_NAME(F->decl)));
+
+ }
+
+ }
+
+ }
+
+ // if we do not have a instrument file list return true
+ if (!allowListFiles.empty() || !allowListFunctions.empty()) {
+
+ return_default = false;
+
+ if (!allowListFunctions.empty()) {
+
+ std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl));
+
+ for (std::list<std::string>::iterator it = allowListFunctions.begin();
+ it != allowListFunctions.end(); ++it) {
+
+ /* We don't check for filename equality here because
+ * filenames might actually be full paths. Instead we
+ * check that the actual filename ends in the filename
+ * specified in the list. We also allow UNIX-style pattern
+ * matching */
+
+ if (instFunction.length() >= it->length()) {
+
+ if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) {
+
+ if (debug)
+ DEBUGF(
+ "Function %s is in the allow function list, instrumenting "
+ "... \n",
+ instFunction.c_str());
+ return true;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if (!allowListFiles.empty()) {
+
+ std::string source_file = getSourceName(F);
+
+ if (!source_file.empty()) {
+
+ for (std::list<std::string>::iterator it = allowListFiles.begin();
+ it != allowListFiles.end(); ++it) {
+
+ /* We don't check for filename equality here because
+ * filenames might actually be full paths. Instead we
+ * check that the actual filename ends in the filename
+ * specified in the list. We also allow UNIX-style pattern
+ * matching */
+
+ if (source_file.length() >= it->length()) {
+
+ if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) {
+
+ if (debug)
+ DEBUGF(
+ "Function %s is in the allowlist (%s), instrumenting ... "
+ "\n",
+ IDENTIFIER_POINTER(DECL_NAME(F->decl)),
+ source_file.c_str());
+ return true;
+
+ }
+
+ }
+
+ }
+
+ } else {
+
+ // we could not find out the location. In this case we say it is not
+ // in the instrument file list
+ if (!be_quiet)
+ WARNF(
+ "No debug information found for function %s, will not be "
+ "instrumented (recompile with -g -O[1-3]).",
+ IDENTIFIER_POINTER(DECL_NAME(F->decl)));
+ return false;
+
+ }
+
+ }
+
+ }
+
+ return return_default;
+
+ }
+
+};
+
+} // namespace
+
+// compatibility for older gcc versions
+#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \
+ 60200 /* >= version 6.2.0 */
+ #define gimple gimple *
+#else
+ #define gimple gimple
+#endif
+
diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc
index 9483da83..8e9e7800 100644
--- a/instrumentation/afl-llvm-common.cc
+++ b/instrumentation/afl-llvm-common.cc
@@ -12,6 +12,7 @@
#include <list>
#include <string>
#include <fstream>
+#include <cmath>
#include <llvm/Support/raw_ostream.h>
@@ -96,11 +97,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
@@ -196,7 +201,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);
}
@@ -271,7 +276,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);
}
@@ -288,10 +293,11 @@ void scanForDangerousFunctions(llvm::Module *M) {
StringRef ifunc_name = IF.getName();
Constant *r = IF.getResolver();
+ if (r->getNumOperands() == 0) { continue; }
StringRef r_name = cast<Function>(r->getOperand(0))->getName();
if (!be_quiet)
fprintf(stderr,
- "Info: Found an ifunc with name %s that points to resolver "
+ "Note: Found an ifunc with name %s that points to resolver "
"function %s, we will not instrument this, putting it into the "
"block list.\n",
ifunc_name.str().c_str(), r_name.str().c_str());
@@ -329,7 +335,7 @@ void scanForDangerousFunctions(llvm::Module *M) {
if (!be_quiet)
fprintf(stderr,
- "Info: Found constructor function %s with prio "
+ "Note: Found constructor function %s with prio "
"%u, we will not instrument this, putting it into a "
"block list.\n",
F->getName().str().c_str(), Priority);
@@ -582,7 +588,7 @@ bool isInInstrumentList(llvm::Function *F, std::string Filename) {
}
// Calculate the number of average collisions that would occur if all
-// location IDs would be assigned randomly (like normal afl/afl++).
+// location IDs would be assigned randomly (like normal afl/AFL++).
// This uses the "balls in bins" algorithm.
unsigned long long int calculateCollisions(uint32_t edges) {
diff --git a/instrumentation/afl-llvm-common.h b/instrumentation/afl-llvm-common.h
index dee5f9fc..23f67179 100644
--- a/instrumentation/afl-llvm-common.h
+++ b/instrumentation/afl-llvm-common.h
@@ -8,6 +8,7 @@
#include <list>
#include <string>
#include <fstream>
+#include <optional>
#include <sys/time.h>
#include "llvm/Config/llvm-config.h"
@@ -21,7 +22,9 @@ typedef long double max_align_t;
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR < 17
+ #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
#if LLVM_VERSION_MAJOR > 3 || \
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
@@ -35,6 +38,12 @@ typedef long double max_align_t;
#if LLVM_VERSION_MAJOR >= 11
#define MNAME M.getSourceFileName()
#define FMNAME F.getParent()->getSourceFileName()
+ #if LLVM_VERSION_MAJOR >= 16
+// None becomes deprecated
+// the standard std::nullopt_t is recommended instead
+// from C++17 and onwards.
+constexpr std::nullopt_t None = std::nullopt;
+ #endif
#else
#define MNAME std::string("")
#define FMNAME std::string("")
diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc
index 31aaab07..ac497b5b 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-2022 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.
@@ -53,7 +53,9 @@
#include "llvm/IR/Verifier.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#if LLVM_VERSION_MAJOR < 17
+ #include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#endif
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ValueTracking.h"
@@ -181,8 +183,8 @@ bool AFLdict2filePass::runOnModule(Module &M) {
#endif
DenseMap<Value *, std::string *> valueMap;
- char * ptr;
- int found = 0;
+ char *ptr;
+ int found = 0, handle_main = 1;
/* Show a banner */
setvbuf(stdout, NULL, _IONBF, 0);
@@ -192,15 +194,30 @@ bool AFLdict2filePass::runOnModule(Module &M) {
SAYF(cCYA "afl-llvm-dict2file" VERSION cRST
" by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n");
- } else
+ } else {
be_quiet = 1;
+ }
+
+ if (getenv("AFL_LLVM_DICT2FILE_NO_MAIN")) { handle_main = 0; }
+
scanForDangerousFunctions(&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);
@@ -210,7 +227,14 @@ bool AFLdict2filePass::runOnModule(Module &M) {
for (auto &F : M) {
- if (isIgnoreFunction(&F)) continue;
+ if (!handle_main &&
+ (!F.getName().compare("main") || !F.getName().compare("_main"))) {
+
+ continue;
+
+ }
+
+ if (isIgnoreFunction(&F)) { continue; }
if (!isInInstrumentList(&F, MNAME) || !F.size()) { continue; }
/* Some implementation notes.
@@ -246,11 +270,11 @@ bool AFLdict2filePass::runOnModule(Module &M) {
for (auto &IN : BB) {
CallInst *callInst = nullptr;
- CmpInst * cmpInst = nullptr;
+ CmpInst *cmpInst = nullptr;
if ((cmpInst = dyn_cast<CmpInst>(&IN))) {
- Value * op = cmpInst->getOperand(1);
+ Value *op = cmpInst->getOperand(1);
ConstantInt *ilen = dyn_cast<ConstantInt>(op);
/* We skip > 64 bit integers. why? first because their value is
@@ -409,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();
@@ -518,7 +545,7 @@ bool AFLdict2filePass::runOnModule(Module &M) {
if (HasStr2 == true) {
- Value * op2 = callInst->getArgOperand(2);
+ Value *op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
if (ilen) {
@@ -631,7 +658,7 @@ bool AFLdict2filePass::runOnModule(Module &M) {
if (isMemcmp || isStrncmp || isStrncasecmp) {
- Value * op2 = callInst->getArgOperand(2);
+ Value *op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
if (ilen) {
@@ -733,7 +760,7 @@ static void registerAFLdict2filePass(const PassManagerBuilder &,
}
static RegisterPass<AFLdict2filePass> X("afl-dict2file",
- "afl++ dict2file instrumentation pass",
+ "AFL++ dict2file instrumentation pass",
false, false);
static RegisterStandardPasses RegisterAFLdict2filePass(
diff --git a/instrumentation/afl-llvm-lto-instrumentlist.so.cc b/instrumentation/afl-llvm-lto-instrumentlist.so.cc
index 70c6b10d..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-2022 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"
@@ -101,7 +101,7 @@ llvmGetPassPluginInfo() {
}
-PreservedAnalyses AFLcheckIfInstrument::run(Module & M,
+PreservedAnalyses AFLcheckIfInstrument::run(Module &M,
ModuleAnalysisManager &MAM) {
/* Show a banner */
@@ -135,7 +135,7 @@ PreservedAnalyses AFLcheckIfInstrument::run(Module & M,
DEBUGF("function %s is NOT in the instrument file list\n",
F.getName().str().c_str());
- auto & Ctx = F.getContext();
+ auto &Ctx = F.getContext();
AttributeList Attrs = F.getAttributes();
#if LLVM_VERSION_MAJOR >= 14
AttributeList NewAttrs = Attrs.addFnAttribute(Ctx, "skipinstrument");
diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc
index fde785bd..62f5023d 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-2022 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.
@@ -251,7 +251,7 @@ bool AFLCoverage::runOnModule(Module &M) {
/* Decide instrumentation ratio */
- char * inst_ratio_str = getenv("AFL_INST_RATIO");
+ char *inst_ratio_str = getenv("AFL_INST_RATIO");
unsigned int inst_ratio = 100;
if (inst_ratio_str) {
@@ -413,7 +413,7 @@ bool AFLCoverage::runOnModule(Module &M) {
GlobalVariable *AFLContext = NULL;
if (ctx_str || caller_str)
-#if defined(__ANDROID__) || defined(__HAIKU__)
+#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
#else
@@ -424,7 +424,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#ifdef AFL_HAVE_VECTOR_INTRINSICS
if (ngram_size)
- #if defined(__ANDROID__) || defined(__HAIKU__)
+ #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLPrevLoc = new GlobalVariable(
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
/* Initializer */ nullptr, "__afl_prev_loc");
@@ -437,7 +437,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#endif
else
#endif
-#if defined(__ANDROID__) || defined(__HAIKU__)
+#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLPrevLoc = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
#else
@@ -448,7 +448,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#ifdef AFL_HAVE_VECTOR_INTRINSICS
if (ctx_k)
- #if defined(__ANDROID__) || defined(__HAIKU__)
+ #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLPrevCaller = new GlobalVariable(
M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
/* Initializer */ nullptr, "__afl_prev_caller");
@@ -461,7 +461,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#endif
else
#endif
-#if defined(__ANDROID__) || defined(__HAIKU__)
+#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLPrevCaller =
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
"__afl_prev_caller");
@@ -486,7 +486,7 @@ bool AFLCoverage::runOnModule(Module &M) {
Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle);
- Constant * PrevCallerShuffleMask = NULL;
+ Constant *PrevCallerShuffleMask = NULL;
SmallVector<Constant *, 32> PrevCallerShuffle = {UndefValue::get(Int32Ty)};
if (ctx_k) {
@@ -506,7 +506,7 @@ bool AFLCoverage::runOnModule(Module &M) {
// other constants we need
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
- Value * PrevCtx = NULL; // CTX sensitive coverage
+ Value *PrevCtx = NULL; // CTX sensitive coverage
LoadInst *PrevCaller = NULL; // K-CTX coverage
/* Instrument all the things! */
@@ -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
diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc
index 4d37bcb2..dc60221e 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-2022 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 {
@@ -156,32 +156,34 @@ Iterator Unique(Iterator first, Iterator last) {
bool CmpLogInstructions::hookInstrs(Module &M) {
std::vector<Instruction *> icomps;
- LLVMContext & C = M.getContext();
+ LLVMContext &C = M.getContext();
- Type * VoidTy = Type::getVoidTy(C);
+ Type *VoidTy = Type::getVoidTy(C);
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int16Ty = IntegerType::getInt16Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
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
@@ -338,7 +340,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
IntegerType *intTyOp1 = NULL;
unsigned max_size = 0, cast_size = 0;
unsigned attr = 0, vector_cnt = 0, is_fp = 0;
- CmpInst * cmpInst = dyn_cast<CmpInst>(selectcmpInst);
+ CmpInst *cmpInst = dyn_cast<CmpInst>(selectcmpInst);
if (!cmpInst) { continue; }
@@ -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);
@@ -666,7 +668,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
}
#if LLVM_MAJOR >= 11 /* use new pass manager */
-PreservedAnalyses CmpLogInstructions::run(Module & M,
+PreservedAnalyses CmpLogInstructions::run(Module &M,
ModuleAnalysisManager &MAM) {
#else
diff --git a/instrumentation/cmplog-routines-pass.cc b/instrumentation/cmplog-routines-pass.cc
index 8205cfb0..78317d5d 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-2022 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.
@@ -38,7 +38,9 @@
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.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"
@@ -83,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 {
@@ -383,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") ||
@@ -396,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") !=
@@ -506,8 +509,8 @@ bool CmpLogRoutines::hookRtns(Module &M) {
IRBuilder<> IRB(ThenTerm);
std::vector<Value *> args;
- Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
- Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
+ Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
+ Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
args.push_back(v1Pcasted);
args.push_back(v2Pcasted);
@@ -537,9 +540,9 @@ bool CmpLogRoutines::hookRtns(Module &M) {
IRBuilder<> IRB(ThenTerm);
std::vector<Value *> args;
- Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
- Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
- Value * v3Pbitcast = IRB.CreateBitCast(
+ Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
+ Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
+ Value *v3Pbitcast = IRB.CreateBitCast(
v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits()));
Value *v3Pcasted =
IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false);
@@ -572,8 +575,8 @@ bool CmpLogRoutines::hookRtns(Module &M) {
IRBuilder<> IRB(ThenTerm);
std::vector<Value *> args;
- Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
- Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
+ Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
+ Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
args.push_back(v1Pcasted);
args.push_back(v2Pcasted);
@@ -603,9 +606,9 @@ bool CmpLogRoutines::hookRtns(Module &M) {
IRBuilder<> IRB(ThenTerm);
std::vector<Value *> args;
- Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
- Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
- Value * v3Pbitcast = IRB.CreateBitCast(
+ Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
+ Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
+ Value *v3Pbitcast = IRB.CreateBitCast(
v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits()));
Value *v3Pcasted =
IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false);
@@ -638,8 +641,8 @@ bool CmpLogRoutines::hookRtns(Module &M) {
IRBuilder<> IRB(ThenTerm);
std::vector<Value *> args;
- Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
- Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
+ Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
+ Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
args.push_back(v1Pcasted);
args.push_back(v2Pcasted);
@@ -668,8 +671,8 @@ bool CmpLogRoutines::hookRtns(Module &M) {
IRBuilder<> IRB(ThenTerm);
std::vector<Value *> args;
- Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
- Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
+ Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
+ Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
args.push_back(v1Pcasted);
args.push_back(v2Pcasted);
@@ -698,8 +701,8 @@ bool CmpLogRoutines::hookRtns(Module &M) {
IRBuilder<> IRB(ThenTerm);
std::vector<Value *> args;
- Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
- Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
+ Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
+ Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
args.push_back(v1Pcasted);
args.push_back(v2Pcasted);
@@ -728,8 +731,8 @@ bool CmpLogRoutines::hookRtns(Module &M) {
IRBuilder<> IRB(ThenTerm);
std::vector<Value *> args;
- Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
- Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
+ Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
+ Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
args.push_back(v1Pcasted);
args.push_back(v2Pcasted);
diff --git a/instrumentation/cmplog-switches-pass.cc b/instrumentation/cmplog-switches-pass.cc
index 37bf3889..3e05c13d 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-2022 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.
@@ -39,7 +39,9 @@
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.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"
@@ -83,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 {
@@ -149,9 +151,9 @@ Iterator Unique(Iterator first, Iterator last) {
bool CmplogSwitches::hookInstrs(Module &M) {
std::vector<SwitchInst *> switches;
- LLVMContext & C = M.getContext();
+ LLVMContext &C = M.getContext();
- Type * VoidTy = Type::getVoidTy(C);
+ Type *VoidTy = Type::getVoidTy(C);
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int16Ty = IntegerType::getInt16Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
@@ -270,7 +272,7 @@ bool CmplogSwitches::hookInstrs(Module &M) {
for (auto &SI : switches) {
- Value * Val = SI->getCondition();
+ Value *Val = SI->getCondition();
unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size;
unsigned char do_cast = 0;
diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc
index 34c88735..b0d6355a 100644
--- a/instrumentation/compare-transform-pass.so.cc
+++ b/instrumentation/compare-transform-pass.so.cc
@@ -168,10 +168,11 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
DenseMap<Value *, std::string *> valueMap;
std::vector<CallInst *> calls;
- LLVMContext & C = M.getContext();
- IntegerType * Int8Ty = IntegerType::getInt8Ty(C);
- IntegerType * Int32Ty = IntegerType::getInt32Ty(C);
- IntegerType * Int64Ty = IntegerType::getInt64Ty(C);
+ LLVMContext &C = M.getContext();
+ IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
+ IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
+ IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
+ IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
#if LLVM_VERSION_MAJOR >= 9
FunctionCallee tolowerFn;
@@ -227,9 +228,9 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
isStrcmp &=
(!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") ||
@@ -237,8 +238,8 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
!FuncName.compare("memcmp_const_time") ||
!FuncName.compare("memcmpct"));
isStrncmp &= (!FuncName.compare("strncmp") ||
- !FuncName.compare("xmlStrncmp") ||
- !FuncName.compare("curl_strnequal"));
+ !FuncName.compare("curl_strnequal") ||
+ !FuncName.compare("xmlStrncmp"));
isStrcasecmp &= (!FuncName.compare("strcasecmp") ||
!FuncName.compare("stricmp") ||
!FuncName.compare("ap_cstr_casecmp") ||
@@ -270,28 +271,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)
@@ -409,7 +412,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
/* check if third operand is a constant integer
* strlen("constStr") and sizeof() are treated as constant */
- Value * op2 = callInst->getArgOperand(2);
+ Value *op2 = callInst->getArgOperand(2);
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
if (ilen) {
@@ -449,7 +452,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
*Str2P = callInst->getArgOperand(1);
StringRef Str1, Str2, ConstStr;
std::string TmpConstStr;
- Value * VarStr;
+ Value *VarStr;
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
bool HasStr2 = getConstantStringInfo(Str2P, Str2);
uint64_t constStrLen, unrollLen, constSizedLen = 0;
@@ -457,7 +460,8 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
bool isSizedcmp = false;
bool isCaseInsensitive = false;
bool needs_null = false;
- Function * Callee = callInst->getCalledFunction();
+ bool success_is_one = false;
+ Function *Callee = callInst->getCalledFunction();
if (Callee) {
@@ -503,6 +507,12 @@ 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;
@@ -616,13 +626,13 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
for (uint64_t i = 0; i < unrollLen; i++) {
- BasicBlock * cur_cmp_bb = next_cmp_bb, *cur_lenchk_bb = next_lenchk_bb;
+ BasicBlock *cur_cmp_bb = next_cmp_bb, *cur_lenchk_bb = next_lenchk_bb;
unsigned char c;
if (cur_lenchk_bb) {
IRBuilder<> cur_lenchk_IRB(&*(cur_lenchk_bb->getFirstInsertionPt()));
- Value * icmp = cur_lenchk_IRB.CreateICmpEQ(
+ Value *icmp = cur_lenchk_IRB.CreateICmpEQ(
sizedValue, ConstantInt::get(sizedValue->getType(), i));
cur_lenchk_IRB.CreateCondBr(icmp, end_bb, cur_cmp_bb);
cur_lenchk_bb->getTerminator()->eraseFromParent();
@@ -667,6 +677,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);
@@ -708,7 +726,11 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
/* since the call is the first instruction of the bb it is safe to
* replace it with a phi instruction */
BasicBlock::iterator ii(callInst);
+#if LLVM_MAJOR >= 16
+ ReplaceInstWithInst(callInst->getParent(), ii, PN);
+#else
ReplaceInstWithInst(callInst->getParent()->getInstList(), ii, PN);
+#endif
}
diff --git a/instrumentation/injection-pass.cc b/instrumentation/injection-pass.cc
new file mode 100644
index 00000000..2280208b
--- /dev/null
+++ b/instrumentation/injection-pass.cc
@@ -0,0 +1,366 @@
+/*
+ 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
+
+ /* 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);
+
+ Value *parameter = callInst->getArgOperand(param);
+
+ std::vector<Value *> args;
+ Value *casted = IRB.CreatePointerCast(parameter, i8PtrTy);
+ args.push_back(casted);
+ IRB.CreateCall(FuncPtr, args);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return true;
+
+}
+
+#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; }
+
+ hookRtns(M);
+#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
+ auto PA = PreservedAnalyses::all();
+#endif
+ verifyModule(M);
+
+#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
+ return PA;
+#else
+ return true;
+#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 d7bb7aba..144025fb 100644
--- a/instrumentation/split-compares-pass.so.cc
+++ b/instrumentation/split-compares-pass.so.cc
@@ -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 {
@@ -230,9 +230,9 @@ char SplitComparesTransform::ID = 0;
/// FCMP instructions with predicate xGT or xLT and EQ
bool SplitComparesTransform::simplifyFPCompares(Module &M) {
- LLVMContext & C = M.getContext();
+ LLVMContext &C = M.getContext();
std::vector<Instruction *> fcomps;
- IntegerType * Int1Ty = IntegerType::getInt1Ty(C);
+ IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
/* iterate over all functions, bbs and instruction and add
* all integer comparisons with >= and <= predicates to the icomps vector */
@@ -322,8 +322,12 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) {
* block bb it is now at the position where the old FcmpInst was */
Instruction *fcmp_np;
fcmp_np = CmpInst::Create(Instruction::FCmp, new_pred, op0, op1);
+#if LLVM_MAJOR >= 16
+ fcmp_np->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+#else
bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
fcmp_np);
+#endif
/* create a new basic block which holds the new EQ fcmp */
Instruction *fcmp_eq;
@@ -331,7 +335,11 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) {
BasicBlock *middle_bb =
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
fcmp_eq = CmpInst::Create(Instruction::FCmp, CmpInst::FCMP_OEQ, op0, op1);
+#if LLVM_MAJOR >= 16
+ fcmp_eq->insertInto(middle_bb, middle_bb->end());
+#else
middle_bb->getInstList().push_back(fcmp_eq);
+#endif
/* add an unconditional branch to the end of middle_bb with destination
* end_bb */
BranchInst::Create(end_bb, middle_bb);
@@ -352,7 +360,11 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) {
PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb);
/* replace the old FcmpInst with our new and shiny PHI inst */
BasicBlock::iterator ii(FcmpInst);
+#if LLVM_MAJOR >= 16
+ ReplaceInstWithInst(FcmpInst->getParent(), ii, PN);
+#else
ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN);
+#endif
}
@@ -362,8 +374,8 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) {
/// This function splits ICMP instructions with xGE or xLE predicates into two
/// ICMP instructions with predicate xGT or xLT and EQ
-bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst * IcmpInst,
- Module & M,
+bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst *IcmpInst,
+ Module &M,
CmpWorklist &worklist) {
LLVMContext &C = M.getContext();
@@ -409,7 +421,11 @@ bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst * IcmpInst,
/* create the ICMP instruction with new_pred and add it to the old basic
* block bb it is now at the position where the old IcmpInst was */
CmpInst *icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1);
+#if LLVM_MAJOR >= 16
+ icmp_np->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+#else
bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), icmp_np);
+#endif
/* create a new basic block which holds the new EQ icmp */
CmpInst *icmp_eq;
@@ -417,7 +433,11 @@ bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst * IcmpInst,
BasicBlock *middle_bb =
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1);
+#if LLVM_MAJOR >= 16
+ icmp_eq->insertInto(middle_bb, middle_bb->end());
+#else
middle_bb->getInstList().push_back(icmp_eq);
+#endif
/* add an unconditional branch to the end of middle_bb with destination
* end_bb */
BranchInst::Create(end_bb, middle_bb);
@@ -438,9 +458,17 @@ bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst * IcmpInst,
PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb);
/* replace the old IcmpInst with our new and shiny PHI inst */
BasicBlock::iterator ii(IcmpInst);
+#if LLVM_MAJOR >= 16
+ ReplaceInstWithInst(IcmpInst->getParent(), ii, PN);
+#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;
@@ -498,7 +526,7 @@ bool SplitComparesTransform::simplifySignedCompare(CmpInst *IcmpInst, Module &M,
/* create a new basic block which is executed if the signedness bit is
* different */
- CmpInst * icmp_inv_sig_cmp;
+ CmpInst *icmp_inv_sig_cmp;
BasicBlock *sign_bb =
BasicBlock::Create(C, "sign", end_bb->getParent(), end_bb);
if (pred == CmpInst::ICMP_SGT) {
@@ -518,17 +546,25 @@ bool SplitComparesTransform::simplifySignedCompare(CmpInst *IcmpInst, Module &M,
}
+#if LLVM_MAJOR >= 16
+ icmp_inv_sig_cmp->insertInto(sign_bb, sign_bb->end());
+#else
sign_bb->getInstList().push_back(icmp_inv_sig_cmp);
+#endif
BranchInst::Create(end_bb, sign_bb);
/* create a new bb which is executed if signedness is equal */
- CmpInst * icmp_usign_cmp;
+ CmpInst *icmp_usign_cmp;
BasicBlock *middle_bb =
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
/* we can do a normal unsigned compare now */
icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1);
+#if LLVM_MAJOR >= 16
+ icmp_usign_cmp->insertInto(middle_bb, middle_bb->end());
+#else
middle_bb->getInstList().push_back(icmp_usign_cmp);
+#endif
BranchInst::Create(end_bb, middle_bb);
auto term = bb->getTerminator();
@@ -543,7 +579,11 @@ bool SplitComparesTransform::simplifySignedCompare(CmpInst *IcmpInst, Module &M,
PN->addIncoming(icmp_inv_sig_cmp, sign_bb);
BasicBlock::iterator ii(IcmpInst);
+#if LLVM_MAJOR >= 16
+ ReplaceInstWithInst(IcmpInst->getParent(), ii, PN);
+#else
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
+#endif
// save for later
worklist.push_back(icmp_usign_cmp);
@@ -566,8 +606,17 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
case CmpInst::ICMP_NE:
case CmpInst::ICMP_UGT:
case CmpInst::ICMP_ULT:
+ case CmpInst::ICMP_UGE:
+ case CmpInst::ICMP_ULE:
+ case CmpInst::ICMP_SGT:
+ case CmpInst::ICMP_SLT:
+ case CmpInst::ICMP_SGE:
+ case CmpInst::ICMP_SLE:
break;
default:
+ if (!be_quiet)
+ fprintf(stderr, "Error: split-compare: Unsupported predicate (%u)\n",
+ pred);
// unsupported predicate!
return false;
@@ -581,6 +630,8 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
if (!intTyOp0) {
// not an integer type
+ if (!be_quiet)
+ fprintf(stderr, "Error: split-compare: not an integer type\n");
return false;
}
@@ -595,11 +646,11 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
LLVMContext &C = M.getContext();
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
- BasicBlock * bb = cmp_inst->getParent();
+ BasicBlock *bb = cmp_inst->getParent();
IntegerType *OldIntType = IntegerType::get(C, bitw);
IntegerType *NewIntType = IntegerType::get(C, bitw / 2);
- BasicBlock * end_bb = bb->splitBasicBlock(BasicBlock::iterator(cmp_inst));
- CmpInst * icmp_high, *icmp_low;
+ BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(cmp_inst));
+ CmpInst *icmp_high, *icmp_low;
/* create the comparison of the top halves of the original operands */
Value *s_op0, *op0_high, *s_op1, *op1_high;
@@ -629,7 +680,7 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
BasicBlock *cmp_low_bb =
BasicBlock::Create(C, "" /*"injected"*/, end_bb->getParent(), end_bb);
- Value * op0_low, *op1_low;
+ Value *op0_low, *op1_low;
IRBuilder<> Builder(cmp_low_bb);
op0_low = Builder.CreateTrunc(op0, NewIntType);
@@ -675,6 +726,12 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
}
+ case CmpInst::ICMP_SGE:
+ case CmpInst::ICMP_SLE:
+ case CmpInst::ICMP_SGT:
+ case CmpInst::ICMP_SLT:
+ case CmpInst::ICMP_UGE:
+ case CmpInst::ICMP_ULE:
case CmpInst::ICMP_UGT:
case CmpInst::ICMP_ULT: {
@@ -684,22 +741,34 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
* if this is true we can go to the end if not we have to go to the
* bb which checks the lower half of the operands */
Instruction *op0_low, *op1_low;
- CmpInst * icmp_inv_cmp = nullptr;
- BasicBlock * inv_cmp_bb =
+ CmpInst *icmp_inv_cmp = nullptr;
+ BasicBlock *inv_cmp_bb =
BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb);
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
+ icmp_inv_cmp->insertInto(inv_cmp_bb, inv_cmp_bb->end());
+#else
inv_cmp_bb->getInstList().push_back(icmp_inv_cmp);
+#endif
worklist.push_back(icmp_inv_cmp);
auto term = bb->getTerminator();
@@ -710,12 +779,18 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
BasicBlock *cmp_low_bb =
BasicBlock::Create(C, "" /*"injected"*/, end_bb->getParent(), end_bb);
op0_low = new TruncInst(op0, NewIntType);
- cmp_low_bb->getInstList().push_back(op0_low);
op1_low = new TruncInst(op1, NewIntType);
- cmp_low_bb->getInstList().push_back(op1_low);
-
icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low);
+
+#if LLVM_MAJOR >= 16
+ op0_low->insertInto(cmp_low_bb, cmp_low_bb->end());
+ op1_low->insertInto(cmp_low_bb, cmp_low_bb->end());
+ icmp_low->insertInto(cmp_low_bb, cmp_low_bb->end());
+#else
+ cmp_low_bb->getInstList().push_back(op0_low);
+ cmp_low_bb->getInstList().push_back(op1_low);
cmp_low_bb->getInstList().push_back(icmp_low);
+#endif
BranchInst::Create(end_bb, cmp_low_bb);
BranchInst::Create(end_bb, cmp_low_bb, icmp_inv_cmp, inv_cmp_bb);
@@ -729,12 +804,18 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
}
default:
+ if (!be_quiet)
+ fprintf(stderr, "Error: split-compare: should not happen\n");
return false;
}
BasicBlock::iterator ii(cmp_inst);
+#if LLVM_MAJOR >= 16
+ ReplaceInstWithInst(cmp_inst->getParent(), ii, PN);
+#else
ReplaceInstWithInst(cmp_inst->getParent()->getInstList(), ii, PN);
+#endif
// We split the comparison into low and high. If this isn't our target
// bitwidth we recursively split the low and high parts again until we have
@@ -944,7 +1025,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
// BUG FIXME TODO: u64 does not work for > 64 bit ... e.g. 80 and 128 bit
if (sizeInBits > 64) { continue; }
- IntegerType * intType = IntegerType::get(C, op_size);
+ IntegerType *intType = IntegerType::get(C, op_size);
const unsigned int precision = sizeInBits == 32 ? 24
: sizeInBits == 64 ? 53
: sizeInBits == 128 ? 113
@@ -979,13 +1060,21 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
Instruction *bpre_op0, *bpre_op1;
bpre_op0 = CastInst::Create(Instruction::BitCast, op0,
IntegerType::get(C, op_size));
+#if LLVM_MAJOR >= 16
+ bpre_op0->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+#else
bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
bpre_op0);
+#endif
bpre_op1 = CastInst::Create(Instruction::BitCast, op1,
IntegerType::get(C, op_size));
+#if LLVM_MAJOR >= 16
+ bpre_op1->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+#else
bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
bpre_op1);
+#endif
/* Check if any operand is NaN.
* If so, all comparisons except unequal (which yields true) yield false */
@@ -1005,34 +1094,42 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
Instruction *nan_op0, *nan_op1;
nan_op0 = BinaryOperator::Create(Instruction::Shl, bpre_op0,
ConstantInt::get(bpre_op0->getType(), 1));
- bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
- nan_op0);
-
+ /* Check op1 for NaN */
+ /* Shift right 1 Bit, ignore sign bit */
+ nan_op1 = BinaryOperator::Create(Instruction::Shl, bpre_op1,
+ ConstantInt::get(bpre_op1->getType(), 1));
/* compare to NaN interval */
Instruction *is_op0_nan =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, nan_op0,
ConstantInt::get(intType, NaN_lowend));
+ /* compare to NaN interval */
+ Instruction *is_op1_nan =
+ CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, nan_op1,
+ ConstantInt::get(intType, NaN_lowend));
+ /* combine checks */
+ Instruction *is_nan =
+ BinaryOperator::Create(Instruction::Or, is_op0_nan, is_op1_nan);
+#if LLVM_MAJOR >= 16
+ nan_op0->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+ is_op0_nan->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+ nan_op1->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+ is_op1_nan->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+ is_nan->insertInto(bb, BasicBlock::iterator(bb->getTerminator()));
+#else
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+ nan_op0);
+
bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
is_op0_nan);
- /* Check op1 for NaN */
- /* Shift right 1 Bit, ignore sign bit */
- nan_op1 = BinaryOperator::Create(Instruction::Shl, bpre_op1,
- ConstantInt::get(bpre_op1->getType(), 1));
bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
nan_op1);
- /* compare to NaN interval */
- Instruction *is_op1_nan =
- CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, nan_op1,
- ConstantInt::get(intType, NaN_lowend));
bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
is_op1_nan);
- /* combine checks */
- Instruction *is_nan =
- BinaryOperator::Create(Instruction::Or, is_op0_nan, is_op1_nan);
bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), is_nan);
+#endif
/* the result of the comparison, when at least one op is NaN
is true only for the "NOT EQUAL" predicates. */
@@ -1052,30 +1149,41 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
/*** now working in nonan_bb ***/
/* Treat -0.0 as equal to +0.0, that is for -0.0 make it +0.0 */
- Instruction * b_op0, *b_op1;
- Instruction * isMzero_op0, *isMzero_op1;
+ Instruction *b_op0, *b_op1;
+ Instruction *isMzero_op0, *isMzero_op1;
const unsigned long long MinusZero = 1UL << (sizeInBits - 1U);
const unsigned long long PlusZero = 0;
isMzero_op0 = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, bpre_op0,
ConstantInt::get(intType, MinusZero));
+ isMzero_op1 = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, bpre_op1,
+ ConstantInt::get(intType, MinusZero));
+ b_op0 = SelectInst::Create(isMzero_op0, ConstantInt::get(intType, PlusZero),
+ bpre_op0);
+ b_op1 = SelectInst::Create(isMzero_op1, ConstantInt::get(intType, PlusZero),
+ bpre_op1);
+#if LLVM_MAJOR >= 16
+ isMzero_op0->insertInto(nonan_bb,
+ BasicBlock::iterator(nonan_bb->getTerminator()));
+ isMzero_op1->insertInto(nonan_bb,
+ BasicBlock::iterator(nonan_bb->getTerminator()));
+ b_op0->insertInto(nonan_bb,
+ BasicBlock::iterator(nonan_bb->getTerminator()));
+ b_op1->insertInto(nonan_bb,
+ BasicBlock::iterator(nonan_bb->getTerminator()));
+#else
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), isMzero_op0);
- isMzero_op1 = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, bpre_op1,
- ConstantInt::get(intType, MinusZero));
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), isMzero_op1);
- b_op0 = SelectInst::Create(isMzero_op0, ConstantInt::get(intType, PlusZero),
- bpre_op0);
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), b_op0);
- b_op1 = SelectInst::Create(isMzero_op1, ConstantInt::get(intType, PlusZero),
- bpre_op1);
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), b_op1);
+#endif
/* isolate signs of value of floating point type */
@@ -1086,26 +1194,35 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
s_s0 =
BinaryOperator::Create(Instruction::LShr, b_op0,
ConstantInt::get(b_op0->getType(), op_size - 1));
+ s_s1 =
+ BinaryOperator::Create(Instruction::LShr, b_op1,
+ ConstantInt::get(b_op1->getType(), op_size - 1));
+ t_s0 = new TruncInst(s_s0, Int1Ty);
+ t_s1 = new TruncInst(s_s1, Int1Ty);
+ /* compare of the sign bits */
+ icmp_sign_bit =
+ CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1);
+#if LLVM_MAJOR >= 16
+ s_s0->insertInto(nonan_bb, BasicBlock::iterator(nonan_bb->getTerminator()));
+ t_s0->insertInto(nonan_bb, BasicBlock::iterator(nonan_bb->getTerminator()));
+ s_s1->insertInto(nonan_bb, BasicBlock::iterator(nonan_bb->getTerminator()));
+ t_s1->insertInto(nonan_bb, BasicBlock::iterator(nonan_bb->getTerminator()));
+ icmp_sign_bit->insertInto(nonan_bb,
+ BasicBlock::iterator(nonan_bb->getTerminator()));
+#else
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), s_s0);
- t_s0 = new TruncInst(s_s0, Int1Ty);
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), t_s0);
- s_s1 =
- BinaryOperator::Create(Instruction::LShr, b_op1,
- ConstantInt::get(b_op1->getType(), op_size - 1));
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), s_s1);
- t_s1 = new TruncInst(s_s1, Int1Ty);
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), t_s1);
- /* compare of the sign bits */
- icmp_sign_bit =
- CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1);
nonan_bb->getInstList().insert(
BasicBlock::iterator(nonan_bb->getTerminator()), icmp_sign_bit);
+#endif
/* create a new basic block which is executed if the signedness bits are
* equal */
@@ -1137,17 +1254,31 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
s_e1 = BinaryOperator::Create(
Instruction::LShr, b_op1,
ConstantInt::get(b_op1->getType(), shiftR_exponent));
+#if LLVM_MAJOR >= 16
+ s_e0->insertInto(signequal_bb,
+ BasicBlock::iterator(signequal_bb->getTerminator()));
+ s_e1->insertInto(signequal_bb,
+ BasicBlock::iterator(signequal_bb->getTerminator()));
+#else
signequal_bb->getInstList().insert(
BasicBlock::iterator(signequal_bb->getTerminator()), s_e0);
signequal_bb->getInstList().insert(
BasicBlock::iterator(signequal_bb->getTerminator()), s_e1);
+#endif
t_e0 = new TruncInst(s_e0, IntExponentTy);
t_e1 = new TruncInst(s_e1, IntExponentTy);
+#if LLVM_MAJOR >= 16
+ t_e0->insertInto(signequal_bb,
+ BasicBlock::iterator(signequal_bb->getTerminator()));
+ t_e1->insertInto(signequal_bb,
+ BasicBlock::iterator(signequal_bb->getTerminator()));
+#else
signequal_bb->getInstList().insert(
BasicBlock::iterator(signequal_bb->getTerminator()), t_e0);
signequal_bb->getInstList().insert(
BasicBlock::iterator(signequal_bb->getTerminator()), t_e1);
+#endif
if (sizeInBits - precision < exTySizeBytes * 8) {
@@ -1157,10 +1288,17 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
m_e1 = BinaryOperator::Create(
Instruction::And, t_e1,
ConstantInt::get(t_e1->getType(), mask_exponent));
+#if LLVM_MAJOR >= 16
+ m_e0->insertInto(signequal_bb,
+ BasicBlock::iterator(signequal_bb->getTerminator()));
+ m_e1->insertInto(signequal_bb,
+ BasicBlock::iterator(signequal_bb->getTerminator()));
+#else
signequal_bb->getInstList().insert(
BasicBlock::iterator(signequal_bb->getTerminator()), m_e0);
signequal_bb->getInstList().insert(
BasicBlock::iterator(signequal_bb->getTerminator()), m_e1);
+#endif
} else {
@@ -1172,7 +1310,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
/* compare the exponents of the operands */
Instruction *icmp_exponents_equal;
Instruction *icmp_exponent_result;
- BasicBlock * signequal2_bb = signequal_bb;
+ BasicBlock *signequal2_bb = signequal_bb;
switch (FcmpInst->getPredicate()) {
case CmpInst::FCMP_UEQ:
@@ -1194,9 +1332,14 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
Instruction *icmp_exponent;
icmp_exponents_equal =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1);
+#if LLVM_MAJOR >= 16
+ icmp_exponents_equal->insertInto(
+ signequal_bb, BasicBlock::iterator(signequal_bb->getTerminator()));
+#else
signequal_bb->getInstList().insert(
BasicBlock::iterator(signequal_bb->getTerminator()),
icmp_exponents_equal);
+#endif
// shortcut for unequal exponents
signequal2_bb = signequal_bb->splitBasicBlock(
@@ -1210,9 +1353,15 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
icmp_exponent =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, m_e0, m_e1);
+#if LLVM_MAJOR >= 16
+ icmp_exponent->insertInto(
+ signequal2_bb,
+ BasicBlock::iterator(signequal2_bb->getTerminator()));
+#else
signequal2_bb->getInstList().insert(
BasicBlock::iterator(signequal2_bb->getTerminator()),
icmp_exponent);
+#endif
icmp_exponent_result =
BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0);
break;
@@ -1220,9 +1369,14 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
case CmpInst::FCMP_ULT:
icmp_exponents_equal =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1);
+#if LLVM_MAJOR >= 16
+ icmp_exponents_equal->insertInto(
+ signequal_bb, BasicBlock::iterator(signequal_bb->getTerminator()));
+#else
signequal_bb->getInstList().insert(
BasicBlock::iterator(signequal_bb->getTerminator()),
icmp_exponents_equal);
+#endif
// shortcut for unequal exponents
signequal2_bb = signequal_bb->splitBasicBlock(
@@ -1236,9 +1390,15 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
icmp_exponent =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, m_e0, m_e1);
+#if LLVM_MAJOR >= 16
+ icmp_exponent->insertInto(
+ signequal2_bb,
+ BasicBlock::iterator(signequal2_bb->getTerminator()));
+#else
signequal2_bb->getInstList().insert(
BasicBlock::iterator(signequal2_bb->getTerminator()),
icmp_exponent);
+#endif
icmp_exponent_result =
BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0);
break;
@@ -1247,9 +1407,14 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
}
+#if LLVM_MAJOR >= 16
+ icmp_exponent_result->insertInto(
+ signequal2_bb, BasicBlock::iterator(signequal2_bb->getTerminator()));
+#else
signequal2_bb->getInstList().insert(
BasicBlock::iterator(signequal2_bb->getTerminator()),
icmp_exponent_result);
+#endif
{
@@ -1299,19 +1464,33 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
m_f1 = BinaryOperator::Create(
Instruction::And, b_op1,
ConstantInt::get(b_op1->getType(), mask_fraction));
+#if LLVM_MAJOR >= 16
+ m_f0->insertInto(middle_bb,
+ BasicBlock::iterator(middle_bb->getTerminator()));
+ m_f1->insertInto(middle_bb,
+ BasicBlock::iterator(middle_bb->getTerminator()));
+#else
middle_bb->getInstList().insert(
BasicBlock::iterator(middle_bb->getTerminator()), m_f0);
middle_bb->getInstList().insert(
BasicBlock::iterator(middle_bb->getTerminator()), m_f1);
+#endif
if (needTrunc) {
t_f0 = new TruncInst(m_f0, IntFractionTy);
t_f1 = new TruncInst(m_f1, IntFractionTy);
+#if LLVM_MAJOR >= 16
+ t_f0->insertInto(middle_bb,
+ BasicBlock::iterator(middle_bb->getTerminator()));
+ t_f1->insertInto(middle_bb,
+ BasicBlock::iterator(middle_bb->getTerminator()));
+#else
middle_bb->getInstList().insert(
BasicBlock::iterator(middle_bb->getTerminator()), t_f0);
middle_bb->getInstList().insert(
BasicBlock::iterator(middle_bb->getTerminator()), t_f1);
+#endif
} else {
@@ -1326,10 +1505,17 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
t_f0 = new TruncInst(b_op0, IntFractionTy);
t_f1 = new TruncInst(b_op1, IntFractionTy);
+#if LLVM_MAJOR >= 16
+ t_f0->insertInto(middle_bb,
+ BasicBlock::iterator(middle_bb->getTerminator()));
+ t_f1->insertInto(middle_bb,
+ BasicBlock::iterator(middle_bb->getTerminator()));
+#else
middle_bb->getInstList().insert(
BasicBlock::iterator(middle_bb->getTerminator()), t_f0);
middle_bb->getInstList().insert(
BasicBlock::iterator(middle_bb->getTerminator()), t_f1);
+#endif
} else {
@@ -1342,26 +1528,36 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
/* compare the fractions of the operands */
Instruction *icmp_fraction_result;
- BasicBlock * middle2_bb = middle_bb;
- PHINode * PN2 = nullptr;
+ BasicBlock *middle2_bb = middle_bb;
+ PHINode *PN2 = nullptr;
switch (FcmpInst->getPredicate()) {
case CmpInst::FCMP_UEQ:
case CmpInst::FCMP_OEQ:
icmp_fraction_result =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_f0, t_f1);
+#if LLVM_MAJOR >= 16
+ icmp_fraction_result->insertInto(
+ middle2_bb, BasicBlock::iterator(middle2_bb->getTerminator()));
+#else
middle2_bb->getInstList().insert(
BasicBlock::iterator(middle2_bb->getTerminator()),
icmp_fraction_result);
+#endif
break;
case CmpInst::FCMP_UNE:
case CmpInst::FCMP_ONE:
icmp_fraction_result =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, t_f0, t_f1);
+#if LLVM_MAJOR >= 16
+ icmp_fraction_result->insertInto(
+ middle2_bb, BasicBlock::iterator(middle2_bb->getTerminator()));
+#else
middle2_bb->getInstList().insert(
BasicBlock::iterator(middle2_bb->getTerminator()),
icmp_fraction_result);
+#endif
break;
case CmpInst::FCMP_OGT:
@@ -1382,21 +1578,31 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
if (FcmpInst->getPredicate() == CmpInst::FCMP_OGT ||
FcmpInst->getPredicate() == CmpInst::FCMP_UGT) {
- negative_bb->getInstList().push_back(
- icmp_fraction_result = CmpInst::Create(
- Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1));
- positive_bb->getInstList().push_back(
- icmp_fraction_result2 = CmpInst::Create(
- Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1));
+ icmp_fraction_result =
+ CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1);
+ icmp_fraction_result2 =
+ 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, positive_bb->end());
+#else
+ negative_bb->getInstList().push_back(icmp_fraction_result);
+ positive_bb->getInstList().push_back(icmp_fraction_result2);
+#endif
} else {
- negative_bb->getInstList().push_back(
- icmp_fraction_result = CmpInst::Create(
- Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1));
- positive_bb->getInstList().push_back(
- icmp_fraction_result2 = CmpInst::Create(
- Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1));
+ icmp_fraction_result =
+ CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1);
+ icmp_fraction_result2 =
+ 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, positive_bb->end());
+#else
+ negative_bb->getInstList().push_back(icmp_fraction_result);
+ positive_bb->getInstList().push_back(icmp_fraction_result2);
+#endif
}
@@ -1410,8 +1616,13 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
PN2 = PHINode::Create(Int1Ty, 2, "");
PN2->addIncoming(icmp_fraction_result, negative_bb);
PN2->addIncoming(icmp_fraction_result2, positive_bb);
+#if LLVM_MAJOR >= 16
+ PN2->insertInto(middle2_bb,
+ BasicBlock::iterator(middle2_bb->getTerminator()));
+#else
middle2_bb->getInstList().insert(
BasicBlock::iterator(middle2_bb->getTerminator()), PN2);
+#endif
} break;
@@ -1474,7 +1685,11 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
}
BasicBlock::iterator ii(FcmpInst);
+#if LLVM_MAJOR >= 16
+ ReplaceInstWithInst(FcmpInst->getParent(), ii, PN);
+#else
ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN);
+#endif
++count;
}
@@ -1484,7 +1699,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
}
#if LLVM_MAJOR >= 11
-PreservedAnalyses SplitComparesTransform::run(Module & M,
+PreservedAnalyses SplitComparesTransform::run(Module &M,
ModuleAnalysisManager &MAM) {
#else
@@ -1492,12 +1707,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) {
@@ -1513,6 +1722,27 @@ bool SplitComparesTransform::runOnModule(Module &M) {
}
+ 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 LLVM_MAJOR >= 11
auto PA = PreservedAnalyses::all();
#endif
@@ -1525,42 +1755,46 @@ 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)) {
+
+ auto op0 = CI->getOperand(0);
+ auto op1 = CI->getOperand(1);
+ if (!op0 || !op1) {
#if LLVM_MAJOR >= 11
- return PA;
+ return PA;
#else
- return false;
+ return false;
#endif
- }
+ }
+
+ 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); }
+ }
}
@@ -1570,13 +1804,13 @@ 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);
+ }
}
diff --git a/instrumentation/split-switches-pass.so.cc b/instrumentation/split-switches-pass.so.cc
index 96e01a8b..e3dfea0d 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 {
@@ -102,7 +102,7 @@ class SplitSwitchesTransform : public ModulePass {
struct CaseExpr {
ConstantInt *Val;
- BasicBlock * BB;
+ BasicBlock *BB;
CaseExpr(ConstantInt *val = nullptr, BasicBlock *bb = nullptr)
: Val(val), BB(bb) {
@@ -182,7 +182,7 @@ BasicBlock *SplitSwitchesTransform::switchConvert(
unsigned ValTypeBitWidth = Cases[0].Val->getBitWidth();
IntegerType *ValType =
IntegerType::get(OrigBlock->getContext(), ValTypeBitWidth);
- IntegerType * ByteType = IntegerType::get(OrigBlock->getContext(), 8);
+ IntegerType *ByteType = IntegerType::get(OrigBlock->getContext(), 8);
unsigned BytesInValue = bytesChecked.size();
std::vector<uint8_t> setSizes;
std::vector<std::set<uint8_t> > byteSets(BytesInValue, std::set<uint8_t>());
@@ -221,16 +221,24 @@ BasicBlock *SplitSwitchesTransform::switchConvert(
/* there are only smallestSize different bytes at index smallestIndex */
Instruction *Shift, *Trunc;
- Function * F = OrigBlock->getParent();
- BasicBlock * NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F);
+ Function *F = OrigBlock->getParent();
+ BasicBlock *NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F);
Shift = BinaryOperator::Create(Instruction::LShr, Val,
ConstantInt::get(ValType, smallestIndex * 8));
+#if LLVM_VERSION_MAJOR >= 16
+ Shift->insertInto(NewNode, NewNode->end());
+#else
NewNode->getInstList().push_back(Shift);
+#endif
if (ValTypeBitWidth > 8) {
Trunc = new TruncInst(Shift, ByteType);
+#if LLVM_VERSION_MAJOR >= 16
+ Trunc->insertInto(NewNode, NewNode->end());
+#else
NewNode->getInstList().push_back(Trunc);
+#endif
} else {
@@ -253,7 +261,11 @@ BasicBlock *SplitSwitchesTransform::switchConvert(
ICmpInst *Comp =
new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte),
"byteMatch");
+#if LLVM_VERSION_MAJOR >= 16
+ Comp->insertInto(NewNode, NewNode->end());
+#else
NewNode->getInstList().push_back(Comp);
+#endif
bytesChecked[smallestIndex] = true;
bool allBytesAreChecked = true;
@@ -355,7 +367,11 @@ BasicBlock *SplitSwitchesTransform::switchConvert(
ICmpInst *Comp =
new ICmpInst(ICmpInst::ICMP_ULT, Trunc,
ConstantInt::get(ByteType, pivot), "byteMatch");
+#if LLVM_VERSION_MAJOR >= 16
+ Comp->insertInto(NewNode, NewNode->end());
+#else
NewNode->getInstList().push_back(Comp);
+#endif
BranchInst::Create(LBB, RBB, Comp, NewNode);
}
@@ -403,9 +419,9 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
BasicBlock *CurBlock = SI->getParent();
BasicBlock *OrigBlock = CurBlock;
- Function * F = CurBlock->getParent();
+ Function *F = CurBlock->getParent();
/* this is the value we are switching on */
- Value * Val = SI->getCondition();
+ Value *Val = SI->getCondition();
BasicBlock *Default = SI->getDefaultDest();
unsigned bitw = Val->getType()->getIntegerBitWidth();
@@ -445,14 +461,18 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
* round up bytesChecked (in case getBitWidth() % 8 != 0) */
std::vector<bool> bytesChecked((7 + Cases[0].Val->getBitWidth()) / 8,
false);
- BasicBlock * SwitchBlock =
+ BasicBlock *SwitchBlock =
switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, 0);
/* Branch to our shiny new if-then stuff... */
BranchInst::Create(SwitchBlock, OrigBlock);
/* We are now done with the switch instruction, delete it. */
+#if LLVM_VERSION_MAJOR >= 16
+ SI->eraseFromParent();
+#else
CurBlock->getInstList().erase(SI);
+#endif
/* we have to update the phi nodes! */
for (BasicBlock::iterator I = Default->begin(); I != Default->end(); ++I) {
@@ -483,7 +503,7 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
}
#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
-PreservedAnalyses SplitSwitchesTransform::run(Module & M,
+PreservedAnalyses SplitSwitchesTransform::run(Module &M,
ModuleAnalysisManager &MAM) {
#else