diff options
Diffstat (limited to 'llvm_mode')
-rw-r--r-- | llvm_mode/GNUmakefile | 36 | ||||
-rw-r--r-- | llvm_mode/LLVMInsTrim.so.cc | 2 | ||||
-rw-r--r-- | llvm_mode/README.instrument_file.md | 2 | ||||
-rw-r--r-- | llvm_mode/README.laf-intel.md | 4 | ||||
-rw-r--r-- | llvm_mode/README.lto.md | 62 | ||||
-rw-r--r-- | llvm_mode/README.md | 4 | ||||
-rw-r--r-- | llvm_mode/README.persistent_mode.md | 15 | ||||
-rw-r--r-- | llvm_mode/afl-clang-fast.c | 76 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-common.cc | 20 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-common.h | 9 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-lto-instrim.so.cc | 10 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-lto-instrumentation.so.cc | 54 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-lto-instrumentlist.so.cc | 52 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-pass.so.cc | 2 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-rt.o.c | 46 | ||||
-rw-r--r-- | llvm_mode/cmplog-instructions-pass.cc | 3 | ||||
-rw-r--r-- | llvm_mode/cmplog-routines-pass.cc | 3 | ||||
-rw-r--r-- | llvm_mode/compare-transform-pass.so.cc | 9 | ||||
-rw-r--r-- | llvm_mode/split-compares-pass.so.cc | 426 | ||||
-rw-r--r-- | llvm_mode/split-switches-pass.so.cc | 3 |
20 files changed, 593 insertions, 245 deletions
diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile index b5d026ef..0813af34 100644 --- a/llvm_mode/GNUmakefile +++ b/llvm_mode/GNUmakefile @@ -32,15 +32,16 @@ ifeq "$(shell uname)" "OpenBSD" LLVM_CONFIG ?= $(BIN_PATH)/llvm-config HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1) ifeq "$(HAS_OPT)" "1" - $(error llvm_mode needs a complete llvm installation (versions 3.4 up to 11) -> e.g. "pkg_add llvm-7.0.1p9") + $(error llvm_mode needs a complete llvm installation (versions 3.4 up to 12) -> e.g. "pkg_add llvm-7.0.1p9") endif else LLVM_CONFIG ?= llvm-config endif LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' ) -LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^1[2-9]' && echo 1 || echo 0 ) +LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^19' && echo 1 || echo 0 ) LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 ) +LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]' && echo 1 || echo 0 ) LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//') LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null) @@ -53,20 +54,29 @@ ifeq "$(LLVMVER)" "" endif ifeq "$(LLVM_UNSUPPORTED)" "1" - $(warning llvm_mode only supports llvm versions 3.4 up to 11) + $(warning llvm_mode only supports llvm versions 3.4 up to 12) endif +LLVM_TOO_OLD=1 + ifeq "$(LLVM_MAJOR)" "9" $(info [+] llvm_mode detected llvm 9, enabling neverZero implementation) + LLVM_TOO_OLD=0 endif ifeq "$(LLVM_NEW_API)" "1" $(info [+] llvm_mode detected llvm 10+, enabling neverZero implementation and c++14) LLVM_STDCXX = c++14 + LLVM_TOO_OLD=0 +endif + +ifeq "$(LLVM_TOO_OLD)" "1" + $(info [!] llvm_mode detected an old version of llvm, upgrade to at least 9 or preferable 11!) + $(shell sleep 1) endif -ifeq "$(LLVM_MAJOR)" "11" - $(info [+] llvm_mode detected llvm 11, enabling afl-clang-lto LTO implementation) +ifeq "$(LLVM_HAVE_LTO)" "1" + $(info [+] llvm_mode detected llvm 11+, enabling afl-clang-lto LTO implementation) LLVM_LTO = 1 #TEST_MMAP = 1 endif @@ -182,18 +192,24 @@ ifeq "$(LLVM_LTO)" "1" ifneq "$(shell readlink $(LLVM_BINDIR)/ld.lld 2>&1)" "" AFL_REAL_LD = $(LLVM_BINDIR)/ld.lld else - $(warn ld.lld not found, can not enable LTO mode) + $(warn ld.lld not found, cannot enable LTO mode) LLVM_LTO = 0 endif endif + else + $(warn -flto is not working (LLVMgold.so not found?), cannot enable LTO mode) + LLVM_LTO = 0 endif endif AFL_CLANG_FUSELD= -ifneq "$(AFL_CLANG_FLTO)" "" -ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=`command -v ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - AFL_CLANG_FUSELD=1 -endif +ifeq "$(LLVM_LTO)" "1" + ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=`command -v ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_FUSELD=1 + else + $(warn -fuse-ld is not working, cannot enable LTO mode) + LLVM_LTO = 0 + endif endif CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 75548266..4d8c4719 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -56,7 +56,6 @@ struct InsTrim : public ModulePass { protected: uint32_t function_minimum_size = 1; - uint32_t debug = 0; char * skip_nozero = NULL; private: @@ -102,7 +101,6 @@ struct InsTrim : public ModulePass { bool runOnModule(Module &M) override { - char be_quiet = 0; setvbuf(stdout, NULL, _IONBF, 0); if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { diff --git a/llvm_mode/README.instrument_file.md b/llvm_mode/README.instrument_file.md index 29c40eec..46e45ba2 100644 --- a/llvm_mode/README.instrument_file.md +++ b/llvm_mode/README.instrument_file.md @@ -18,6 +18,8 @@ For this purpose, I have added a "partial instrumentation" support to the LLVM mode of AFLFuzz that allows you to specify on a source file level which files should be compiled with or without instrumentation. +Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: +https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation ## 2) Building the LLVM module diff --git a/llvm_mode/README.laf-intel.md b/llvm_mode/README.laf-intel.md index 2fa4bc26..f63ab2bb 100644 --- a/llvm_mode/README.laf-intel.md +++ b/llvm_mode/README.laf-intel.md @@ -35,8 +35,8 @@ bit_width may be 64, 32 or 16. A new experimental feature is splitting floating point comparisons into a series of sign, exponent and mantissa comparisons followed by splitting each of them into 8 bit comparisons when necessary. -It is activated with the `AFL_LLVM_LAF_SPLIT_FLOATS` setting, available only -when `AFL_LLVM_LAF_SPLIT_COMPARES` is set. +It is activated with the `AFL_LLVM_LAF_SPLIT_FLOATS` setting. +Note that setting this automatically activates `AFL_LLVM_LAF_SPLIT_COMPARES` You can also set `AFL_LLVM_LAF_ALL` and have all of the above enabled :-) diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index 4641fa89..e521ac82 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -2,7 +2,7 @@ ## TLDR; -This version requires a current llvm 11 compiled from the github master. +This version requires a current llvm 11+ compiled from the github master. 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 @@ -10,7 +10,7 @@ This version requires a current llvm 11 compiled from the github master. 2. You can use it together with llvm_mode: laf-intel and the instrument file listing features and can be combined with cmplog/Redqueen -3. It only works with llvm 11 (current github master state) +3. It only works with llvm 11+ 4. AUTODICTIONARY feature! see below @@ -61,9 +61,9 @@ 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 11+ -### Installing llvm 11 from the llvm repository +### Installing llvm from the llvm repository (version 11) Installing the llvm snapshot builds is easy and mostly painless: @@ -83,7 +83,7 @@ apt-get install -y clang-11 clang-tools-11 libc++1-11 libc++-11-dev \ libomp5-11 lld-11 lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools ``` -### Building llvm 11 yourself +### Building llvm yourself (version 12) Building llvm from github takes quite some long time and is not painless: ``` @@ -125,10 +125,9 @@ NOTE: some targets also need to set the linker, try both `afl-clang-lto` and ## AUTODICTIONARY feature -Setting `AFL_LLVM_LTO_AUTODICTIONARY` will generate a dictionary in the -target binary based on string compare and memory compare functions. -afl-fuzz will automatically get these transmitted when starting to fuzz. -This improves coverage on a lot of targets. +While compiling, automatically a dictionary based on string comparisons is +generated put into the target binary. This dictionary is transfered to afl-fuzz +on start. This improves coverage statistically by 5-10% :) ## Fixed memory map @@ -141,12 +140,20 @@ to be dynamic - the original afl way, which is slower). AFL_LLVM_MAP_DYNAMIC can be set so the shared memory address is dynamic (which is safer but also slower). +## Document edge IDs + +Setting `export AFL_LLVM_DOCUMENT_IDS=file` will document to a file which edge +ID was given to which function. This helps to identify functions with variable +bytes or which functions were touched by an input. + ## Solving difficult targets Some targets are difficult because the configure script does unusual stuff that is unexpected for afl. See the next chapter `Potential issues` how to solve these. +### Example: ffmpeg + An example of a hard to solve target is ffmpeg. Here is how to successfully instrument it: @@ -156,7 +163,7 @@ instrument it: when compiling, so we have to trick configure: ``` -./configure --enable-lto --disable-shared +./configure --enable-lto --disable-shared --disable-inline-asm ``` 3. Now the configuration is done - and we edit the settings in `./ffbuild/config.mak` @@ -186,6 +193,31 @@ instrument it: 4. Then type make, wait for a long time and you are done :) +### Example: WebKit jsc + +Building jsc is difficult as the build script has bugs. + +1. checkout Webkit: +``` +svn checkout https://svn.webkit.org/repository/webkit/trunk WebKit +cd WebKit +``` + +2. Fix the build environment: +``` +mkdir -p WebKitBuild/Release +cd WebKitBuild/Release +ln -s ../../../../../usr/bin/llvm-ar-12 llvm-ar-12 +ln -s ../../../../../usr/bin/llvm-ranlib-12 llvm-ranlib-12 +cd ../.. +``` + +3. Build :) + +``` +Tools/Scripts/build-jsc --jsc-only --cli --cmakeargs="-DCMAKE_AR='llvm-ar-12' -DCMAKE_RANLIB='llvm-ranlib-12' -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_CC_FLAGS='-O3 -lrt' -DCMAKE_CXX_FLAGS='-O3 -lrt' -DIMPORTED_LOCATION='/lib/x86_64-linux-gnu/' -DCMAKE_CC=afl-clang-lto -DCMAKE_CXX=afl-clang-lto++ -DENABLE_STATIC_JSC=ON" +``` + ## Potential issues ### compiling libraries fails @@ -220,17 +252,17 @@ AS=llvm-as ... afl-clang-lto is still work in progress. Known issues: - * Anything that llvm 11 cannot compile, afl-clang-lto can not compile either - obviously + * Anything that llvm 11+ cannot compile, afl-clang-lto can not compile either - obviously * Anything that does not compile with LTO, afl-clang-lto can not compile either - obviously -Hence if building a target with afl-clang-lto fails try to build it with llvm11 -and LTO enabled (`CC=clang-11` `CXX=clang++-11` `CFLAGS=-flto=full` and +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` and `CXXFLAGS=-flto=full`). If this succeeeds then there is an issue with afl-clang-lto. Please report at [https://github.com/AFLplusplus/AFLplusplus/issues/226](https://github.com/AFLplusplus/AFLplusplus/issues/226) -Even some targets where clang-11 fails can be build if the fail is just in +Even some targets where clang-12 fails can be build if the fail is just in `./configure`, see `Solving difficult targets` above. ### Target crashes immediately @@ -270,7 +302,7 @@ Still more problems came up though as this only works without bugs from llvm 9 onwards, and with high optimization the link optimization ruins the instrumented control flow graph. -This is all now fixed with llvm 11. The llvm's own linker is now able to +This is all now fixed with llvm 11+. The llvm's own linker is now able to load passes and this bypasses all problems we had. Happy end :) diff --git a/llvm_mode/README.md b/llvm_mode/README.md index e2e22751..22088dfd 100644 --- a/llvm_mode/README.md +++ b/llvm_mode/README.md @@ -6,7 +6,7 @@ ## 1) Introduction -! llvm_mode works with llvm versions 3.4 up to 11 ! +! llvm_mode works with llvm versions 3.4 up to 12 ! The code in this directory allows you to instrument programs for AFL using true compiler-level instrumentation, instead of the more crude @@ -183,4 +183,4 @@ AFL_LLVM_INSTRUMENT=PCGUARD make ``` Note that this us currently the default, as it is the best mode. -If you have llvm 11 and compiled afl-clang-lto - this is the only better mode. +If you have llvm 11+ and compiled afl-clang-lto - this is the only better mode. diff --git a/llvm_mode/README.persistent_mode.md b/llvm_mode/README.persistent_mode.md index 83cc7f4d..4f0bcb2e 100644 --- a/llvm_mode/README.persistent_mode.md +++ b/llvm_mode/README.persistent_mode.md @@ -52,6 +52,21 @@ afl-clang-fast -o fuzz_target fuzz_target.c -lwhat_you_need_for_your_target And that is it! The speed increase is usually x10 to x20. +If you want to be able to compile the target without afl-clang-fast/lto then +add this just after the includes: + +``` +#ifndef __AFL_FUZZ_TESTCASE_LEN + ssize_t fuzz_len; + #define __AFL_FUZZ_TESTCASE_LEN fuzz_len + unsigned char fuzz_buf[1024000]; + #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf + #define __AFL_FUZZ_INIT() void sync(void); + #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? + #define __AFL_INIT() sync() +#endif +``` + ## 3) deferred initialization AFL tries to optimize performance by executing the targeted binary just once, diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index f1b03682..738433ac 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -162,6 +162,7 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; + u8 have_pic = 0; u8 *name; cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); @@ -268,7 +269,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES")) { + if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; @@ -311,12 +313,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD); cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - if (instrument_mode == INSTRUMENT_CFG) - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so", obj_path); - else - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); + /* + The current LTO instrim mode is not good, so we disable it + if (instrument_mode == INSTRUMENT_CFG) + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so", + obj_path); else + */ + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); cc_params[cc_par_cnt++] = lto_flag; } else { @@ -359,6 +364,19 @@ static void edit_params(u32 argc, char **argv, char **envp) { } + u32 idx; + if (lto_mode && argc > 1) { + + for (idx = 1; idx < argc; idx++) { + + if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1; + + } + + if (!have_pic) cc_params[cc_par_cnt++] = "-fPIC"; + + } + /* Detect stray -v calls from ./configure scripts. */ while (--argc) { @@ -379,6 +397,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) continue; + if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; + cc_params[cc_par_cnt++] = cur; } @@ -500,13 +520,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { "int __afl_sharedmem_fuzzing = 1;" "extern unsigned int *__afl_fuzz_len;" "extern unsigned char *__afl_fuzz_ptr;" - "unsigned char *__afl_fuzz_alt_ptr;"; + "unsigned char __afl_fuzz_alt[1024000];" + "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " - "(__afl_fuzz_alt_ptr = (unsigned char *) malloc(1 * 1024 * 1024)))"; + "__afl_fuzz_alt_ptr)"; cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : read(0, " - "__afl_fuzz_alt_ptr, 1 * 1024 * 1024))"; + "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " + "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1024000)) == 0xffffffff " + "? 0 : *__afl_fuzz_len)"; cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)=" @@ -617,6 +639,10 @@ int main(int argc, char **argv, char **envp) { } + if ((getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) && + getenv("AFL_DONT_OPTIMIZE")) + FATAL("AFL_LLVM_INSTRUMENT_FILE and AFL_DONT_OPTIMIZE cannot be combined"); + if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || getenv("INSTRIM_LIB")) { @@ -660,7 +686,7 @@ int main(int argc, char **argv, char **envp) { } if (strncasecmp(ptr, "pc-guard", strlen("pc-guard")) == 0 || - strncasecmp(ptr, "pcguard", strlen("pcgard")) == 0) { + strncasecmp(ptr, "pcguard", strlen("pcguard")) == 0) { if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD) instrument_mode = INSTRUMENT_PCGUARD; @@ -763,9 +789,19 @@ int main(int argc, char **argv, char **envp) { #if LLVM_VERSION_MAJOR <= 6 instrument_mode = INSTRUMENT_AFL; #else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) + if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) { + instrument_mode = INSTRUMENT_AFL; - else + WARNF( + "switching to classic instrumentation because " + "AFL_LLVM_INSTRUMENT_FILE does not work with PCGUARD. Use " + "-fsanitize-coverage-allowlist=allowlist.txt if you want to use " + "PCGUARD. Requires llvm 12+. See " + "https://clang.llvm.org/docs/" + "SanitizerCoverage.html#partially-disabling-instrumentation"); + + } else + instrument_mode = INSTRUMENT_PCGUARD; #endif @@ -813,9 +849,13 @@ int main(int argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD && (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST"))) - WARNF( + FATAL( "Instrumentation type PCGUARD does not support " - "AFL_LLVM_INSTRUMENT_FILE!"); + "AFL_LLVM_INSTRUMENT_FILE! Use " + "-fsanitize-coverage-allowlist=allowlist.txt instead (requires llvm " + "12+), see " + "https://clang.llvm.org/docs/" + "SanitizerCoverage.html#partially-disabling-instrumentation"); if (argc < 2 || strcmp(argv[1], "-h") == 0) { @@ -871,6 +911,8 @@ int main(int argc, char **argv, char **envp) { "AFL_NO_BUILTIN: compile for use with libtokencap.so\n" "AFL_PATH: path to instrumenting pass and runtime " "(afl-llvm-rt.*o)\n" + "AFL_LLVM_DOCUMENT_IDS: document edge IDs given to which function (LTO " + "only)\n" "AFL_QUIET: suppress verbose output\n" "AFL_USE_ASAN: activate address sanitizer\n" "AFL_USE_CFISAN: activate control flow sanitizer\n" @@ -939,7 +981,7 @@ int main(int argc, char **argv, char **envp) { u32 map_size = atoi(ptr2); if (map_size != MAP_SIZE) - FATAL("AFL_MAP_SIZE is not supported by afl-clang-fast"); + WARNF("AFL_MAP_SIZE is not supported by afl-clang-fast"); } diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 47b49358..9a884ded 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -14,6 +14,8 @@ #include <fstream> #include <llvm/Support/raw_ostream.h> + +#define IS_EXTERN extern #include "afl-llvm-common.h" using namespace llvm; @@ -88,6 +90,7 @@ void initInstrumentList() { char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); if (!instrumentListFilename) instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); + if (instrumentListFilename) { std::string line; @@ -105,6 +108,10 @@ void initInstrumentList() { } + if (debug) + SAYF(cMGN "[D] " cRST "loaded instrument list with %zu entries\n", + myInstrumentList.size()); + } bool isInInstrumentList(llvm::Function *F) { @@ -145,8 +152,6 @@ bool isInInstrumentList(llvm::Function *F) { } - (void)instLine; - /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { @@ -164,6 +169,10 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == 0) { + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the list (%s), instrumenting ... \n", + F->getName().str().c_str(), instFilename.str().c_str()); return true; } @@ -219,12 +228,15 @@ bool isInInstrumentList(llvm::Function *F) { // we could not find out the location. in this case we say it is not // in the 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]).", + F->getName().str().c_str()); return false; } - // return false; } diff --git a/llvm_mode/afl-llvm-common.h b/llvm_mode/afl-llvm-common.h index 38e0c830..5b96be43 100644 --- a/llvm_mode/afl-llvm-common.h +++ b/llvm_mode/afl-llvm-common.h @@ -38,5 +38,14 @@ void initInstrumentList(); bool isInInstrumentList(llvm::Function *F); unsigned long long int calculateCollisions(uint32_t edges); +#ifndef IS_EXTERN + #define IS_EXTERN +#endif + +IS_EXTERN int debug; +IS_EXTERN int be_quiet; + +#undef IS_EXTERN + #endif diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc index ca2b5886..98e9ff9a 100644 --- a/llvm_mode/afl-llvm-lto-instrim.so.cc +++ b/llvm_mode/afl-llvm-lto-instrim.so.cc @@ -73,8 +73,8 @@ struct InsTrimLTO : public ModulePass { protected: uint32_t function_minimum_size = 1; char * skip_nozero = NULL; - int afl_global_id = 1, debug = 0, autodictionary = 0; - uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0; + int afl_global_id = 1, autodictionary = 1; + uint32_t inst_blocks = 0, inst_funcs = 0; uint64_t map_addr = 0x10000; public: @@ -127,10 +127,6 @@ struct InsTrimLTO : public ModulePass { /* Process environment variables */ - if (getenv("AFL_LLVM_AUTODICTIONARY") || - getenv("AFL_LLVM_LTO_AUTODICTIONARY")) - autodictionary = 1; - if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { @@ -705,7 +701,7 @@ struct InsTrimLTO : public ModulePass { Value *Incr = IRB.CreateAdd(Counter, One); - if (skip_nozero) { + if (skip_nozero == NULL) { auto cf = IRB.CreateICmpEQ(Incr, Zero); auto carry = IRB.CreateZExt(cf, Int8Ty); diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index af2db3ff..5686eb56 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -86,9 +86,9 @@ class AFLLTOPass : public ModulePass { bool runOnModule(Module &M) override; protected: - int afl_global_id = 1, debug = 0, autodictionary = 0; + int afl_global_id = 1, autodictionary = 1; uint32_t function_minimum_size = 1; - uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0; + uint32_t inst_blocks = 0, inst_funcs = 0, total_instr = 0; uint64_t map_addr = 0x10000; char * skip_nozero = NULL; @@ -103,6 +103,7 @@ bool AFLLTOPass::runOnModule(Module &M) { std::vector<CallInst *> calls; DenseMap<Value *, std::string *> valueMap; char * ptr; + FILE * documentFile = NULL; IntegerType *Int8Ty = IntegerType::getInt8Ty(C); IntegerType *Int32Ty = IntegerType::getInt32Ty(C); @@ -120,15 +121,16 @@ bool AFLLTOPass::runOnModule(Module &M) { be_quiet = 1; - if (getenv("AFL_LLVM_AUTODICTIONARY") || - getenv("AFL_LLVM_LTO_AUTODICTIONARY")) - autodictionary = 1; + if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { + + if ((documentFile = fopen(ptr, "a")) == NULL) + WARNF("Cannot access document file %s", ptr); + + } if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; - if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || - getenv("AFL_LLVM_SKIPSINGLEBLOCK")) - function_minimum_size = 2; + if (getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { @@ -204,7 +206,8 @@ bool AFLLTOPass::runOnModule(Module &M) { if (debug) fprintf(stderr, - "DEBUG: Function %s is not the instrument file listed\n", + "DEBUG: Function %s is not in a source file that was specified " + "in the instrument file list\n", F.getName().str().c_str()); continue; @@ -536,6 +539,8 @@ bool AFLLTOPass::runOnModule(Module &M) { uint32_t succ = 0; + if (F.size() == 1) InsBlocks.push_back(&BB); + for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE; ++SI) if ((*SI)->size() > 0) succ++; @@ -554,9 +559,12 @@ bool AFLLTOPass::runOnModule(Module &M) { do { --i; + BasicBlock * newBB; BasicBlock * origBB = &(*InsBlocks[i]); std::vector<BasicBlock *> Successors; Instruction * TI = origBB->getTerminator(); + uint32_t fs = origBB->getParent()->size(); + uint32_t countto; for (succ_iterator SI = succ_begin(origBB), SE = succ_end(origBB); SI != SE; ++SI) { @@ -566,15 +574,25 @@ bool AFLLTOPass::runOnModule(Module &M) { } - if (TI == NULL || TI->getNumSuccessors() < 2) continue; + if (fs == 1) { + + newBB = origBB; + countto = 1; + + } else { + + if (TI == NULL || TI->getNumSuccessors() < 2) continue; + countto = Successors.size(); + + } // if (Successors.size() != TI->getNumSuccessors()) // FATAL("Different successor numbers %lu <-> %u\n", Successors.size(), // TI->getNumSuccessors()); - for (uint32_t j = 0; j < Successors.size(); j++) { + for (uint32_t j = 0; j < countto; j++) { - BasicBlock *newBB = llvm::SplitEdge(origBB, Successors[j]); + if (fs != 1) newBB = llvm::SplitEdge(origBB, Successors[j]); if (!newBB) { @@ -583,6 +601,13 @@ bool AFLLTOPass::runOnModule(Module &M) { } + if (documentFile) { + + fprintf(documentFile, "%s %u\n", F.getName().str().c_str(), + afl_global_id); + + } + BasicBlock::iterator IP = newBB->getFirstInsertionPt(); IRBuilder<> IRB(&(*IP)); @@ -615,7 +640,7 @@ bool AFLLTOPass::runOnModule(Module &M) { Value *Incr = IRB.CreateAdd(Counter, One); - if (skip_nozero) { + if (skip_nozero == NULL) { auto cf = IRB.CreateICmpEQ(Incr, Zero); auto carry = IRB.CreateZExt(cf, Int8Ty); @@ -638,6 +663,9 @@ bool AFLLTOPass::runOnModule(Module &M) { } + if (documentFile) fclose(documentFile); + documentFile = NULL; + // save highest location ID to global variable // do this after each function to fail faster if (!be_quiet && afl_global_id > MAP_SIZE && diff --git a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc index 6e6199e9..ab7c0c58 100644 --- a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc @@ -104,7 +104,6 @@ class AFLcheckIfInstrument : public ModulePass { protected: std::list<std::string> myInstrumentList; - int debug = 0; }; @@ -116,7 +115,6 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { /* Show a banner */ - char be_quiet = 0; setvbuf(stdout, NULL, _IONBF, 0); if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { @@ -164,32 +162,41 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { } + if (instFilename.str().empty()) { + + if (!be_quiet) + WARNF( + "Function %s has no source file name information and will " + "not be instrumented.", + F.getName().str().c_str()); + continue; + + } + } - (void)instLine; + //(void)instLine; + fprintf(stderr, "xxx %s %s\n", F.getName().str().c_str(), + instFilename.str().c_str()); if (debug) SAYF(cMGN "[D] " cRST "function %s is in file %s\n", F.getName().str().c_str(), instFilename.str().c_str()); - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { - for (std::list<std::string>::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + for (std::list<std::string>::iterator it = myInstrumentList.begin(); + it != myInstrumentList.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. */ - if (instFilename.str().length() >= it->length()) { + /* 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. */ + if (instFilename.str().length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { - instrumentFunction = true; - break; - - } + instrumentFunction = true; + break; } @@ -197,6 +204,15 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { } + } else { + + if (!be_quiet) + WARNF( + "No debug information found for function %s, recompile with -g " + "-O[1-3]", + F.getName().str().c_str()); + continue; + } /* Either we couldn't figure out our location or the location is diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 90cf3eb4..618abe48 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -82,7 +82,6 @@ class AFLCoverage : public ModulePass { protected: uint32_t ngram_size = 0; - uint32_t debug = 0; uint32_t map_size = MAP_SIZE; uint32_t function_minimum_size = 1; char * ctx_str = NULL, *skip_nozero = NULL; @@ -139,7 +138,6 @@ bool AFLCoverage::runOnModule(Module &M) { /* Show a banner */ - char be_quiet = 0; setvbuf(stdout, NULL, _IONBF, 0); if (getenv("AFL_DEBUG")) debug = 1; diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index f81d13ee..88abcbe0 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -53,7 +53,11 @@ #define CONST_PRIO 5 #ifndef MAP_FIXED_NOREPLACE - #define MAP_FIXED_NOREPLACE MAP_FIXED + #ifdef MAP_EXCL + #define MAP_FIXED_NOREPLACE MAP_EXCL | MAP_FIXED + #else + #define MAP_FIXED_NOREPLACE MAP_FIXED + #endif #endif #include <sys/mman.h> @@ -510,12 +514,19 @@ static void __afl_start_snapshots(void) { if (!child_pid) { + //(void)nice(-20); // does not seem to improve + signal(SIGCHLD, old_sigchld_handler); close(FORKSRV_FD); close(FORKSRV_FD + 1); - if (!afl_snapshot_do()) { raise(SIGSTOP); } + if (!afl_snapshot_take(AFL_SNAPSHOT_MMAP | AFL_SNAPSHOT_FDS | + AFL_SNAPSHOT_REGS | AFL_SNAPSHOT_EXIT)) { + + raise(SIGSTOP); + + } __afl_area_ptr[0] = 1; memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); @@ -713,6 +724,8 @@ static void __afl_start_forkserver(void) { if (!child_pid) { + //(void)nice(-20); + signal(SIGCHLD, old_sigchld_handler); close(FORKSRV_FD); @@ -846,6 +859,35 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { + // For stability analysis, if you want to know to which function unstable + // edge IDs belong to - uncomment, recompile+install llvm_mode, recompile + // the target. libunwind and libbacktrace are better solutions. + // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture + // the backtrace output + /* + uint32_t unstable[] = { ... unstable edge IDs }; + uint32_t idx; + char bt[1024]; + for (idx = 0; i < sizeof(unstable)/sizeof(uint32_t); i++) { + + if (unstable[idx] == __afl_area_ptr[*guard]) { + + int bt_size = backtrace(bt, 256); + if (bt_size > 0) { + + char **bt_syms = backtrace_symbols(bt, bt_size); + if (bt_syms) + fprintf(stderr, "DEBUG: edge=%u caller=%s\n", unstable[idx], + bt_syms[0]); + + } + + } + + } + + */ + __afl_area_ptr[*guard]++; } diff --git a/llvm_mode/cmplog-instructions-pass.cc b/llvm_mode/cmplog-instructions-pass.cc index f929361a..7c48d906 100644 --- a/llvm_mode/cmplog-instructions-pass.cc +++ b/llvm_mode/cmplog-instructions-pass.cc @@ -76,9 +76,6 @@ class CmpLogInstructions : public ModulePass { } - protected: - int be_quiet = 0; - private: bool hookInstrs(Module &M); diff --git a/llvm_mode/cmplog-routines-pass.cc b/llvm_mode/cmplog-routines-pass.cc index 318193a4..a0f8f64f 100644 --- a/llvm_mode/cmplog-routines-pass.cc +++ b/llvm_mode/cmplog-routines-pass.cc @@ -76,9 +76,6 @@ class CmpLogRoutines : public ModulePass { } - protected: - int be_quiet = 0; - private: bool hookRtns(Module &M); diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc index 2d1ab1cc..bed3597a 100644 --- a/llvm_mode/compare-transform-pass.so.cc +++ b/llvm_mode/compare-transform-pass.so.cc @@ -75,9 +75,6 @@ class CompareTransform : public ModulePass { } - protected: - int be_quiet = 0; - private: bool transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp, const bool processStrncmp, @@ -358,7 +355,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, Value * VarStr; bool HasStr1 = getConstantStringInfo(Str1P, Str1); bool HasStr2 = getConstantStringInfo(Str2P, Str2); - uint64_t constStrLen, constSizedLen, unrollLen; + uint64_t constStrLen, unrollLen, constSizedLen = 0; bool isMemcmp = !callInst->getCalledFunction()->getName().compare(StringRef("memcmp")); bool isSizedcmp = isMemcmp || @@ -474,8 +471,8 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, if (cur_lenchk_bb) { IRBuilder<> cur_lenchk_IRB(&*(cur_lenchk_bb->getFirstInsertionPt())); - Value * icmp = cur_lenchk_IRB.CreateICmpEQ(sizedValue, - ConstantInt::get(Int64Ty, i)); + 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(); diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc index 651fa5b4..3630bd8c 100644 --- a/llvm_mode/split-compares-pass.so.cc +++ b/llvm_mode/split-compares-pass.so.cc @@ -71,15 +71,13 @@ class SplitComparesTransform : public ModulePass { } - protected: - int be_quiet = 0; - private: int enableFPSplit; size_t splitIntCompares(Module &M, unsigned bitw); size_t splitFPCompares(Module &M); bool simplifyCompares(Module &M); + bool simplifyFPCompares(Module &M); bool simplifyIntSignedness(Module &M); size_t nextPowerOfTwo(size_t in); @@ -89,12 +87,11 @@ class SplitComparesTransform : public ModulePass { char SplitComparesTransform::ID = 0; -/* This function splits ICMP instructions with xGE or xLE predicates into two - * ICMP instructions with predicate xGT or xLT and EQ */ -bool SplitComparesTransform::simplifyCompares(Module &M) { +/* This function splits FCMP instructions with xGE or xLE predicates into two + * FCMP instructions with predicate xGT or xLT and EQ */ +bool SplitComparesTransform::simplifyFPCompares(Module &M) { LLVMContext & C = M.getContext(); - std::vector<Instruction *> icomps; std::vector<Instruction *> fcomps; IntegerType * Int1Ty = IntegerType::getInt1Ty(C); @@ -112,24 +109,6 @@ bool SplitComparesTransform::simplifyCompares(Module &M) { if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) { - if (selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) { - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); - IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType()); - - /* this is probably not needed but we do it anyway */ - if (!intTyOp0 || !intTyOp1) { continue; } - - icomps.push_back(selectcmpInst); - - } - if (enableFPSplit && (selectcmpInst->getPredicate() == CmpInst::FCMP_OGE || selectcmpInst->getPredicate() == CmpInst::FCMP_UGE || @@ -159,105 +138,159 @@ bool SplitComparesTransform::simplifyCompares(Module &M) { } - if (!icomps.size() && !fcomps.size()) { return false; } + if (!fcomps.size()) { return false; } - for (auto &IcmpInst : icomps) { + /* transform for floating point */ + for (auto &FcmpInst : fcomps) { - BasicBlock *bb = IcmpInst->getParent(); + BasicBlock *bb = FcmpInst->getParent(); - auto op0 = IcmpInst->getOperand(0); - auto op1 = IcmpInst->getOperand(1); + auto op0 = FcmpInst->getOperand(0); + auto op1 = FcmpInst->getOperand(1); /* find out what the new predicate is going to be */ - auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate(); + auto pred = dyn_cast<CmpInst>(FcmpInst)->getPredicate(); CmpInst::Predicate new_pred; switch (pred) { - case CmpInst::ICMP_UGE: - new_pred = CmpInst::ICMP_UGT; + case CmpInst::FCMP_UGE: + new_pred = CmpInst::FCMP_UGT; break; - case CmpInst::ICMP_SGE: - new_pred = CmpInst::ICMP_SGT; + case CmpInst::FCMP_OGE: + new_pred = CmpInst::FCMP_OGT; break; - case CmpInst::ICMP_ULE: - new_pred = CmpInst::ICMP_ULT; + case CmpInst::FCMP_ULE: + new_pred = CmpInst::FCMP_ULT; break; - case CmpInst::ICMP_SLE: - new_pred = CmpInst::ICMP_SLT; + case CmpInst::FCMP_OLE: + new_pred = CmpInst::FCMP_OLT; break; default: // keep the compiler happy continue; } - /* split before the icmp instruction */ - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); + /* split before the fcmp instruction */ + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst)); /* the old bb now contains a unconditional jump to the new one (end_bb) * we need to delete it later */ - /* 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 */ - Instruction *icmp_np; - icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); + /* create the FCMP instruction with new_pred and add it to the old basic + * 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); bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - icmp_np); + fcmp_np); - /* create a new basic block which holds the new EQ icmp */ - Instruction *icmp_eq; + /* create a new basic block which holds the new EQ fcmp */ + Instruction *fcmp_eq; /* insert middle_bb before end_bb */ BasicBlock *middle_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1); - middle_bb->getInstList().push_back(icmp_eq); + fcmp_eq = CmpInst::Create(Instruction::FCmp, CmpInst::FCMP_OEQ, op0, op1); + middle_bb->getInstList().push_back(fcmp_eq); /* add an unconditional branch to the end of middle_bb with destination * end_bb */ BranchInst::Create(end_bb, middle_bb); /* replace the uncond branch with a conditional one, which depends on the - * new_pred icmp. True goes to end, false to the middle (injected) bb */ + * new_pred fcmp. True goes to end, false to the middle (injected) bb */ auto term = bb->getTerminator(); - BranchInst::Create(end_bb, middle_bb, icmp_np, bb); + BranchInst::Create(end_bb, middle_bb, fcmp_np, bb); term->eraseFromParent(); - /* replace the old IcmpInst (which is the first inst in end_bb) with a PHI + /* replace the old FcmpInst (which is the first inst in end_bb) with a PHI * inst to wire up the loose ends */ PHINode *PN = PHINode::Create(Int1Ty, 2, ""); - /* the first result depends on the outcome of icmp_eq */ - PN->addIncoming(icmp_eq, middle_bb); - /* if the source was the original bb we know that the icmp_np yielded true + /* the first result depends on the outcome of fcmp_eq */ + PN->addIncoming(fcmp_eq, middle_bb); + /* if the source was the original bb we know that the fcmp_np yielded true * hence we can hardcode this value */ PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); - /* replace the old IcmpInst with our new and shiny PHI inst */ - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + /* replace the old FcmpInst with our new and shiny PHI inst */ + BasicBlock::iterator ii(FcmpInst); + ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); } - /* now for floating point */ - for (auto &FcmpInst : fcomps) { + return true; - BasicBlock *bb = FcmpInst->getParent(); +} - auto op0 = FcmpInst->getOperand(0); - auto op1 = FcmpInst->getOperand(1); +/* This function splits ICMP instructions with xGE or xLE predicates into two + * ICMP instructions with predicate xGT or xLT and EQ */ +bool SplitComparesTransform::simplifyCompares(Module &M) { + + LLVMContext & C = M.getContext(); + std::vector<Instruction *> icomps; + IntegerType * Int1Ty = IntegerType::getInt1Ty(C); + + /* iterate over all functions, bbs and instruction and add + * all integer comparisons with >= and <= predicates to the icomps vector */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CmpInst *selectcmpInst = nullptr; + + if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) { + + if (selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || + selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || + selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || + selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) { + + auto op0 = selectcmpInst->getOperand(0); + auto op1 = selectcmpInst->getOperand(1); + + IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); + IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType()); + + /* this is probably not needed but we do it anyway */ + if (!intTyOp0 || !intTyOp1) { continue; } + + icomps.push_back(selectcmpInst); + + } + + } + + } + + } + + } + + if (!icomps.size()) { return false; } + + for (auto &IcmpInst : icomps) { + + BasicBlock *bb = IcmpInst->getParent(); + + auto op0 = IcmpInst->getOperand(0); + auto op1 = IcmpInst->getOperand(1); /* find out what the new predicate is going to be */ - auto pred = dyn_cast<CmpInst>(FcmpInst)->getPredicate(); + auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate(); CmpInst::Predicate new_pred; switch (pred) { - case CmpInst::FCMP_UGE: - new_pred = CmpInst::FCMP_UGT; + case CmpInst::ICMP_UGE: + new_pred = CmpInst::ICMP_UGT; break; - case CmpInst::FCMP_OGE: - new_pred = CmpInst::FCMP_OGT; + case CmpInst::ICMP_SGE: + new_pred = CmpInst::ICMP_SGT; break; - case CmpInst::FCMP_ULE: - new_pred = CmpInst::FCMP_ULT; + case CmpInst::ICMP_ULE: + new_pred = CmpInst::ICMP_ULT; break; - case CmpInst::FCMP_OLE: - new_pred = CmpInst::FCMP_OLT; + case CmpInst::ICMP_SLE: + new_pred = CmpInst::ICMP_SLT; break; default: // keep the compiler happy continue; @@ -265,25 +298,25 @@ bool SplitComparesTransform::simplifyCompares(Module &M) { } /* split before the icmp instruction */ - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst)); + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); /* the old bb now contains a unconditional jump to the new one (end_bb) * we need to delete it later */ /* 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 */ - Instruction *fcmp_np; - fcmp_np = CmpInst::Create(Instruction::FCmp, new_pred, op0, op1); + Instruction *icmp_np; + icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - fcmp_np); + icmp_np); - /* create a new basic block which holds the new EQ fcmp */ - Instruction *fcmp_eq; + /* create a new basic block which holds the new EQ icmp */ + Instruction *icmp_eq; /* insert middle_bb before end_bb */ BasicBlock *middle_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - fcmp_eq = CmpInst::Create(Instruction::FCmp, CmpInst::FCMP_OEQ, op0, op1); - middle_bb->getInstList().push_back(fcmp_eq); + icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1); + middle_bb->getInstList().push_back(icmp_eq); /* add an unconditional branch to the end of middle_bb with destination * end_bb */ BranchInst::Create(end_bb, middle_bb); @@ -291,20 +324,20 @@ bool SplitComparesTransform::simplifyCompares(Module &M) { /* replace the uncond branch with a conditional one, which depends on the * new_pred icmp. True goes to end, false to the middle (injected) bb */ auto term = bb->getTerminator(); - BranchInst::Create(end_bb, middle_bb, fcmp_np, bb); + BranchInst::Create(end_bb, middle_bb, icmp_np, bb); term->eraseFromParent(); /* replace the old IcmpInst (which is the first inst in end_bb) with a PHI * inst to wire up the loose ends */ PHINode *PN = PHINode::Create(Int1Ty, 2, ""); /* the first result depends on the outcome of icmp_eq */ - PN->addIncoming(fcmp_eq, middle_bb); + PN->addIncoming(icmp_eq, middle_bb); /* if the source was the original bb we know that the icmp_np yielded true * hence we can hardcode this value */ PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); /* replace the old IcmpInst with our new and shiny PHI inst */ - BasicBlock::iterator ii(FcmpInst); - ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); + BasicBlock::iterator ii(IcmpInst); + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); } @@ -640,7 +673,8 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { BranchInst::Create(end_bb, signequal_bb); - /* create a new bb which is executed if exponents are equal */ + /* create a new bb which is executed if exponents are satisfying the compare + */ BasicBlock *middle_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); @@ -695,7 +729,9 @@ 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; switch (FcmpInst->getPredicate()) { case CmpInst::FCMP_OEQ: @@ -707,22 +743,60 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { icmp_exponent_result = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, m_e0, m_e1); break; + /* compare the exponents of the operands (signs are equal) + * if exponents are equal -> proceed to mantissa comparison + * else get result depending on sign + */ case CmpInst::FCMP_OGT: case CmpInst::FCMP_UGT: Instruction *icmp_exponent; + icmp_exponents_equal = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), + icmp_exponents_equal); + + // shortcut for unequal exponents + signequal2_bb = signequal_bb->splitBasicBlock( + BasicBlock::iterator(signequal_bb->getTerminator())); + + /* if the exponents are equal goto middle_bb else to signequal2_bb */ + term = signequal_bb->getTerminator(); + BranchInst::Create(middle_bb, signequal2_bb, icmp_exponents_equal, + signequal_bb); + term->eraseFromParent(); + icmp_exponent = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, m_e0, m_e1); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), icmp_exponent); + signequal2_bb->getInstList().insert( + BasicBlock::iterator(signequal2_bb->getTerminator()), + icmp_exponent); icmp_exponent_result = BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0); break; case CmpInst::FCMP_OLT: case CmpInst::FCMP_ULT: + icmp_exponents_equal = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), + icmp_exponents_equal); + + // shortcut for unequal exponents + signequal2_bb = signequal_bb->splitBasicBlock( + BasicBlock::iterator(signequal_bb->getTerminator())); + + /* if the exponents are equal goto middle_bb else to signequal2_bb */ + term = signequal_bb->getTerminator(); + BranchInst::Create(middle_bb, signequal2_bb, icmp_exponents_equal, + signequal_bb); + term->eraseFromParent(); + icmp_exponent = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, m_e0, m_e1); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), icmp_exponent); + signequal2_bb->getInstList().insert( + BasicBlock::iterator(signequal2_bb->getTerminator()), + icmp_exponent); icmp_exponent_result = BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0); break; @@ -731,15 +805,40 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { } - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), + signequal2_bb->getInstList().insert( + BasicBlock::iterator(signequal2_bb->getTerminator()), icmp_exponent_result); { - auto term = signequal_bb->getTerminator(); - /* if the exponents are different do a fraction cmp */ - BranchInst::Create(middle_bb, end_bb, icmp_exponent_result, signequal_bb); + term = signequal2_bb->getTerminator(); + + switch (FcmpInst->getPredicate()) { + + case CmpInst::FCMP_OEQ: + /* if the exponents are satifying the compare do a fraction cmp in + * middle_bb */ + BranchInst::Create(middle_bb, end_bb, icmp_exponent_result, + signequal2_bb); + break; + case CmpInst::FCMP_ONE: + case CmpInst::FCMP_UNE: + /* if the exponents are satifying the compare do a fraction cmp in + * middle_bb */ + BranchInst::Create(end_bb, middle_bb, icmp_exponent_result, + signequal2_bb); + break; + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: + BranchInst::Create(end_bb, signequal2_bb); + break; + default: + continue; + + } + term->eraseFromParent(); } @@ -800,44 +899,82 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { /* compare the fractions of the operands */ Instruction *icmp_fraction_result; + Instruction *icmp_fraction_result2; + BasicBlock * middle2_bb = middle_bb; + PHINode * PN2 = nullptr; switch (FcmpInst->getPredicate()) { case CmpInst::FCMP_OEQ: icmp_fraction_result = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_f0, t_f1); + middle2_bb->getInstList().insert( + BasicBlock::iterator(middle2_bb->getTerminator()), + icmp_fraction_result); + break; case CmpInst::FCMP_UNE: case CmpInst::FCMP_ONE: icmp_fraction_result = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, t_f0, t_f1); + middle2_bb->getInstList().insert( + BasicBlock::iterator(middle2_bb->getTerminator()), + icmp_fraction_result); + break; case CmpInst::FCMP_OGT: case CmpInst::FCMP_UGT: - Instruction *icmp_fraction; - icmp_fraction = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1); - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), icmp_fraction); - icmp_fraction_result = - BinaryOperator::Create(Instruction::Xor, icmp_fraction, t_s0); - break; case CmpInst::FCMP_OLT: - case CmpInst::FCMP_ULT: - icmp_fraction = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1); - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), icmp_fraction); - icmp_fraction_result = - BinaryOperator::Create(Instruction::Xor, icmp_fraction, t_s0); - break; + case CmpInst::FCMP_ULT: { + + middle2_bb = middle_bb->splitBasicBlock( + BasicBlock::iterator(middle_bb->getTerminator())); + + BasicBlock *negative_bb = BasicBlock::Create( + C, "negative_value", middle2_bb->getParent(), middle2_bb); + BasicBlock *positive_bb = BasicBlock::Create( + C, "positive_value", negative_bb->getParent(), negative_bb); + + 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)); + + } 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)); + + } + + BranchInst::Create(middle2_bb, negative_bb); + BranchInst::Create(middle2_bb, positive_bb); + + term = middle_bb->getTerminator(); + BranchInst::Create(negative_bb, positive_bb, t_s0, middle_bb); + term->eraseFromParent(); + + PN2 = PHINode::Create(Int1Ty, 2, ""); + PN2->addIncoming(icmp_fraction_result, negative_bb); + PN2->addIncoming(icmp_fraction_result2, positive_bb); + middle2_bb->getInstList().insert( + BasicBlock::iterator(middle2_bb->getTerminator()), PN2); + + } break; + default: continue; } - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), icmp_fraction_result); - PHINode *PN = PHINode::Create(Int1Ty, 3, ""); switch (FcmpInst->getPredicate()) { @@ -849,7 +986,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { /* unequal exponents cannot be equal values, too */ PN->addIncoming(ConstantInt::get(Int1Ty, 0), signequal_bb); /* fractions comparison */ - PN->addIncoming(icmp_fraction_result, middle_bb); + PN->addIncoming(icmp_fraction_result, middle2_bb); break; case CmpInst::FCMP_ONE: case CmpInst::FCMP_UNE: @@ -857,25 +994,25 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { /* goto true branch */ PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); /* unequal exponents are unequal values, too */ - PN->addIncoming(ConstantInt::get(Int1Ty, 1), signequal_bb); + PN->addIncoming(icmp_exponent_result, signequal_bb); /* fractions comparison */ - PN->addIncoming(icmp_fraction_result, middle_bb); + PN->addIncoming(icmp_fraction_result, middle2_bb); break; case CmpInst::FCMP_OGT: case CmpInst::FCMP_UGT: /* if op1 is negative goto true branch, else go on comparing */ PN->addIncoming(t_s1, bb); - PN->addIncoming(icmp_exponent_result, signequal_bb); - PN->addIncoming(icmp_fraction_result, middle_bb); + PN->addIncoming(icmp_exponent_result, signequal2_bb); + PN->addIncoming(PN2, middle2_bb); break; case CmpInst::FCMP_OLT: case CmpInst::FCMP_ULT: /* if op0 is negative goto true branch, else go on comparing */ PN->addIncoming(t_s0, bb); - PN->addIncoming(icmp_exponent_result, signequal_bb); - PN->addIncoming(icmp_fraction_result, middle_bb); + PN->addIncoming(icmp_exponent_result, signequal2_bb); + PN->addIncoming(PN2, middle2_bb); break; default: continue; @@ -1107,7 +1244,8 @@ size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) { bool SplitComparesTransform::runOnModule(Module &M) { - int bitw = 64; + int bitw = 64; + size_t count; char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); @@ -1115,30 +1253,44 @@ bool SplitComparesTransform::runOnModule(Module &M) { enableFPSplit = getenv("AFL_LLVM_LAF_SPLIT_FLOATS") != NULL; - simplifyCompares(M); - - simplifyIntSignedness(M); - if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) { errs() << "Split-compare-pass by laf.intel@gmail.com, extended by " "heiko@hexco.de\n"; - if (enableFPSplit) - errs() << "Split-floatingpoint-compare-pass: " << splitFPCompares(M) + } else { + + be_quiet = 1; + + } + + if (enableFPSplit) { + + count = splitFPCompares(M); + + if (!be_quiet) { + + errs() << "Split-floatingpoint-compare-pass: " << count << " FP comparisons splitted\n"; - } else + } - be_quiet = 1; + simplifyFPCompares(M); + + } + + simplifyCompares(M); + + simplifyIntSignedness(M); switch (bitw) { case 64: + count = splitIntCompares(M, bitw); if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw - << "bit: " << splitIntCompares(M, bitw) << " splitted\n"; + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " splitted\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ @@ -1146,9 +1298,10 @@ bool SplitComparesTransform::runOnModule(Module &M) { [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ #endif case 32: + count = splitIntCompares(M, bitw); if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw - << "bit: " << splitIntCompares(M, bitw) << " splitted\n"; + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " splitted\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ @@ -1156,9 +1309,10 @@ bool SplitComparesTransform::runOnModule(Module &M) { [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ #endif case 16: + count = splitIntCompares(M, bitw); if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw - << "bit: " << splitIntCompares(M, bitw) << " splitted\n"; + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " splitted\n"; bitw >>= 1; break; diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc index 44075c94..f025df77 100644 --- a/llvm_mode/split-switches-pass.so.cc +++ b/llvm_mode/split-switches-pass.so.cc @@ -91,9 +91,6 @@ class SplitSwitchesTransform : public ModulePass { typedef std::vector<CaseExpr> CaseVector; - protected: - int be_quiet = 0; - private: bool splitSwitches(Module &M); bool transformCmps(Module &M, const bool processStrcmp, |