aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhexcoder- <heiko@hexco.de>2020-05-07 18:33:38 +0200
committerhexcoder- <heiko@hexco.de>2020-05-07 18:33:38 +0200
commitd217c7df055b9ca44e5398d8c7d50d43e0b2e56d (patch)
tree39b381389f65c029a6bdb6dae9bba9c9ec4160e6
parent9484da57ed3f421ac274ac51282dba779994da9a (diff)
parentef2ccc8117bb899616472e2d95525ae0ca1a2098 (diff)
downloadafl++-d217c7df055b9ca44e5398d8c7d50d43e0b2e56d.tar.gz
Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev
-rw-r--r--GNUmakefile50
-rw-r--r--README.md4
-rw-r--r--docs/Changelog.md5
-rw-r--r--docs/env_variables.md19
-rw-r--r--examples/afl_untracer/Makefile10
-rw-r--r--examples/afl_untracer/README.md47
-rw-r--r--examples/afl_untracer/afl-untracer.c85
-rw-r--r--examples/afl_untracer/ida_get_patchpoints.py3
-rw-r--r--llvm_mode/GNUmakefile9
-rw-r--r--llvm_mode/LLVMInsTrim.so.cc5
-rw-r--r--llvm_mode/README.lto.md10
-rw-r--r--llvm_mode/afl-clang-fast.c98
-rw-r--r--llvm_mode/afl-llvm-common.cc17
-rw-r--r--llvm_mode/afl-llvm-common.h9
-rw-r--r--llvm_mode/afl-llvm-lto-instrim.so.cc934
-rw-r--r--llvm_mode/afl-llvm-lto-instrumentation.so.cc75
-rw-r--r--llvm_mode/afl-llvm-pass.so.cc17
-rwxr-xr-xqemu_mode/build_qemu_support.sh3
-rw-r--r--src/afl-common.c18
-rw-r--r--src/afl-fuzz-run.c24
-rw-r--r--src/afl-fuzz.c11
-rw-r--r--unicorn_mode/UNICORNAFL_VERSION2
m---------unicorn_mode/unicornafl0
23 files changed, 1295 insertions, 160 deletions
diff --git a/GNUmakefile b/GNUmakefile
index df1434a0..785aacd8 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -52,12 +52,28 @@ endif
ifneq "$(shell uname)" "Darwin"
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
- CFLAGS_OPT = -march=native
+ CFLAGS_OPT += -march=native
endif
# OS X does not like _FORTIFY_SOURCE=2
CFLAGS_OPT += -D_FORTIFY_SOURCE=2
endif
+ifdef STATIC
+ $(info Compiling static version of binaries)
+ # Disable python for static compilation to simplify things
+ PYTHON_OK=0
+ PYFLAGS=
+
+ CFLAGS_OPT += -static
+ LDFLAGS += -lm -lpthread -lz -lutil
+endif
+
+ifdef PROFILING
+ $(info Compiling with profiling information, for analysis: gprof ./afl-fuzz gmon.out > prof.txt)
+ CFLAGS_OPT += -pg -DPROFILING=1
+ LDFLAGS += -pg
+endif
+
ifneq "$(shell uname -m)" "x86_64"
ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386"
ifneq "$(shell uname -m)" "amd64"
@@ -142,23 +158,23 @@ else
endif
ifneq "$(filter Linux GNU%,$(shell uname))" ""
- LDFLAGS += -ldl
+ LDFLAGS += -ldl
endif
ifneq "$(findstring FreeBSD, $(shell uname))" ""
- CFLAGS += -pthread
- LDFLAGS += -lpthread
+ CFLAGS += -pthread
+ LDFLAGS += -lpthread
endif
ifneq "$(findstring NetBSD, $(shell uname))" ""
- CFLAGS += -pthread
- LDFLAGS += -lpthread
+ CFLAGS += -pthread
+ LDFLAGS += -lpthread
endif
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
- TEST_CC = afl-gcc
+ TEST_CC = afl-gcc
else
- TEST_CC = afl-clang
+ TEST_CC = afl-clang
endif
COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h
@@ -184,18 +200,8 @@ ifeq "$(shell svn proplist . 2>/dev/null && echo 1 || echo 0)" "1"
IN_REPO=1
endif
-ifdef STATIC
- $(info Compiling static version of binaries)
- # Disable python for static compilation to simplify things
- PYTHON_OK=0
- PYFLAGS=
-
- CFLAGS += -static
- LDFLAGS += -lm -lpthread -lz -lutil
-endif
-
ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
-ASAN_LDFLAGS+=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
+ASAN_LDFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
ifdef ASAN_BUILD
$(info Compiling ASAN version of binaries)
@@ -203,12 +209,6 @@ ifdef ASAN_BUILD
LDFLAGS+=$(ASAN_LDFLAGS)
endif
-ifdef PROFILING
- $(info Compiling profiling version of binaries)
- CFLAGS+=-pg
- LDFLAGS+=-pg
-endif
-
ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1"
SHMAT_OK=1
else
diff --git a/README.md b/README.md
index 222afab7..04ead78e 100644
--- a/README.md
+++ b/README.md
@@ -85,7 +85,7 @@
(3) partially via AFL_CODE_START/AFL_CODE_END
- (4) Only for LLVM >= 9 and not all targets compile
+ (4) Only for LLVM >= 11 and not all targets compile
(5) upcoming, development in the branch
@@ -137,7 +137,7 @@ afl++ has many build options.
The easiest is to build and install everything:
```shell
-$ sudo apt install build-essential libtool-bin python3 automake bison libglib2.0-dev libpixman-1-dev clang python-setuptools llvm
+$ sudo apt install build-essential libtool-bin python3 automake flex bison libglib2.0-dev libpixman-1-dev clang python-setuptools llvm
$ make distrib
$ sudo make install
```
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 54564a5d..8bcc8949 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -16,9 +16,12 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
- an old, old bug in afl that would show negative stability in rare
circumstances is now hopefully fixed
- llvm_mode:
+ - afl-clang-fast/lto now do not skip single block functions. This
+ behaviour can be reactivated with AFL_LLVM_SKIPSINGLEBLOCK
- if LLVM 11 is installed the posix shm_open+mmap is used and a fixed
address for the shared memory map is used as this increases the
fuzzing speed
+ - InsTrim now has an LTO version! :-) That is the best and fastest mode!
- fixes to LTO mode if instrumented edges > MAP_SIZE
- CTX and NGRAM can now be used together
- CTX and NGRAM are now also supported in CFG/INSTRIM mode
@@ -38,7 +41,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
network (not fuzzing tcp/ip services but running afl-fuzz on one system
and the target being on an embedded device)
- added examples/afl_untracer which does a binary-only fuzzing with the
- modifications done in memory
+ modifications done in memory (intel32/64 and aarch64 support)
- added examples/afl_proxy which can be easily used to fuzz and instrument
non-standard things
- all:
diff --git a/docs/env_variables.md b/docs/env_variables.md
index ed81c8a3..36e5a432 100644
--- a/docs/env_variables.md
+++ b/docs/env_variables.md
@@ -83,6 +83,10 @@ tools make fairly broad use of environmental variables:
The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset
of the settings discussed in section #1, with the exception of:
+ - Setting AFL_LLVM_SKIPSINGLEBLOCK=1 will skip instrumenting
+ functions with a single basic block. This is useful for most C and
+ some C++ targets. This works for all instrumentation modes.
+
- AFL_AS, since this toolchain does not directly invoke GNU as.
- TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are
@@ -116,6 +120,9 @@ Then there are a few specific features that are only available in llvm_mode:
afl-clang-lto/afl-clang-lto++ instead of afl-clang-fast, but is only
built if LLVM 11 or newer is used.
+ - AFL_LLVM_INSTRUMENT=CFG will use Control Flow Graph instrumentation.
+ (recommended)
+
- 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
@@ -139,7 +146,13 @@ Then there are a few specific features that are only available in llvm_mode:
### INSTRIM
- This feature increases the speed by ~15% without any disadvantages.
+ This feature increases the speed by ~15% without any disadvantages to the
+ classic instrumentation.
+
+ Note that there is also an LTO version (if you have llvm 11 or higher) -
+ that is the best instrumentation we have. Use `afl-clang-lto` to activate.
+ The InsTrim LTO version additionally has all the options and features of
+ LTO (see above).
- Setting AFL_LLVM_INSTRIM or AFL_LLVM_INSTRUMENT=CFG to activates this mode
@@ -147,10 +160,6 @@ Then there are a few specific features that are only available in llvm_mode:
afl-fuzz will only be able to see the path the loop took, but not how
many times it was called (unless it is a complex loop).
- - Setting AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK=1 will skip instrumenting
- functions with a single basic block. This is useful for most C and
- some C++ targets.
-
See llvm_mode/README.instrim.md
### NGRAM
diff --git a/examples/afl_untracer/Makefile b/examples/afl_untracer/Makefile
index 5c525877..14a09b41 100644
--- a/examples/afl_untracer/Makefile
+++ b/examples/afl_untracer/Makefile
@@ -1,10 +1,16 @@
+ifdef DEBUG
+ OPT=-O0
+else
+ OPT=-O3
+endif
+
all: afl-untracer libtestinstr.so
afl-untracer: afl-untracer.c
- $(CC) -I../../include -g -o afl-untracer afl-untracer.c -ldl -pthread
+ $(CC) $(OPT) -I../../include -g -o afl-untracer afl-untracer.c -ldl
libtestinstr.so: libtestinstr.c
- $(CC) -fPIC -o libtestinstr.so -shared libtestinstr.c
+ $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c
clean:
rm -f afl-untracer libtestinstr.so *~ core
diff --git a/examples/afl_untracer/README.md b/examples/afl_untracer/README.md
index 4ff96423..29234889 100644
--- a/examples/afl_untracer/README.md
+++ b/examples/afl_untracer/README.md
@@ -1,19 +1,58 @@
-# afl-untracer
+# afl-untracer - fast fuzzing of binary-only libraries
+
+## Introduction
afl-untracer is an example skeleton file which can easily be used to fuzz
a closed source library.
-It requires less memory than qemu_mode however it is way
+It requires less memory and is x3-5 faster than qemu_mode however it is way
more course grained and does not provide interesting features like compcov
or cmplog.
-Read and modify afl-untracer.c then `make` and use it as the afl-fuzz target
-(or even remote via afl-network-proxy).
+Supported is so far Intel (i386/x86_64) and AARCH64.
+
+## How-to
+
+### Modify afl-untracer.c
+
+Read and modify afl-untracer.c then `make`.
+To adapt afl-untracer.c to your need read the header of the file and then
+search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations.
+
+### Generate patches.txt file
To generate the `patches.txt` file for your target library use the
`ida_get_patchpoints.py` script for IDA Pro or
`ghidra_get_patchpoints.java` for Ghidra.
+The patches.txt file has to pointed to by `AFL_UNTRACER_FILE`.
+
+To easily run the scripts without needing to run the GUI with Ghidra:
+```
+$ /opt/ghidra/support/analyzeHeadless /tmp/ tmp$$ -import libtestinstr.so -postscript ./ghidra_get_patchpoints.java
+$ rm -rf /tmp/tmp$$
+```
+
+### Fuzzing
+
+Example (after modifying afl-untracer.c to your needs, compiling and creating
+patches.txt):
+```
+AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer
+```
+(or even remote via afl-network-proxy).
+
+### Testing and debugging
+
+For testing/debugging you can try:
+```
+make DEBUG=1
+AFL_UNTRACER_FILE=./patches.txt AFL_DEBUG=1 gdb ./afl-untracer
+```
+and then you can easily set breakpoints to "breakpoint" and "fuzz".
+
+# Background
+
This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL)
and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz).
This implementation is slower because the traps are not patched out with each
diff --git a/examples/afl_untracer/afl-untracer.c b/examples/afl_untracer/afl-untracer.c
index d319b530..5dbc71bf 100644
--- a/examples/afl_untracer/afl-untracer.c
+++ b/examples/afl_untracer/afl-untracer.c
@@ -156,7 +156,7 @@ void read_library_information() {
liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16);
if (debug)
fprintf(
- stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name,
+ stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name,
liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start,
liblist[liblist_cnt].addr_start,
liblist[liblist_cnt].addr_end - 1);
@@ -276,6 +276,18 @@ library_list_t *find_library(char *name) {
}
+/* for having an easy breakpoint after load the shared library */
+// this seems to work for clang too. nice :) requires gcc 4.4+
+#pragma GCC push_options
+#pragma GCC optimize("O0")
+void breakpoint() {
+
+ if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n");
+
+}
+
+#pragma GCC pop_options
+
/* Error reporting to forkserver controller */
void send_forkserver_error(int error) {
@@ -433,10 +445,17 @@ static void __afl_end_testcase(int status) {
}
+#ifdef __aarch64__
+#define SHADOW(addr) \
+ ((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \
+ MEMORY_MAP_DECREMENT - \
+ ((uintptr_t)addr & 0x7) * 0x10000000000))
+#else
#define SHADOW(addr) \
((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \
MEMORY_MAP_DECREMENT - \
((uintptr_t)addr & 0x3) * 0x10000000000))
+#endif
void setup_trap_instrumentation() {
@@ -452,8 +471,12 @@ void setup_trap_instrumentation() {
FILE *patches = fopen(filename, "r");
if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename);
- // Index into the coverage bitmap for the current trap instruction.
- int bitmap_index = 0;
+ // Index into the coverage bitmap for the current trap instruction.
+#ifdef __aarch64__
+ uint64_t bitmap_index = 0;
+#else
+ uint32_t bitmap_index = 0;
+#endif
while ((nread = getline(&line, &len, patches)) != -1) {
@@ -485,9 +508,15 @@ void setup_trap_instrumentation() {
PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
FATAL("Failed to mprotect library %s writable", line);
- // Create shadow memory.
+ // Create shadow memory.
+#ifdef __aarch64__
+ for (int i = 0; i < 8; i++) {
+
+#else
for (int i = 0; i < 4; i++) {
+#endif
+
void *shadow_addr = SHADOW(lib_addr + i);
void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0);
@@ -513,12 +542,18 @@ void setup_trap_instrumentation() {
if (bitmap_index >= __afl_map_size)
FATAL("Too many basic blocks to instrument");
- uint32_t *shadow = SHADOW(lib_addr + offset);
+#ifdef __arch64__
+ uint64_t
+#else
+ uint32_t
+#endif
+ *shadow = SHADOW(lib_addr + offset);
if (*shadow != 0) continue; // skip duplicates
// Make lookup entry in shadow memory.
-#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__))
+#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \
+ defined(__i386__))
// this is for Intel x64
@@ -531,15 +566,29 @@ void setup_trap_instrumentation() {
lib_addr, offset, lib_addr + offset, orig_byte, shadow,
bitmap_index, *shadow);
+#elif defined(__aarch64__)
+
+ // this is for aarch64
+
+ uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset);
+ uint32_t orig_bytes = *patch_bytes;
+ *shadow = (bitmap_index << 32) | orig_bytes;
+ *patch_bytes = 0xd4200000; // replace instruction with debug trap
+ if (debug)
+ fprintf(stderr,
+ "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n",
+ lib_addr, offset, lib_addr + offset, orig_bytes, shadow,
+ bitmap_index, *shadow);
+
#else
- // this will be ARM and AARCH64
- // for ARM we will need to identify if the code is in thumb or ARM
-#error "non x86_64 not supported yet"
- //__arm__:
- // linux thumb: 0xde01
- // linux arm: 0xe7f001f0
- //__aarch64__:
- // linux aarch64: 0xd4200000
+ // this will be ARM and AARCH64
+ // for ARM we will need to identify if the code is in thumb or ARM
+#error "non x86_64/aarch64 not supported yet"
+ //__arm__:
+ // linux thumb: 0xde01
+ // linux arm: 0xe7f001f0
+ //__aarch64__:
+ // linux aarch64: 0xd4200000
#endif
bitmap_index++;
@@ -573,8 +622,15 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
ctx->uc_mcontext->__ss.__rip -= 1;
addr = ctx->uc_mcontext->__ss.__rip;
#elif defined(__linux__)
+#if defined(__x86_64__) || defined(__i386__)
ctx->uc_mcontext.gregs[REG_RIP] -= 1;
addr = ctx->uc_mcontext.gregs[REG_RIP];
+#elif defined(__aarch64__)
+ ctx->uc_mcontext.pc -= 4;
+ addr = ctx->uc_mcontext.pc;
+#else
+#error "Unsupported processor"
+#endif
#elif defined(__FreeBSD__) && defined(__LP64__)
ctx->uc_mcontext.mc_rip -= 1;
addr = ctx->uc_mcontext.mc_rip;
@@ -642,6 +698,7 @@ int main(int argc, char *argv[]) {
// END STEP 2
/* setup instrumentation, shared memory and forkserver */
+ breakpoint();
read_library_information();
setup_trap_instrumentation();
__afl_map_shm();
diff --git a/examples/afl_untracer/ida_get_patchpoints.py b/examples/afl_untracer/ida_get_patchpoints.py
index c7e8f899..43cf6d89 100644
--- a/examples/afl_untracer/ida_get_patchpoints.py
+++ b/examples/afl_untracer/ida_get_patchpoints.py
@@ -57,3 +57,6 @@ with open(home + "/Desktop/patches.txt", "w") as f:
f.write('\n')
print("Done, found {} patchpoints".format(len(patchpoints)))
+
+# For headless script running remove the comment from the next line
+#ida_pro.qexit()
diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile
index 93886e47..6a58f6a5 100644
--- a/llvm_mode/GNUmakefile
+++ b/llvm_mode/GNUmakefile
@@ -238,7 +238,7 @@ ifeq "$(TEST_MMAP)" "1"
LDFLAGS += -Wno-deprecated-declarations
endif
- PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-lto-whitelist.so ../afl-llvm-lto-instrumentation.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so
+ PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-lto-whitelist.so ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so
# If prerequisites are not given, warn, do not build anything, and exit with code 0
ifeq "$(LLVMVER)" ""
@@ -330,6 +330,11 @@ ifeq "$(LLVM_LTO)" "1"
@$(CLANG_BIN) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
endif
+../afl-llvm-lto-instrim.so: afl-llvm-lto-instrim.so.cc afl-llvm-common.o
+ifeq "$(LLVM_LTO)" "1"
+ $(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o
+endif
+
# laf
../split-switches-pass.so: split-switches-pass.so.cc afl-llvm-common.o | test_deps
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
@@ -373,7 +378,7 @@ all_done: test_build
install: all
install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
- if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi
+ if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f ../afl-llvm-rt-32.o ]; then set -e; install -m 755 ../afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi
if [ -f ../afl-llvm-rt-64.o ]; then set -e; install -m 755 ../afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi
if [ -f ../compare-transform-pass.so ]; then set -e; install -m 755 ../compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi
diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc
index ad046a8b..ed6c79e8 100644
--- a/llvm_mode/LLVMInsTrim.so.cc
+++ b/llvm_mode/LLVMInsTrim.so.cc
@@ -134,7 +134,8 @@ struct InsTrim : public ModulePass {
}
- if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") != NULL)
+ if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
+ getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
function_minimum_size = 2;
unsigned PrevLocSize = 0;
@@ -394,7 +395,7 @@ struct InsTrim : public ModulePass {
if ((callInst = dyn_cast<CallInst>(&IN))) {
Function *Callee = callInst->getCalledFunction();
- if (!Callee || Callee->size() < 2)
+ if (!Callee || Callee->size() < function_minimum_size)
continue;
else {
diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md
index d8e4766d..110b5e9c 100644
--- a/llvm_mode/README.lto.md
+++ b/llvm_mode/README.lto.md
@@ -6,6 +6,8 @@ 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
+ 1a. Set AFL_LLVM_INSTRUMENT=CFG if you want the InsTrimLTO version
+ (recommended)
2. You can use it together with llvm_mode: laf-intel and whitelisting
features and can be combined with cmplog/Redqueen
@@ -41,7 +43,7 @@ and many dead ends until we got to this:
-fsanitize=coverage edge coverage mode :)
The result:
- * 10-20% speed gain compared to llvm_mode
+ * 10-25% speed gain compared to llvm_mode
* guaranteed non-colliding edge coverage :-)
* The compile time especially for libraries can be longer
@@ -80,11 +82,13 @@ Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc.
Also whitelisting (AFL_LLVM_WHITELIST -> [README.whitelist.md](README.whitelist.md)) and
laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work.
-Instrim does not - but we can not really use it anyway for our approach.
+InsTrim (control flow graph instrumentation) is supported and recommended!
+ (set `AFL_LLVM_INSTRUMENT=CFG`)
Example:
```
-CC=afl-clang-lto CXX=afl-clang-lto++ ./configure
+CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure
+export AFL_LLVM_INSTRUMENT=CFG
make
```
diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c
index 2d1b427c..42b02bdd 100644
--- a/llvm_mode/afl-clang-fast.c
+++ b/llvm_mode/afl-clang-fast.c
@@ -43,7 +43,7 @@ static u8 * obj_path; /* Path to runtime libraries */
static u8 **cc_params; /* Parameters passed to the real CC */
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
static u8 llvm_fullpath[PATH_MAX];
-static u8 instrument_mode, instrument_opt_mode, ngram_size;
+static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode;
static u8 * lto_flag = AFL_CLANG_FLTO;
static u8 * march_opt = CFLAGS_OPT;
static u8 debug;
@@ -170,7 +170,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
else
++name;
- if (instrument_mode == INSTRUMENT_LTO)
+ if (lto_mode)
if (lto_flag[0] != '-')
FATAL(
"Using afl-clang-lto is not possible because Makefile magic did not "
@@ -227,8 +227,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (getenv("LAF_TRANSFORM_COMPARES") ||
getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
- if (!be_quiet && getenv("AFL_LLVM_LTO_AUTODICTIONARY") &&
- instrument_mode != INSTRUMENT_LTO)
+ if (!be_quiet && getenv("AFL_LLVM_LTO_AUTODICTIONARY") && lto_mode)
WARNF(
"using AFL_LLVM_LAF_TRANSFORM_COMPARES together with "
"AFL_LLVM_LTO_AUTODICTIONARY makes no sense. Use only "
@@ -281,7 +280,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
}
- if (instrument_mode == INSTRUMENT_LTO) {
+ if (lto_mode) {
if (getenv("AFL_LLVM_WHITELIST") != NULL) {
@@ -295,8 +294,12 @@ 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";
- cc_params[cc_par_cnt++] = alloc_printf(
- "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path);
+ 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 {
@@ -391,7 +394,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (getenv("AFL_USE_CFISAN")) {
- if (instrument_mode != INSTRUMENT_LTO) {
+ if (!lto_mode) {
uint32_t i = 0, found = 0;
while (envp[i] != NULL && !found)
@@ -417,9 +420,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
getenv("LAF_TRANSFORM_COMPARES") ||
- (instrument_mode == INSTRUMENT_LTO &&
- (getenv("AFL_LLVM_LTO_AUTODICTIONARY") ||
- getenv("AFL_LLVM_AUTODICTIONARY")))) {
+ (lto_mode && (getenv("AFL_LLVM_LTO_AUTODICTIONARY") ||
+ getenv("AFL_LLVM_AUTODICTIONARY")))) {
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
@@ -500,7 +502,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
case 0:
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path);
- if (instrument_mode == INSTRUMENT_LTO)
+ if (lto_mode)
cc_params[cc_par_cnt++] =
alloc_printf("%s/afl-llvm-rt-lto.o", obj_path);
break;
@@ -509,7 +511,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path);
if (access(cc_params[cc_par_cnt - 1], R_OK))
FATAL("-m32 is not supported by your compiler");
- if (instrument_mode == INSTRUMENT_LTO) {
+ if (lto_mode) {
cc_params[cc_par_cnt++] =
alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path);
@@ -524,7 +526,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path);
if (access(cc_params[cc_par_cnt - 1], R_OK))
FATAL("-m64 is not supported by your compiler");
- if (instrument_mode == INSTRUMENT_LTO) {
+ if (lto_mode) {
cc_params[cc_par_cnt++] =
alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path);
@@ -548,7 +550,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
int main(int argc, char **argv, char **envp) {
int i;
- char *callname = "afl-clang-fast", *ptr;
+ char *callname = "afl-clang-fast", *ptr = NULL;
if (getenv("AFL_DEBUG")) {
@@ -630,7 +632,13 @@ int main(int argc, char **argv, char **envp) {
if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 ||
strncasecmp(ptr, "instrim", strlen("instrim")) == 0) {
- if (!instrument_mode || instrument_mode == INSTRUMENT_CFG)
+ if (instrument_mode == INSTRUMENT_LTO) {
+
+ instrument_mode = INSTRUMENT_CFG;
+ lto_mode = 1;
+
+ } else if (!instrument_mode || instrument_mode == INSTRUMENT_CFG)
+
instrument_mode = INSTRUMENT_CFG;
else
FATAL("main instrumentation mode already set with %s",
@@ -640,9 +648,10 @@ int main(int argc, char **argv, char **envp) {
if (strncasecmp(ptr, "lto", strlen("lto")) == 0) {
+ lto_mode = 1;
if (!instrument_mode || instrument_mode == INSTRUMENT_LTO)
instrument_mode = INSTRUMENT_LTO;
- else
+ else if (instrument_mode != INSTRUMENT_CFG)
FATAL("main instrumentation mode already set with %s",
instrument_mode_string[instrument_mode]);
@@ -684,37 +693,54 @@ int main(int argc, char **argv, char **envp) {
}
- if (!instrument_opt_mode)
- ptr = instrument_mode_string[instrument_mode];
- else if (instrument_opt_mode == INSTRUMENT_OPT_CTX)
- ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]);
- else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM)
- ptr = alloc_printf("%s + NGRAM-%u", instrument_mode_string[instrument_mode],
- ngram_size);
- else
- ptr = alloc_printf("%s + CTX + NGRAM-%u",
- instrument_mode_string[instrument_mode], ngram_size);
-
if (strstr(argv[0], "afl-clang-lto") != NULL) {
- if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO) {
+ if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO ||
+ instrument_mode == INSTRUMENT_CFG) {
+ lto_mode = 1;
callname = "afl-clang-lto";
- instrument_mode = INSTRUMENT_LTO;
- ptr = instrument_mode_string[instrument_mode];
+ if (!instrument_mode) {
+
+ instrument_mode = INSTRUMENT_LTO;
+ ptr = instrument_mode_string[instrument_mode];
+
+ }
} else {
if (!be_quiet)
WARNF("afl-clang-lto called with mode %s, using that mode instead",
- ptr);
+ instrument_mode_string[instrument_mode]);
}
}
+ if (instrument_opt_mode && lto_mode)
+ FATAL(
+ "CTX and NGRAM can not be used in LTO mode (and would make LTO "
+ "useless)");
+
+ if (!instrument_opt_mode) {
+
+ if (lto_mode && instrument_mode == INSTRUMENT_CFG)
+ ptr = alloc_printf("InsTrimLTO");
+ else
+ ptr = instrument_mode_string[instrument_mode];
+
+ } else if (instrument_opt_mode == INSTRUMENT_OPT_CTX)
+
+ ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]);
+ else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM)
+ ptr = alloc_printf("%s + NGRAM-%u", instrument_mode_string[instrument_mode],
+ ngram_size);
+ else
+ ptr = alloc_printf("%s + CTX + NGRAM-%u",
+ instrument_mode_string[instrument_mode], ngram_size);
+
#ifndef AFL_CLANG_FLTO
- if (instrument_mode == INSTRUMENT_LTO)
+ if (lto_mode)
FATAL(
"instrumentation mode LTO specified but LLVM support not available "
"(requires LLVM 11 or higher)");
@@ -733,7 +759,7 @@ int main(int argc, char **argv, char **envp) {
if (argc < 2 || strcmp(argv[1], "-h") == 0) {
- if (instrument_mode != INSTRUMENT_LTO)
+ if (!lto_mode)
printf("afl-clang-fast" VERSION " by <lszekeres@google.com> in %s mode\n",
ptr);
else
@@ -831,7 +857,7 @@ int main(int argc, char **argv, char **envp) {
getenv("AFL_DEBUG") != NULL) {
- if (instrument_mode != INSTRUMENT_LTO)
+ if (!lto_mode)
SAYF(cCYA "afl-clang-fast" VERSION cRST
" by <lszekeres@google.com> in %s mode\n",
@@ -846,7 +872,7 @@ int main(int argc, char **argv, char **envp) {
}
u8 *ptr2;
- if (!be_quiet && instrument_mode != INSTRUMENT_LTO &&
+ if (!be_quiet && !lto_mode &&
((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) {
u32 map_size = atoi(ptr2);
diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc
index 04dd9475..c62b4c14 100644
--- a/llvm_mode/afl-llvm-common.cc
+++ b/llvm_mode/afl-llvm-common.cc
@@ -201,3 +201,20 @@ bool isInWhitelist(llvm::Function *F) {
}
+// Calculate the number of average collisions that would occur if all
+// 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) {
+
+ double bins = MAP_SIZE;
+ double balls = edges;
+ double step1 = 1 - (1 / bins);
+ double step2 = pow(step1, balls);
+ double step3 = bins * step2;
+ double step4 = round(step3);
+ unsigned long long int empty = step4;
+ unsigned long long int collisions = edges - (MAP_SIZE - empty);
+ return collisions;
+
+}
+
diff --git a/llvm_mode/afl-llvm-common.h b/llvm_mode/afl-llvm-common.h
index 5b5e08d0..cf14d2e1 100644
--- a/llvm_mode/afl-llvm-common.h
+++ b/llvm_mode/afl-llvm-common.h
@@ -32,10 +32,11 @@ typedef long double max_align_t;
#include "llvm/Support/CFG.h"
#endif
-char *getBBName(const llvm::BasicBlock *BB);
-bool isBlacklisted(const llvm::Function *F);
-void initWhitelist();
-bool isInWhitelist(llvm::Function *F);
+char * getBBName(const llvm::BasicBlock *BB);
+bool isBlacklisted(const llvm::Function *F);
+void initWhitelist();
+bool isInWhitelist(llvm::Function *F);
+unsigned long long int calculateCollisions(uint32_t edges);
#endif
diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc
new file mode 100644
index 00000000..a686bb81
--- /dev/null
+++ b/llvm_mode/afl-llvm-lto-instrim.so.cc
@@ -0,0 +1,934 @@
+/*
+ american fuzzy lop++ - LLVM-mode instrumentation pass
+ ---------------------------------------------------
+
+ Copyright 2019-2020 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:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ This library is plugged into LLVM when invoking clang through afl-clang-fast.
+
+ */
+
+#define AFL_LLVM_PASS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <unordered_set>
+#include <list>
+#include <string>
+#include <fstream>
+#include <set>
+
+#include "llvm/Config/llvm-config.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/MemorySSAUpdater.h"
+#include "llvm/Analysis/ValueTracking.h"
+
+#include "MarkNodes.h"
+#include "afl-llvm-common.h"
+
+#include "config.h"
+#include "debug.h"
+
+using namespace llvm;
+
+static cl::opt<bool> MarkSetOpt("markset", cl::desc("MarkSet"),
+ cl::init(false));
+static cl::opt<bool> LoopHeadOpt("loophead", cl::desc("LoopHead"),
+ cl::init(false));
+
+namespace {
+
+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;
+ uint64_t map_addr = 0x10000;
+
+ public:
+ static char ID;
+
+ InsTrimLTO() : ModulePass(ID) {
+
+ char *ptr;
+
+ if (getenv("AFL_DEBUG")) debug = 1;
+ if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL)
+ if ((afl_global_id = atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE)
+ FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n",
+ ptr, MAP_SIZE - 1);
+
+ skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
+
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+
+ ModulePass::getAnalysisUsage(AU);
+ AU.addRequired<DominatorTreeWrapperPass>();
+ AU.addRequired<LoopInfoWrapperPass>();
+
+ }
+
+ StringRef getPassName() const override {
+
+ return "InstTrim LTO Instrumentation";
+
+ }
+
+ bool runOnModule(Module &M) override {
+
+ char be_quiet = 0;
+ char *ptr;
+
+ if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
+
+ SAYF(cCYA "InsTrimLTO" VERSION cRST
+ " by csienslab and Marc \"vanHauser\" Heuse\n");
+
+ } else
+
+ be_quiet = 1;
+
+ /* 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"))) {
+
+ uint64_t val;
+ if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) {
+
+ map_addr = 0;
+
+ } else if (map_addr == 0) {
+
+ FATAL(
+ "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used "
+ "together");
+
+ } else if (strncmp(ptr, "0x", 2) != 0) {
+
+ map_addr = 0x10000; // the default
+
+ } else {
+
+ val = strtoull(ptr, NULL, 16);
+ if (val < 0x100 || val > 0xffffffff00000000) {
+
+ FATAL(
+ "AFL_LLVM_MAP_ADDR must be a value between 0x100 and "
+ "0xffffffff00000000");
+
+ }
+
+ map_addr = val;
+
+ }
+
+ }
+
+ if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); }
+
+ if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL ||
+ getenv("LOOPHEAD") != NULL) {
+
+ LoopHeadOpt = true;
+
+ }
+
+ if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
+ getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
+ function_minimum_size = 2;
+
+ // this is our default
+ MarkSetOpt = true;
+
+ /* Initialize LLVM instrumentation */
+
+ LLVMContext & C = M.getContext();
+ std::vector<std::string> dictionary;
+ std::vector<CallInst *> calls;
+ DenseMap<Value *, std::string *> valueMap;
+
+ IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
+ IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
+ IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
+
+ ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
+ ConstantInt *One = ConstantInt::get(Int8Ty, 1);
+
+ /* Get/set globals for the SHM region. */
+
+ GlobalVariable *AFLMapPtr = NULL;
+ Value * MapPtrFixed = NULL;
+
+ if (!map_addr) {
+
+ AFLMapPtr =
+ new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
+ GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
+
+ } else {
+
+ ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
+ MapPtrFixed =
+ ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty));
+
+ }
+
+ if (autodictionary) {
+
+ /* Some implementation notes.
+ *
+ * We try to handle 3 cases:
+ * - memcmp("foo", arg, 3) <- literal string
+ * - static char globalvar[] = "foo";
+ * memcmp(globalvar, arg, 3) <- global variable
+ * - char localvar[] = "foo";
+ * memcmp(locallvar, arg, 3) <- local variable
+ *
+ * The local variable case is the hardest. We can only detect that
+ * case if there is no reassignment or change in the variable.
+ * And it might not work across llvm version.
+ * What we do is hooking the initializer function for local variables
+ * (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned
+ * variable. And if that variable is then used in a compare function
+ * we use that noted string.
+ * This seems not to work for tokens that have a size <= 4 :-(
+ *
+ * - if the compared length is smaller than the string length we
+ * save the full string. This is likely better for fuzzing but
+ * might be wrong in a few cases depending on optimizers
+ *
+ * - not using StringRef because there is a bug in the llvm 11
+ * checkout I am using which sometimes points to wrong strings
+ *
+ * Over and out. Took me a full day. damn. mh/vh
+ */
+
+ for (Function &F : M) {
+
+ for (auto &BB : F) {
+
+ for (auto &IN : BB) {
+
+ CallInst *callInst = nullptr;
+
+ if ((callInst = dyn_cast<CallInst>(&IN))) {
+
+ bool isStrcmp = true;
+ bool isMemcmp = true;
+ bool isStrncmp = true;
+ bool isStrcasecmp = true;
+ bool isStrncasecmp = true;
+ bool isIntMemcpy = true;
+ bool addedNull = false;
+ uint8_t optLen = 0;
+
+ Function *Callee = callInst->getCalledFunction();
+ if (!Callee) continue;
+ if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
+ std::string FuncName = Callee->getName().str();
+ isStrcmp &= !FuncName.compare("strcmp");
+ isMemcmp &= !FuncName.compare("memcmp");
+ isStrncmp &= !FuncName.compare("strncmp");
+ isStrcasecmp &= !FuncName.compare("strcasecmp");
+ isStrncasecmp &= !FuncName.compare("strncasecmp");
+ isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64");
+
+ if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
+ !isStrncasecmp && !isIntMemcpy)
+ continue;
+
+ /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp
+ * function 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());
+ 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();
+
+ if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
+ !isStrncasecmp && !isIntMemcpy)
+ continue;
+
+ /* is a str{n,}{case,}cmp/memcmp, check if we have
+ * str{case,}cmp(x, "const") or str{case,}cmp("const", x)
+ * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x,
+ * ..) memcmp(x, "const", ..) or memcmp("const", x, ..) */
+ Value *Str1P = callInst->getArgOperand(0),
+ *Str2P = callInst->getArgOperand(1);
+ std::string Str1, Str2;
+ StringRef TmpStr;
+ bool HasStr1 = getConstantStringInfo(Str1P, TmpStr);
+ if (TmpStr.empty())
+ HasStr1 = false;
+ else
+ Str1 = TmpStr.str();
+ bool HasStr2 = getConstantStringInfo(Str2P, TmpStr);
+ if (TmpStr.empty())
+ HasStr2 = false;
+ else
+ Str2 = TmpStr.str();
+
+ if (debug)
+ fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n",
+ FuncName.c_str(), Str1P, Str1P->getName().str().c_str(),
+ Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P,
+ Str2P->getName().str().c_str(), Str2.c_str(),
+ HasStr2 == true ? "true" : "false");
+
+ // we handle the 2nd parameter first because of llvm memcpy
+ if (!HasStr2) {
+
+ auto *Ptr = dyn_cast<ConstantExpr>(Str2P);
+ if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
+
+ if (auto *Var =
+ dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
+
+ if (Var->hasInitializer()) {
+
+ if (auto *Array = dyn_cast<ConstantDataArray>(
+ Var->getInitializer())) {
+
+ HasStr2 = true;
+ Str2 = Array->getAsString().str();
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ // for the internal memcpy routine we only care for the second
+ // parameter and are not reporting anything.
+ if (isIntMemcpy == true) {
+
+ if (HasStr2 == true) {
+
+ Value * op2 = callInst->getArgOperand(2);
+ ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
+ if (ilen) {
+
+ uint64_t literalLength = Str2.size();
+ uint64_t optLength = ilen->getZExtValue();
+ if (literalLength + 1 == optLength) {
+
+ Str2.append("\0", 1); // add null byte
+ addedNull = true;
+
+ }
+
+ }
+
+ valueMap[Str1P] = new std::string(Str2);
+
+ if (debug)
+ fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P);
+ continue;
+
+ }
+
+ continue;
+
+ }
+
+ // Neither a literal nor a global variable?
+ // maybe it is a local variable that we saved
+ if (!HasStr2) {
+
+ std::string *strng = valueMap[Str2P];
+ if (strng && !strng->empty()) {
+
+ Str2 = *strng;
+ HasStr2 = true;
+ if (debug)
+ fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(),
+ Str2P);
+
+ }
+
+ }
+
+ if (!HasStr1) {
+
+ auto Ptr = dyn_cast<ConstantExpr>(Str1P);
+
+ if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
+
+ if (auto *Var =
+ dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
+
+ if (Var->hasInitializer()) {
+
+ if (auto *Array = dyn_cast<ConstantDataArray>(
+ Var->getInitializer())) {
+
+ HasStr1 = true;
+ Str1 = Array->getAsString().str();
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ // Neither a literal nor a global variable?
+ // maybe it is a local variable that we saved
+ if (!HasStr1) {
+
+ std::string *strng = valueMap[Str1P];
+ if (strng && !strng->empty()) {
+
+ Str1 = *strng;
+ HasStr1 = true;
+ if (debug)
+ fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(),
+ Str1P);
+
+ }
+
+ }
+
+ /* handle cases of one string is const, one string is variable */
+ if (!(HasStr1 ^ HasStr2)) continue;
+
+ std::string thestring;
+
+ if (HasStr1)
+ thestring = Str1;
+ else
+ thestring = Str2;
+
+ optLen = thestring.length();
+
+ if (isMemcmp || isStrncmp || isStrncasecmp) {
+
+ Value * op2 = callInst->getArgOperand(2);
+ ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
+ if (ilen) {
+
+ uint64_t literalLength = optLen;
+ optLen = ilen->getZExtValue();
+ if (literalLength + 1 == optLen) { // add null byte
+ thestring.append("\0", 1);
+ addedNull = true;
+
+ }
+
+ }
+
+ }
+
+ // add null byte if this is a string compare function and a null
+ // was not already added
+ if (addedNull == false && !isMemcmp) {
+
+ thestring.append("\0", 1); // add null byte
+ optLen++;
+
+ }
+
+ if (!be_quiet) {
+
+ std::string outstring;
+ fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen,
+ (unsigned int)thestring.length());
+ for (uint8_t i = 0; i < thestring.length(); i++) {
+
+ uint8_t c = thestring[i];
+ if (c <= 32 || c >= 127)
+ fprintf(stderr, "\\x%02x", c);
+ else
+ fprintf(stderr, "%c", c);
+
+ }
+
+ fprintf(stderr, "\"\n");
+
+ }
+
+ // we take the longer string, even if the compare was to a
+ // shorter part. Note that depending on the optimizer of the
+ // compiler this can be wrong, but it is more likely that this
+ // is helping the fuzzer
+ if (optLen != thestring.length()) optLen = thestring.length();
+ if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA;
+ if (optLen < MIN_AUTO_EXTRA) // too short? skip
+ continue;
+
+ dictionary.push_back(thestring.substr(0, optLen));
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /* InsTrim instrumentation starts here */
+
+ u64 total_rs = 0;
+ u64 total_hs = 0;
+
+ for (Function &F : M) {
+
+ if (debug) {
+
+ uint32_t bb_cnt = 0;
+
+ for (auto &BB : F)
+ if (BB.size() > 0) ++bb_cnt;
+ SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n",
+ F.getName().str().c_str(), F.size(), bb_cnt);
+
+ }
+
+ // if the function below our minimum size skip it (1 or 2)
+ if (F.size() < function_minimum_size) continue;
+ if (isBlacklisted(&F)) continue;
+
+ std::unordered_set<BasicBlock *> MS;
+ if (!MarkSetOpt) {
+
+ for (auto &BB : F) {
+
+ MS.insert(&BB);
+
+ }
+
+ total_rs += F.size();
+
+ } else {
+
+ auto Result = markNodes(&F);
+ auto RS = Result.first;
+ auto HS = Result.second;
+
+ MS.insert(RS.begin(), RS.end());
+ if (!LoopHeadOpt) {
+
+ MS.insert(HS.begin(), HS.end());
+ total_rs += MS.size();
+
+ } else {
+
+ DenseSet<std::pair<BasicBlock *, BasicBlock *>> EdgeSet;
+ DominatorTreeWrapperPass * DTWP =
+ &getAnalysis<DominatorTreeWrapperPass>(F);
+ auto DT = &DTWP->getDomTree();
+
+ total_rs += RS.size();
+ total_hs += HS.size();
+
+ for (BasicBlock *BB : HS) {
+
+ bool Inserted = false;
+ for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) {
+
+ auto Edge = BasicBlockEdge(*BI, BB);
+ if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) {
+
+ EdgeSet.insert({*BI, BB});
+ Inserted = true;
+ break;
+
+ }
+
+ }
+
+ if (!Inserted) {
+
+ MS.insert(BB);
+ total_rs += 1;
+ total_hs -= 1;
+
+ }
+
+ }
+
+ for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) {
+
+ auto PredBB = I->first;
+ auto SuccBB = I->second;
+ auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT,
+ nullptr, nullptr, false);
+ MS.insert(NewBB);
+
+ }
+
+ }
+
+ }
+
+ for (BasicBlock &BB : F) {
+
+ auto PI = pred_begin(&BB);
+ auto PE = pred_end(&BB);
+ IRBuilder<> IRB(&*BB.getFirstInsertionPt());
+ Value * L = NULL;
+
+ if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; }
+
+ if (PI == PE) {
+
+ L = ConstantInt::get(Int32Ty, afl_global_id++);
+
+ } else {
+
+ auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin());
+ DenseMap<BasicBlock *, unsigned> PredMap;
+ for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) {
+
+ BasicBlock *PBB = *PI;
+ auto It = PredMap.insert({PBB, afl_global_id++});
+ unsigned Label = It.first->second;
+ PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
+
+ }
+
+ L = PN;
+
+ }
+
+ /* Load SHM pointer */
+ Value *MapPtrIdx;
+
+ if (map_addr) {
+
+ MapPtrIdx = IRB.CreateGEP(MapPtrFixed, L);
+
+ } else {
+
+ LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
+ MapPtr->setMetadata(M.getMDKindID("nosanitize"),
+ MDNode::get(C, None));
+ MapPtrIdx = IRB.CreateGEP(MapPtr, L);
+
+ }
+
+ /* Update bitmap */
+ LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
+ Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+
+ Value *Incr = IRB.CreateAdd(Counter, One);
+
+ if (skip_nozero) {
+
+ auto cf = IRB.CreateICmpEQ(Incr, Zero);
+ auto carry = IRB.CreateZExt(cf, Int8Ty);
+ Incr = IRB.CreateAdd(Incr, carry);
+
+ }
+
+ IRB.CreateStore(Incr, MapPtrIdx)
+ ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
+
+ // done :)
+
+ inst_blocks++;
+
+ }
+
+ }
+
+ // save highest location ID to global variable
+ // do this after each function to fail faster
+ if (!be_quiet && afl_global_id > MAP_SIZE &&
+ afl_global_id > FS_OPT_MAX_MAPSIZE) {
+
+ uint32_t pow2map = 1, map = afl_global_id;
+ while ((map = map >> 1))
+ pow2map++;
+ WARNF(
+ "We have %u blocks to instrument but the map size is only %u. Either "
+ "edit config.h and set MAP_SIZE_POW2 from %u to %u, then recompile "
+ "afl-fuzz and llvm_mode and then make this target - or set "
+ "AFL_MAP_SIZE with at least size %u when running afl-fuzz with this "
+ "target.",
+ afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id);
+
+ }
+
+ if (!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
+
+ Function *f = M.getFunction("__afl_auto_init_globals");
+
+ if (!f) {
+
+ fprintf(stderr,
+ "Error: init function could not be found (this should not "
+ "happen)\n");
+ exit(-1);
+
+ }
+
+ BasicBlock *bb = &f->getEntryBlock();
+ if (!bb) {
+
+ fprintf(stderr,
+ "Error: init function does not have an EntryBlock (this should "
+ "not happen)\n");
+ exit(-1);
+
+ }
+
+ BasicBlock::iterator IP = bb->getFirstInsertionPt();
+ IRBuilder<> IRB(&(*IP));
+
+ if (map_addr) {
+
+ GlobalVariable *AFLMapAddrFixed =
+ new GlobalVariable(M, Int64Ty, true, GlobalValue::ExternalLinkage,
+ 0, "__afl_map_addr");
+ ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
+ StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed);
+ StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"),
+ MDNode::get(C, None));
+
+ }
+
+ if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) {
+
+ uint32_t write_loc = afl_global_id;
+
+ if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3);
+
+ GlobalVariable *AFLFinalLoc =
+ new GlobalVariable(M, Int32Ty, true, GlobalValue::ExternalLinkage,
+ 0, "__afl_final_loc");
+ ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc);
+ StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
+ StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"),
+ MDNode::get(C, None));
+
+ }
+
+ if (dictionary.size()) {
+
+ size_t memlen = 0, count = 0, offset = 0;
+ char * ptr;
+
+ for (auto token : dictionary) {
+
+ memlen += token.length();
+ count++;
+
+ }
+
+ if (!be_quiet)
+ printf("AUTODICTIONARY: %lu string%s found\n", count,
+ count == 1 ? "" : "s");
+
+ if (count) {
+
+ if ((ptr = (char *)malloc(memlen + count)) == NULL) {
+
+ fprintf(stderr, "Error: malloc for %lu bytes failed!\n",
+ memlen + count);
+ exit(-1);
+
+ }
+
+ count = 0;
+
+ for (auto token : dictionary) {
+
+ if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) {
+
+ ptr[offset++] = (uint8_t)token.length();
+ memcpy(ptr + offset, token.c_str(), token.length());
+ offset += token.length();
+ count++;
+
+ }
+
+ }
+
+ GlobalVariable *AFLDictionaryLen = new GlobalVariable(
+ M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
+ "__afl_dictionary_len");
+ ConstantInt *const_len = ConstantInt::get(Int32Ty, offset);
+ StoreInst * StoreDictLen =
+ IRB.CreateStore(const_len, AFLDictionaryLen);
+ StoreDictLen->setMetadata(M.getMDKindID("nosanitize"),
+ MDNode::get(C, None));
+
+ ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset);
+ GlobalVariable *AFLInternalDictionary = new GlobalVariable(
+ M, ArrayTy, true, GlobalValue::ExternalLinkage,
+ ConstantDataArray::get(
+ C, *(new ArrayRef<char>((char *)ptr, offset))),
+ "__afl_internal_dictionary");
+ AFLInternalDictionary->setInitializer(ConstantDataArray::get(
+ C, *(new ArrayRef<char>((char *)ptr, offset))));
+ AFLInternalDictionary->setConstant(true);
+
+ GlobalVariable *AFLDictionary = new GlobalVariable(
+ M, PointerType::get(Int8Ty, 0), false,
+ GlobalValue::ExternalLinkage, 0, "__afl_dictionary");
+
+ Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero);
+ Value *AFLDictPtr =
+ IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0));
+ StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary);
+ StoreDict->setMetadata(M.getMDKindID("nosanitize"),
+ MDNode::get(C, None));
+
+ }
+
+ }
+
+ }
+
+ // count basic blocks for comparison with classic instrumentation
+
+ u32 edges = 0;
+ for (auto &F : M) {
+
+ if (F.size() < function_minimum_size) continue;
+
+ for (auto &BB : F) {
+
+ bool would_instrument = false;
+
+ for (BasicBlock *Pred : predecessors(&BB)) {
+
+ int count = 0;
+ for (BasicBlock *Succ : successors(Pred))
+ if (Succ != NULL) count++;
+
+ if (count > 1) return true;
+
+ }
+
+ if (would_instrument == true) edges++;
+
+ }
+
+ }
+
+ /* Say something nice. */
+
+ if (!be_quiet) {
+
+ if (!inst_blocks)
+ WARNF("No instrumentation targets found.");
+ else {
+
+ char modeline[100];
+ snprintf(modeline, sizeof(modeline), "%s%s%s%s%s",
+ getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
+ getenv("AFL_USE_ASAN") ? ", ASAN" : "",
+ getenv("AFL_USE_MSAN") ? ", MSAN" : "",
+ getenv("AFL_USE_CFISAN") ? ", CFISAN" : "",
+ getenv("AFL_USE_UBSAN") ? ", UBSAN" : "");
+ OKF("Instrumented %u locations (%llu, %llu) with no collisions (on "
+ "average %llu collisions would be in afl-gcc/afl-clang-fast for %u "
+ "edges) (%s mode).",
+ inst_blocks, total_rs, total_hs, calculateCollisions(edges), edges,
+ modeline);
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+}; // end of struct InsTrim
+
+} // end of anonymous namespace
+
+char InsTrimLTO::ID = 0;
+
+static void registerInsTrimLTO(const PassManagerBuilder &,
+ legacy::PassManagerBase &PM) {
+
+ PM.add(new InsTrimLTO());
+
+}
+
+static RegisterPass<InsTrimLTO> X("afl-lto-instrim",
+ "afl++ InsTrim LTO instrumentation pass",
+ false, false);
+
+static RegisterStandardPasses RegisterInsTrimLTO(
+ PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerInsTrimLTO);
+
diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc
index 838e45af..f44b336e 100644
--- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc
+++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc
@@ -1,14 +1,9 @@
/*
- american fuzzy lop++ - LLVM-mode instrumentation pass
- ---------------------------------------------------
+ american fuzzy lop++ - LLVM LTO instrumentation pass
+ ----------------------------------------------------
- Written by Laszlo Szekeres <lszekeres@google.com> and
- Michal Zalewski
+ Written by Marc Heuse <mh@mh-sec.de>
- LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted
- from afl-as.c are Michal's fault.
-
- Copyright 2015, 2016 Google Inc. All rights reserved.
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +12,7 @@
http://www.apache.org/licenses/LICENSE-2.0
- This library is plugged into LLVM when invoking clang through afl-clang-fast.
- It tells the compiler to add code roughly equivalent to the bits discussed
- in ../afl-as.h.
+ This library is plugged into LLVM when invoking clang through afl-clang-lto.
*/
@@ -32,11 +25,12 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <sys/time.h>
#include <list>
#include <string>
#include <fstream>
-#include <sys/time.h>
+#include <set>
#include "llvm/Config/llvm-config.h"
#include "llvm/ADT/Statistic.h"
@@ -56,7 +50,6 @@
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Pass.h"
-#include <set>
#include "afl-llvm-common.h"
using namespace llvm;
@@ -90,27 +83,11 @@ class AFLLTOPass : public ModulePass {
}
- // Calculate the number of average collisions that would occur if all
- // 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) {
-
- double bins = MAP_SIZE;
- double balls = edges;
- double step1 = 1 - (1 / bins);
- double step2 = pow(step1, balls);
- double step3 = bins * step2;
- double step4 = round(step3);
- unsigned long long int empty = step4;
- unsigned long long int collisions = edges - (MAP_SIZE - empty);
- return collisions;
-
- }
-
bool runOnModule(Module &M) override;
protected:
int afl_global_id = 1, debug = 0, autodictionary = 0;
+ uint32_t function_minimum_size = 1;
uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0;
uint64_t map_addr = 0x10000;
char * skip_nozero = NULL;
@@ -131,8 +108,6 @@ bool AFLLTOPass::runOnModule(Module &M) {
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
- if (getenv("AFL_DEBUG")) debug = 1;
-
/* Show a banner */
if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
@@ -150,6 +125,10 @@ bool AFLLTOPass::runOnModule(Module &M) {
if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0;
+ if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
+ getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
+ function_minimum_size = 2;
+
if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) {
uint64_t val;
@@ -185,12 +164,10 @@ bool AFLLTOPass::runOnModule(Module &M) {
if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); }
- /* Get globals for the SHM region and the previous location. Note that
- __afl_prev_loc is thread-local. */
+ /* Get/set the globals for the SHM region. */
GlobalVariable *AFLMapPtr = NULL;
- ;
- Value *MapPtrFixed = NULL;
+ Value * MapPtrFixed = NULL;
if (!map_addr) {
@@ -217,7 +194,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
// fprintf(stderr, "DEBUG: Function %s\n", F.getName().str().c_str());
- if (F.size() < 2) continue;
+ if (F.size() < function_minimum_size) continue;
if (isBlacklisted(&F)) continue;
std::vector<BasicBlock *> InsBlocks;
@@ -354,11 +331,15 @@ bool AFLLTOPass::runOnModule(Module &M) {
if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
- if (auto *Array =
- dyn_cast<ConstantDataArray>(Var->getInitializer())) {
+ if (Var->hasInitializer()) {
+
+ if (auto *Array = dyn_cast<ConstantDataArray>(
+ Var->getInitializer())) {
- HasStr2 = true;
- Str2 = Array->getAsString().str();
+ HasStr2 = true;
+ Str2 = Array->getAsString().str();
+
+ }
}
@@ -426,11 +407,15 @@ bool AFLLTOPass::runOnModule(Module &M) {
if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
- if (auto *Array =
- dyn_cast<ConstantDataArray>(Var->getInitializer())) {
+ if (Var->hasInitializer()) {
+
+ if (auto *Array = dyn_cast<ConstantDataArray>(
+ Var->getInitializer())) {
+
+ HasStr1 = true;
+ Str1 = Array->getAsString().str();
- HasStr1 = true;
- Str1 = Array->getAsString().str();
+ }
}
diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc
index 0d9e0aba..2d23ad21 100644
--- a/llvm_mode/afl-llvm-pass.so.cc
+++ b/llvm_mode/afl-llvm-pass.so.cc
@@ -84,6 +84,7 @@ class AFLCoverage : public ModulePass {
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;
};
@@ -182,6 +183,10 @@ bool AFLCoverage::runOnModule(Module &M) {
#endif
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
+ if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
+ getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
+ function_minimum_size = 2;
+
unsigned PrevLocSize = 0;
char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE");
@@ -294,13 +299,15 @@ bool AFLCoverage::runOnModule(Module &M) {
if (!isInWhitelist(&F)) continue;
+ if (F.size() < function_minimum_size) continue;
+
for (auto &BB : F) {
BasicBlock::iterator IP = BB.getFirstInsertionPt();
IRBuilder<> IRB(&(*IP));
// Context sensitive coverage
- if (ctx_str && &BB == &F.getEntryBlock() && F.size() > 1) {
+ if (ctx_str && &BB == &F.getEntryBlock()) {
// load the context ID of the previous function and write to to a local
// variable on the stack
@@ -318,7 +325,7 @@ bool AFLCoverage::runOnModule(Module &M) {
if ((callInst = dyn_cast<CallInst>(&IN))) {
Function *Callee = callInst->getCalledFunction();
- if (!Callee || Callee->size() < 2)
+ if (!Callee || Callee->size() < function_minimum_size)
continue;
else {
@@ -389,11 +396,11 @@ bool AFLCoverage::runOnModule(Module &M) {
}
// fprintf(stderr, " == %d\n", more_than_one);
- if (more_than_one != 1) {
+ if (F.size() > 1 && more_than_one != 1) {
// in CTX mode we have to restore the original context for the caller -
// she might be calling other functions which need the correct CTX
- if (ctx_str && has_calls && F.size() > 1) {
+ if (ctx_str && has_calls) {
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
@@ -526,7 +533,7 @@ bool AFLCoverage::runOnModule(Module &M) {
// in CTX mode we have to restore the original context for the caller -
// she might be calling other functions which need the correct CTX.
// Currently this is only needed for the Ubuntu clang-6.0 bug
- if (ctx_str && has_calls && F.size() > 1) {
+ if (ctx_str && has_calls) {
Instruction *Inst = BB.getTerminator();
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh
index 1b6690c6..bd18927f 100755
--- a/qemu_mode/build_qemu_support.sh
+++ b/qemu_mode/build_qemu_support.sh
@@ -163,6 +163,8 @@ fi
cd qemu-$VERSION || exit 1
+echo Building for CPU target $CPU_TARGET
+
echo "[*] Applying patches..."
patch -p1 <../patches/elfload.diff || exit 1
@@ -188,6 +190,7 @@ echo "[+] Patching done."
if [ "$STATIC" = "1" ]; then
+ echo Building STATIC binary
./configure --extra-cflags="-O3 -ggdb -DAFL_QEMU_STATIC_BUILD=1" \
--disable-bsd-user --disable-guest-agent --disable-strip --disable-werror \
--disable-gcrypt --disable-debug-info --disable-debug-tcg --disable-tcg-interpreter \
diff --git a/src/afl-common.c b/src/afl-common.c
index 54b2e790..d9d57863 100644
--- a/src/afl-common.c
+++ b/src/afl-common.c
@@ -64,15 +64,15 @@ char *afl_environment_variables[] = {
"AFL_LD_PRELOAD", "AFL_LD_VERBOSE", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM",
"AFL_LLVM_CTX", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD",
"AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY",
- "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", "AFL_LLVM_LAF_SPLIT_COMPARES",
- "AFL_LLVM_LAF_SPLIT_COMPARES_BITW", "AFL_LLVM_LAF_SPLIT_FLOATS",
- "AFL_LLVM_LAF_SPLIT_SWITCHES", "AFL_LLVM_LAF_TRANSFORM_COMPARES",
- "AFL_LLVM_MAP_ADDR", "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE",
- "AFL_NGRAM_SIZE", "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST",
- "AFL_LLVM_SKIP_NEVERZERO", "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID",
- "AFL_LLVM_LTO_DONTWRITEID", "AFL_NO_ARITH", "AFL_NO_BUILTIN",
- "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", "AFL_NO_UI", "AFL_NO_PYTHON",
- "AFL_UNTRACER_FILE",
+ "AFL_LLVM_SKIPSINGLEBLOCK", "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK",
+ "AFL_LLVM_LAF_SPLIT_COMPARES", "AFL_LLVM_LAF_SPLIT_COMPARES_BITW",
+ "AFL_LLVM_LAF_SPLIT_FLOATS", "AFL_LLVM_LAF_SPLIT_SWITCHES",
+ "AFL_LLVM_LAF_TRANSFORM_COMPARES", "AFL_LLVM_MAP_ADDR",
+ "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", "AFL_NGRAM_SIZE",
+ "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", "AFL_LLVM_SKIP_NEVERZERO",
+ "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID",
+ "AFL_NO_ARITH", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV",
+ "AFL_NO_UI", "AFL_NO_PYTHON", "AFL_UNTRACER_FILE",
"AFL_NO_X86", // not really an env but we dont want to warn on it
"AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_PATH", "AFL_PERFORMANCE_FILE",
//"AFL_PERSISTENT", // not implemented anymore, so warn additionally
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index 692026d4..b7f7f29c 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -30,13 +30,37 @@
#include "cmplog.h"
+#ifdef PROFILING
+u64 time_spent_working = 0;
+#endif
+
/* Execute target application, monitoring for timeouts. Return status
information. The called program will update afl->fsrv->trace_bits. */
fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv,
u32 timeout) {
+#ifdef PROFILING
+ static u64 time_spent_start = 0;
+ struct timespec spec;
+ if (time_spent_start) {
+
+ u64 current;
+ clock_gettime(CLOCK_REALTIME, &spec);
+ current = (spec.tv_sec * 1000000000) + spec.tv_nsec;
+ time_spent_working += (current - time_spent_start);
+
+ }
+
+#endif
+
fsrv_run_result_t res = afl_fsrv_run_target(fsrv, timeout, &afl->stop_soon);
+
+#ifdef PROFILING
+ clock_gettime(CLOCK_REALTIME, &spec);
+ time_spent_start = (spec.tv_sec * 1000000000) + spec.tv_nsec;
+#endif
+
// TODO: Don't classify for faults?
classify_counts(fsrv);
return res;
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 5920f5c0..64973260 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -27,6 +27,10 @@
#include "cmplog.h"
#include <limits.h>
+#ifdef PROFILING
+extern u64 time_spent_working;
+#endif
+
static u8 *get_libradamsa_path(u8 *own_loc) {
u8 *tmp, *cp, *rsl, *own_copy;
@@ -1351,6 +1355,13 @@ stop_fuzzing:
}
+#ifdef PROFILING
+ SAYF(cYEL "[!] " cRST
+ "Profiling information: %llu ms total work, %llu ns/run\n",
+ time_spent_working / 1000000,
+ time_spent_working / afl->fsrv.total_execs);
+#endif
+
fclose(afl->fsrv.plot_file);
destroy_queue(afl);
destroy_extras(afl);
diff --git a/unicorn_mode/UNICORNAFL_VERSION b/unicorn_mode/UNICORNAFL_VERSION
index 11bd2155..c0cc9e02 100644
--- a/unicorn_mode/UNICORNAFL_VERSION
+++ b/unicorn_mode/UNICORNAFL_VERSION
@@ -1 +1 @@
-d4cc77cce71c15bb3d1f552d703a77e2a17cf42d
+94c1976
diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl
-Subproject d4cc77cce71c15bb3d1f552d703a77e2a17cf42
+Subproject 94c1976975518691a03602f7ec5a817e2f34118