aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrea Fioraldi <andreafioraldi@gmail.com>2020-01-30 22:52:27 +0100
committerGitHub <noreply@github.com>2020-01-30 22:52:27 +0100
commit6e9fce1c2d654c92dbf8e6b8cc21a88d8cba9496 (patch)
tree9c6d27d58d0606d59725ef46766eb1961e908d31
parentf07fc52cd061fadde21a57fd757e316d6254f588 (diff)
parentb050c1158398dd07e25a6cd65234da84e5656fa6 (diff)
downloadafl++-6e9fce1c2d654c92dbf8e6b8cc21a88d8cba9496.tar.gz
Merge branch 'master' into CmpLog
-rw-r--r--Dockerfile13
-rw-r--r--Makefile22
-rw-r--r--README.md8
-rwxr-xr-xafl-cmin906
-rwxr-xr-xafl-cmin.bash470
-rwxr-xr-xafl-system-config93
-rw-r--r--docs/ChangeLog12
-rw-r--r--docs/binaryonly_fuzzing.md161
-rw-r--r--docs/binaryonly_fuzzing.txt144
-rw-r--r--experimental/README.experiments3
-rw-r--r--include/afl-as.h2
-rw-r--r--include/afl-fuzz.h2
-rw-r--r--include/alloc-inl.h2
-rwxr-xr-xinclude/android-ashmem.h2
-rw-r--r--include/common.h2
-rw-r--r--include/config.h5
-rw-r--r--include/debug.h2
-rw-r--r--include/forkserver.h2
-rw-r--r--include/sharedmem.h2
-rw-r--r--include/types.h2
-rw-r--r--libdislocator/libdislocator.so.c23
-rw-r--r--llvm_mode/LLVMInsTrim.so.cc89
-rw-r--r--llvm_mode/Makefile8
-rw-r--r--llvm_mode/MarkNodes.cc30
-rw-r--r--llvm_mode/README.md21
-rw-r--r--llvm_mode/afl-clang-fast.c33
-rw-r--r--llvm_mode/afl-llvm-pass.so.cc71
-rw-r--r--llvm_mode/compare-transform-pass.so.cc150
-rw-r--r--llvm_mode/split-compares-pass.so.cc270
-rw-r--r--llvm_mode/split-switches-pass.so.cc199
-rw-r--r--src/afl-analyze.c2
-rw-r--r--src/afl-as.c2
-rw-r--r--src/afl-common.c2
-rw-r--r--src/afl-forkserver.c2
-rw-r--r--src/afl-fuzz-bitmap.c2
-rw-r--r--src/afl-fuzz-extras.c2
-rw-r--r--src/afl-fuzz-globals.c2
-rw-r--r--src/afl-fuzz-init.c46
-rw-r--r--src/afl-fuzz-misc.c2
-rw-r--r--src/afl-fuzz-one.c2
-rw-r--r--src/afl-fuzz-python.c2
-rw-r--r--src/afl-fuzz-queue.c2
-rw-r--r--src/afl-fuzz-run.c2
-rw-r--r--src/afl-fuzz-stats.c2
-rw-r--r--src/afl-fuzz.c2
-rw-r--r--src/afl-gcc.c2
-rw-r--r--src/afl-gotcpu.c2
-rw-r--r--src/afl-sharedmem.c2
-rw-r--r--src/afl-showmap.c2
-rw-r--r--src/afl-tmin.c2
-rw-r--r--test/test-unsigaction.c25
-rwxr-xr-xtest/test.sh113
-rw-r--r--unicorn_mode/samples/c/a.outbin17184 -> 0 bytes
-rw-r--r--unicorn_mode/samples/c/harness.c81
-rw-r--r--unicorn_mode/samples/c/persistent_target.c39
-rw-r--r--unicorn_mode/samples/c/persistent_target_x86_64bin0 -> 16544 bytes
-rw-r--r--unicorn_mode/samples/persistent/.gitignore3
-rw-r--r--unicorn_mode/samples/persistent/COMPILE.md24
-rw-r--r--unicorn_mode/samples/persistent/Makefile42
-rw-r--r--unicorn_mode/samples/persistent/harness.c269
-rw-r--r--unicorn_mode/samples/persistent/persistent_target.c39
-rw-r--r--unicorn_mode/samples/persistent/persistent_target_x86_64bin0 -> 16544 bytes
-rw-r--r--unicorn_mode/samples/persistent/sample_all.sh18
-rw-r--r--unicorn_mode/samples/persistent/sample_inputs/sample1.bin1
-rw-r--r--unicorn_mode/samples/persistent/sample_inputs/sample2.binbin0 -> 1 bytes
-rw-r--r--unicorn_mode/samples/persistent/sample_inputs/sample3.bin1
-rw-r--r--unicorn_mode/samples/persistent/sample_inputs/sample4.bin1
-rw-r--r--unicorn_mode/samples/persistent/sample_inputs/sample5.bin1
-rw-r--r--unicorn_mode/samples/persistent/simple_target_noncrashing.c (renamed from unicorn_mode/samples/c/simple_target.c)7
-rw-r--r--unicorn_mode/samples/persistent/simple_target_x86_64bin0 -> 17624 bytes
70 files changed, 2638 insertions, 859 deletions
diff --git a/Dockerfile b/Dockerfile
index 1947f211..7bb60610 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,6 +9,9 @@ RUN apt-get update && apt-get install -y \
clang \
clang-9 \
flex \
+ git \
+ python3.7 \
+ python3.7-dev \
gcc-9 \
gcc-9-plugin-dev \
gcc-9-multilib \
@@ -23,10 +26,12 @@ RUN apt-get update && apt-get install -y \
ca-certificates \
libpixman-1-dev \
&& rm -rf /var/lib/apt/lists/*
+
ARG CC=gcc-9
ARG CXX=g++-9
ARG LLVM_CONFIG=llvm-config-9
-COPY . /app
-RUN cd /app && make clean && make distrib && \
- make install && cd .. && rm -rf /app
-WORKDIR /work
+
+RUN git clone https://github.com/vanhauser-thc/AFLplusplus
+
+RUN cd AFLplusplus && make clean && make distrib && \
+ make install && cd .. && rm -rf AFLplusplus
diff --git a/Makefile b/Makefile
index 703ed673..459cae5f 100644
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@ VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
# PROGS intentionally omit afl-as, which gets installed elsewhere.
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
-SH_PROGS = afl-plot afl-cmin afl-whatsup afl-system-config
+SH_PROGS = afl-plot afl-cmin afl-cmin.bash afl-whatsup afl-system-config
MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8)
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
@@ -48,6 +48,14 @@ ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .te
CFLAGS_OPT = -march=native
endif
+ifneq "$(shell uname -m)" "x86_64"
+ ifneq "$(shell uname -m)" "i386"
+ ifneq "$(shell uname -m)" "amd64"
+ AFL_NO_X86=1
+ endif
+ endif
+endif
+
CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT)
CFLAGS += -Wall -g -Wno-pointer-sign -I include/ \
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
@@ -55,17 +63,17 @@ CFLAGS += -Wall -g -Wno-pointer-sign -I include/ \
AFL_FUZZ_FILES = $(wildcard src/afl-fuzz*.c)
-ifneq "($filter %3.7m, $(shell python3.7m-config --includes 2>/dev/null)" ""
+ifneq "$(filter %3.7m, $(shell python3.7m-config --includes 2>/dev/null))" ""
PYTHON_INCLUDE ?= $(shell python3.7m-config --includes)
PYTHON_LIB ?= $(shell python3.7m-config --ldflags)
PYTHON_VERSION = 3.7m
else
- ifneq "($filter %3.7, $(shell python3.7-config --includes) 2> /dev/null" ""
+ ifneq "$(filter %3.7, $(shell python3.7-config --includes 2>/dev/null))" ""
PYTHON_INCLUDE ?= $(shell python3.7-config --includes)
PYTHON_LIB ?= $(shell python3.7-config --ldflags)
PYTHON_VERSION = 3.7
else
- ifneq "($filter %2.7, $(shell python2.7-config --includes) 2> /dev/null" ""
+ ifneq "$(filter %2.7, $(shell python2.7-config --includes 2>/dev/null))" ""
PYTHON_INCLUDE ?= $(shell python2.7-config --includes)
PYTHON_LIB ?= $(shell python2.7-config --ldflags)
PYTHON_VERSION = 2.7
@@ -77,14 +85,14 @@ PYTHON_INCLUDE ?= $(shell test -e /usr/include/python3.7m && echo /usr/include/p
PYTHON_INCLUDE ?= $(shell test -e /usr/include/python3.7 && echo /usr/include/python3.7)
PYTHON_INCLUDE ?= $(shell test -e /usr/include/python2.7 && echo /usr/include/python2.7)
-ifneq "($filter %3.7m, $(PYTHON_INCLUDE))" ""
+ifneq "$(filter %3.7m, $(PYTHON_INCLUDE))" ""
PYTHON_VERSION ?= 3.7m
PYTHON_LIB ?= -lpython3.7m
else
- ifneq "($filter %3.7, $(PYTHON_INCLUDE))" ""
+ ifneq "$(filter %3.7, $(PYTHON_INCLUDE))" ""
PYTHON_VERSION ?= 3.7
else
- ifneq "($filter %2.7, $(PYTHON_INCLUDE))" ""
+ ifneq "$(filter %2.7, $(PYTHON_INCLUDE))" ""
PYTHON_VERSION ?= 2.7
PYTHON_LIB ?= -lpython2.7
else
diff --git a/README.md b/README.md
index 2edca8af..dc43d5d2 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@
get any feature improvements since November 2017.
Among other changes afl++ has a more performant llvm_mode, supports
- llvm up to version 10, QEMU 3.1, more speed and crashfixes for QEMU,
+ llvm up to version 11, QEMU 3.1, more speed and crashfixes for QEMU,
better *BSD and Android support and much, much more.
Additionally the following features and patches have been integrated:
@@ -204,7 +204,7 @@ superior to blind fuzzing or coverage-only tools.
PLEASE NOTE: llvm_mode compilation with afl-clang-fast/afl-clang-fast++
instead of afl-gcc/afl-g++ is much faster and has a few cool features.
See llvm_mode/ - however few code does not compile with llvm.
-We support llvm versions 3.8.0 to 10.
+We support llvm versions 3.8.0 to 11.
When source code is available, instrumentation can be injected by a companion
tool that works as a drop-in replacement for gcc or clang in any standard build
@@ -227,7 +227,7 @@ For C++ programs, you'd would also want to set `CXX=/path/to/afl/afl-g++`.
The clang wrappers (afl-clang and afl-clang++) can be used in the same way;
clang users may also opt to leverage a higher-performance instrumentation mode,
as described in [llvm_mode/README.md](llvm_mode/README.md).
-Clang/LLVM has a much better performance and works with LLVM version 3.8.0 to 10.
+Clang/LLVM has a much better performance and works with LLVM version 3.8.0 to 11.
Using the LAF Intel performance enhancements are also recommended, see
[llvm_mode/README.laf-intel.md](llvm_mode/README.laf-intel.md)
@@ -272,7 +272,7 @@ $ ./build_qemu_support.sh
For additional instructions and caveats, see [qemu_mode/README.md](qemu_mode/README.md).
The mode is approximately 2-5x slower than compile-time instrumentation, is
-less conductive to parallelization, and may have some other quirks.
+less conducive to parallelization, and may have some other quirks.
If [afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst) works for
your binary, then you can use afl-fuzz normally and it will have twice
diff --git a/afl-cmin b/afl-cmin
index 1dd782d8..9179628e 100755
--- a/afl-cmin
+++ b/afl-cmin
@@ -1,470 +1,464 @@
-#!/usr/bin/env bash
-#
-# american fuzzy lop++ - corpus minimization tool
-# ---------------------------------------------
-#
-# Originally written by Michal Zalewski
-#
-# Copyright 2014, 2015 Google Inc. 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 tool tries to find the smallest subset of files in the input directory
-# that still trigger the full range of instrumentation data points seen in
-# the starting corpus. This has two uses:
-#
-# - Screening large corpora of input files before using them as a seed for
-# afl-fuzz. The tool will remove functionally redundant files and likely
-# leave you with a much smaller set.
-#
-# (In this case, you probably also want to consider running afl-tmin on
-# the individual files later on to reduce their size.)
-#
-# - Minimizing the corpus generated organically by afl-fuzz, perhaps when
-# planning to feed it to more resource-intensive tools. The tool achieves
-# this by removing all entries that used to trigger unique behaviors in the
-# past, but have been made obsolete by later finds.
+#!/usr/bin/env sh
+THISPATH=`dirname ${0}`
+export PATH=${THISPATH}:$PATH
+awk -f - -- ${@+"$@"} <<'EOF'
+#!/usr/bin/awk -f
+
+# awk script to minimize a test corpus of input files
#
-# Note that the tool doesn't modify the files themselves. For that, you want
-# afl-tmin.
+# based on afl-cmin bash script written by Michal Zalewski
+# rewritten by Heiko Eißfeldt (hexcoder-)
+# tested with:
+# gnu awk (x86 Linux)
+# bsd awk (x86 *BSD)
+# mawk (arm32 raspbian)
#
-# This script must use bash because other shells may have hardcoded limits on
-# array sizes.
+# uses getopt.awk package from Arnold Robbins
#
-
-echo "corpus minimization tool for afl-fuzz by Michal Zalewski"
-echo
-
-#########
-# SETUP #
-#########
-
-# Process command-line options...
-
-MEM_LIMIT=200
-TIMEOUT=none
-
-unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
- AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE
-
-while getopts "+i:o:f:m:t:eQUCh" opt; do
-
- case "$opt" in
-
- "h")
- ;;
-
- "i")
- IN_DIR="$OPTARG"
- ;;
-
- "o")
- OUT_DIR="$OPTARG"
- ;;
- "f")
- STDIN_FILE="$OPTARG"
- ;;
- "m")
- MEM_LIMIT="$OPTARG"
- MEM_LIMIT_GIVEN=1
- ;;
- "t")
- TIMEOUT="$OPTARG"
- ;;
- "e")
- EXTRA_PAR="$EXTRA_PAR -e"
- ;;
- "C")
- export AFL_CMIN_CRASHES_ONLY=1
- ;;
- "Q")
- EXTRA_PAR="$EXTRA_PAR -Q"
- test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250
- QEMU_MODE=1
- ;;
- "U")
- EXTRA_PAR="$EXTRA_PAR -U"
- test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250
- UNICORN_MODE=1
- ;;
- "?")
- exit 1
- ;;
-
- esac
-
-done
-
-shift $((OPTIND-1))
-
-TARGET_BIN="$1"
-
-if [ "$TARGET_BIN" = "" -o "$IN_DIR" = "" -o "$OUT_DIR" = "" ]; then
-
- cat 1>&2 <<_EOF_
-Usage: $0 [ options ] -- /path/to/target_app [ ... ]
-
-Required parameters:
-
- -i dir - input directory with the starting corpus
- -o dir - output directory for minimized files
-
-Execution control settings:
-
- -f file - location read by the fuzzed program (stdin)
- -m megs - memory limit for child process ($MEM_LIMIT MB)
- -t msec - run time limit for child process (none)
- -Q - use binary-only instrumentation (QEMU mode)
- -U - use unicorn-based instrumentation (Unicorn mode)
-
-Minimization settings:
-
- -C - keep crashing inputs, reject everything else
- -e - solve for edge coverage only, ignore hit counts
-
-For additional tips, please consult docs/README.
-
-_EOF_
- exit 1
-fi
-
-# Do a sanity check to discourage the use of /tmp, since we can't really
-# handle this safely from a shell script.
-
-if [ "$AFL_ALLOW_TMP" = "" ]; then
-
- echo "$IN_DIR" | grep -qE '^(/var)?/tmp/'
- T1="$?"
-
- echo "$TARGET_BIN" | grep -qE '^(/var)?/tmp/'
- T2="$?"
-
- echo "$OUT_DIR" | grep -qE '^(/var)?/tmp/'
- T3="$?"
-
- echo "$STDIN_FILE" | grep -qE '^(/var)?/tmp/'
- T4="$?"
-
- echo "$PWD" | grep -qE '^(/var)?/tmp/'
- T5="$?"
-
- if [ "$T1" = "0" -o "$T2" = "0" -o "$T3" = "0" -o "$T4" = "0" -o "$T5" = "0" ]; then
- echo "[-] Error: do not use this script in /tmp or /var/tmp." 1>&2
+# external tools used by this script:
+# test
+# grep
+# rm
+# mkdir
+# ln
+# cp
+# pwd
+# which
+# cd
+# find
+# stat
+# sort
+# cut
+# and afl-showmap from this project :-)
+
+# getopt.awk --- Do C library getopt(3) function in awk
+
+# External variables:
+# Optind -- index in ARGV of first nonoption argument
+# Optarg -- string value of argument to current option
+# Opterr -- if nonzero, print our own diagnostic
+# Optopt -- current option letter
+
+# Returns:
+# -1 at end of options
+# "?" for unrecognized option
+# <c> a character representing the current option
+
+# Private Data:
+# _opti -- index in multiflag option, e.g., -abc
+
+function getopt(argc, argv, options, thisopt, i)
+{
+ if (length(options) == 0) # no options given
+ return -1
+
+ if (argv[Optind] == "--") { # all done
+ Optind++
+ _opti = 0
+ return -1
+ } else if (argv[Optind] !~ /^-[^:\t ]/) {
+ _opti = 0
+ return -1
+ }
+ if (_opti == 0)
+ _opti = 2
+ thisopt = substr(argv[Optind], _opti, 1)
+ Optopt = thisopt
+ i = index(options, thisopt)
+ if (i == 0) {
+ if (Opterr)
+ printf("%c -- invalid option\n", thisopt) > "/dev/stderr"
+ if (_opti >= length(argv[Optind])) {
+ Optind++
+ _opti = 0
+ } else
+ _opti++
+ return "?"
+ }
+ if (substr(options, i + 1, 1) == ":") {
+ # get option argument
+ if (length(substr(argv[Optind], _opti + 1)) > 0)
+ Optarg = substr(argv[Optind], _opti + 1)
+ else
+ Optarg = argv[++Optind]
+ _opti = 0
+ } else
+ Optarg = ""
+ if (_opti == 0 || _opti >= length(argv[Optind])) {
+ Optind++
+ _opti = 0
+ } else
+ _opti++
+ return thisopt
+}
+
+function usage() {
+ print \
+"Usage: afl-cmin [ options ] -- /path/to/target_app [ ... ]\n" \
+"\n" \
+"Required parameters:\n" \
+"\n" \
+" -i dir - input directory with starting corpus\n" \
+" -o dir - output directory for minimized files\n" \
+"\n" \
+"Execution control settings:\n" \
+"\n" \
+" -f file - location read by the fuzzed program (stdin)\n" \
+" -m megs - memory limit for child process ("mem_limit" MB)\n" \
+" -t msec - run time limit for child process (none)\n" \
+" -Q - use binary-only instrumentation (QEMU mode)\n" \
+" -U - use unicorn-based instrumentation (unicorn mode)\n" \
+"\n" \
+"Minimization settings:\n" \
+" -C - keep crashing inputs, reject everything else\n" \
+" -e - solve for edge coverage only, ignore hit counts\n" \
+"\n" \
+"For additional tips, please consult docs/README.md\n" \
+"\n" \
+ > "/dev/stderr"
+ exit 1
+}
+
+function exists_and_is_executable(binarypath) {
+ return 0 == system("test -f "binarypath" -a -x "binarypath)
+}
+
+BEGIN {
+ print "corpus minimization tool for afl++ (awk version)\n"
+
+ # defaults
+ extra_par = ""
+ # process options
+ Opterr = 1 # default is to diagnose
+ Optind = 1 # skip ARGV[0]
+ while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eCQU?")) != -1) {
+ if (_go_c == "i") {
+ if (!Optarg) usage()
+ if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
+ in_dir = Optarg
+ continue
+ } else
+ if (_go_c == "o") {
+ if (!Optarg) usage()
+ if (out_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
+ out_dir = Optarg
+ continue
+ } else
+ if (_go_c == "f") {
+ if (!Optarg) usage()
+ if (stdin_file) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
+ stdin_file = Optarg
+ continue
+ } else
+ if (_go_c == "m") {
+ if (!Optarg) usage()
+ if (mem_limit) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
+ mem_limit = Optarg
+ mem_limit_given = 1
+ continue
+ } else
+ if (_go_c == "t") {
+ if (!Optarg) usage()
+ if (timeout) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
+ timeout = Optarg
+ continue
+ } else
+ if (_go_c == "C") {
+ ENVIRON["AFL_CMIN_CRASHES_ONLY"] = 1
+ continue
+ } else
+ if (_go_c == "e") {
+ extra_par = extra_par " -e"
+ continue
+ } else
+ if (_go_c == "Q") {
+ if (qemu_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
+ extra_par = extra_par " -Q"
+ if ( !mem_limit_given ) mem_limit = "250"
+ qemu_mode = 1
+ continue
+ } else
+ if (_go_c == "U") {
+ if (unicorn_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
+ extra_par = extra_par " -U"
+ if ( !mem_limit_given ) mem_limit = "250"
+ unicorn_mode = 1
+ continue
+ } else
+ if (_go_c == "?") {
+ exit 1
+ } else
+ usage()
+ } # while options
+
+ if (!mem_limit) mem_limit = 200
+ if (!timeout) timeout = "none"
+
+ # get program args
+ i = 0
+ prog_args_string = ""
+ for (; Optind < ARGC; Optind++) {
+ prog_args[i++] = ARGV[Optind]
+ if (i > 1)
+ prog_args_string = prog_args_string" "ARGV[Optind]
+ }
+
+ # sanity checks
+ if (!prog_args[0] || !in_dir || !out_dir) usage()
+
+ target_bin = prog_args[0]
+
+ # Do a sanity check to discourage the use of /tmp, since we can't really
+ # handle this safely from an awk script.
+
+ if (!ENVIRON["AFL_ALLOW_TMP"]) {
+ dirlist[0] = in_dir
+ dirlist[1] = target_bin
+ dirlist[2] = out_dir
+ dirlist[3] = stdin_file
+ "pwd" | getline dirlist[4] # current directory
+ for (dirind in dirlist) {
+ dir = dirlist[dirind]
+
+ if (dir ~ /^(\/var)?\/tmp/) {
+ print "[-] Error: do not use this script in /tmp or /var/tmp." > "/dev/stderr"
+ exit 1
+ }
+ }
+ delete dirlist
+ }
+
+ # If @@ is specified, but there's no -f, let's come up with a temporary input
+ # file name.
+
+ trace_dir = out_dir "/.traces"
+
+ if (!stdin_file) {
+ found_atat = 0
+ for (prog_args_ind in prog_args) {
+ if ("@@" == prog_args[prog_args_ind]) {
+ found_atat = 1
+ break
+ }
+ }
+ if (found_atat) {
+ stdin_file = trace_dir "/.cur_input"
+ }
+ }
+
+ # Check for obvious errors.
+
+ if (mem_limit && mem_limit != "none" && mem_limit < 5) {
+ print "[-] Error: dangerously low memory limit." > "/dev/stderr"
exit 1
- fi
-
-fi
-
-# If @@ is specified, but there's no -f, let's come up with a temporary input
-# file name.
-
-TRACE_DIR="$OUT_DIR/.traces"
+ }
-if [ "$STDIN_FILE" = "" ]; then
-
- if echo "$*" | grep -qF '@@'; then
- STDIN_FILE="$TRACE_DIR/.cur_input"
- fi
-
-fi
-
-# Check for obvious errors.
-
-if [ ! "$MEM_LIMIT" = "none" ]; then
-
- if [ "$MEM_LIMIT" -lt "5" ]; then
- echo "[-] Error: dangerously low memory limit." 1>&2
+ if (timeout && timeout != "none" && timeout < 10) {
+ print "[-] Error: dangerously low timeout." > "/dev/stderr"
exit 1
- fi
-
-fi
-
-if [ ! "$TIMEOUT" = "none" ]; then
-
- if [ "$TIMEOUT" -lt "10" ]; then
- echo "[-] Error: dangerously low timeout." 1>&2
+ }
+
+ if (target_bin && !exists_and_is_executable(target_bin)) {
+
+ "which "target_bin" 2>/dev/null" | getline tnew
+ if (!tnew || !exists_and_is_executable(tnew)) {
+ print "[-] Error: binary '"target_bin"' not found or not executable." > "/dev/stderr"
+ exit 1
+ }
+ target_bin = tnew
+ }
+
+ if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !unicorn_mode) {
+ if (0 != system( "grep -q __AFL_SHM_ID "target_bin )) {
+ print "[-] Error: binary '"target_bin"' doesn't appear to be instrumented." > "/dev/stderr"
+ exit 1
+ }
+ }
+
+ if (0 != system( "test -d "in_dir )) {
+ print "[-] Error: directory '"in_dir"' not found." > "/dev/stderr"
exit 1
- fi
+ }
-fi
+ if (0 == system( "test -d "in_dir"/queue" )) {
+ in_dir = in_dir "/queue"
+ }
-if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then
+ system("rm -rf "trace_dir" 2>/dev/null");
+ system("rm "out_dir"/id[:_]* 2>/dev/null")
- TNEW="`which "$TARGET_BIN" 2>/dev/null`"
-
- if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then
- echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2
+ if (0 == system( "test -d "out_dir" -a -e "out_dir"/*" )) {
+ print "[-] Error: directory '"out_dir"' exists and is not empty - delete it first." > "/dev/stderr"
exit 1
- fi
-
- TARGET_BIN="$TNEW"
-
-fi
+ }
-if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$UNICORN_MODE" = "" ]; then
-
- if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then
- echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2
+ # Check for the more efficient way to copy files...
+ if (0 != system("mkdir -p -m 0700 "trace_dir)) {
+ print "[-] Error: Cannot create directory "trace_dir > "/dev/stderr"
exit 1
- fi
-
-fi
-
-if [ ! -d "$IN_DIR" ]; then
- echo "[-] Error: directory '$IN_DIR' not found." 1>&2
- exit 1
-fi
-
-test -d "$IN_DIR/queue" && IN_DIR="$IN_DIR/queue"
-
-find "$OUT_DIR" -name 'id[:_]*' -maxdepth 1 -exec rm -- {} \; 2>/dev/null
-rm -rf "$TRACE_DIR" 2>/dev/null
-
-rmdir "$OUT_DIR" 2>/dev/null
-
-if [ -d "$OUT_DIR" ]; then
- echo "[-] Error: directory '$OUT_DIR' exists and is not empty - delete it first." 1>&2
- exit 1
-fi
-
-mkdir -m 700 -p "$TRACE_DIR" || exit 1
-
-if [ ! "$STDIN_FILE" = "" ]; then
- rm -f "$STDIN_FILE" || exit 1
- touch "$STDIN_FILE" || exit 1
-fi
-
-if [ "$AFL_PATH" = "" ]; then
- SHOWMAP="${0%/afl-cmin}/afl-showmap"
-else
- SHOWMAP="$AFL_PATH/afl-showmap"
-fi
-
-if [ ! -x "$SHOWMAP" ]; then
- echo "[-] Error: can't find 'afl-showmap' - please set AFL_PATH." 1>&2
- rm -rf "$TRACE_DIR"
- exit 1
-fi
-
-IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`))
-
-if [ "$IN_COUNT" = "0" ]; then
- echo "[+] Hmm, no inputs in the target directory. Nothing to be done."
- rm -rf "$TRACE_DIR"
- exit 1
-fi
-
-FIRST_FILE=`ls "$IN_DIR" | head -1`
-
-# Make sure that we're not dealing with a directory.
-
-if [ -d "$IN_DIR/$FIRST_FILE" ]; then
- echo "[-] Error: The target directory contains subdirectories - please fix." 1>&2
- rm -rf "$TRACE_DIR"
- exit 1
-fi
-
-# Check for the more efficient way to copy files...
-
-if ln "$IN_DIR/$FIRST_FILE" "$TRACE_DIR/.link_test" 2>/dev/null; then
- CP_TOOL=ln
-else
- CP_TOOL=cp
-fi
-
-# Make sure that we can actually get anything out of afl-showmap before we
-# waste too much time.
-
-echo "[*] Testing the target binary..."
-
-if [ "$STDIN_FILE" = "" ]; then
-
- AFL_CMIN_ALLOW_ANY=1 "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/.run_test" -Z $EXTRA_PAR -- "$@" <"$IN_DIR/$FIRST_FILE"
-
-else
-
- cp "$IN_DIR/$FIRST_FILE" "$STDIN_FILE"
- AFL_CMIN_ALLOW_ANY=1 "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/.run_test" -Z $EXTRA_PAR -A "$STDIN_FILE" -- "$@" </dev/null
-
-fi
-
-FIRST_COUNT=$((`grep -c . "$TRACE_DIR/.run_test"`))
-
-if [ "$FIRST_COUNT" -gt "0" ]; then
-
- echo "[+] OK, $FIRST_COUNT tuples recorded."
-
-else
-
- echo "[-] Error: no instrumentation output detected (perhaps crash or timeout)." 1>&2
- test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR"
- exit 1
-
-fi
-
-# Let's roll!
-
-#############################
-# STEP 1: COLLECTING TRACES #
-#############################
-
-echo "[*] Obtaining traces for input files in '$IN_DIR'..."
-
-(
-
- CUR=0
-
- if [ "$STDIN_FILE" = "" ]; then
-
- ls "$IN_DIR" | while read -r fn; do
-
- CUR=$((CUR+1))
- printf "\\r Processing file $CUR/$IN_COUNT... "
-
- "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -- "$@" <"$IN_DIR/$fn"
-
- done
-
- else
-
- ls "$IN_DIR" | while read -r fn; do
-
- CUR=$((CUR+1))
- printf "\\r Processing file $CUR/$IN_COUNT... "
-
- cp "$IN_DIR/$fn" "$STDIN_FILE"
-
- "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -A "$STDIN_FILE" -- "$@" </dev/null
-
- done
-
-
- fi
-
-)
-
-echo
-
-##########################
-# STEP 2: SORTING TUPLES #
-##########################
-
-# With this out of the way, we sort all tuples by popularity across all
-# datasets. The reasoning here is that we won't be able to avoid the files
-# that trigger unique tuples anyway, so we will want to start with them and
-# see what's left.
-
-echo "[*] Sorting trace sets (this may take a while)..."
-
-ls "$IN_DIR" | sed "s#^#$TRACE_DIR/#" | tr '\n' '\0' | xargs -0 -n 1 cat | \
- sort | uniq -c | sort -k 1,1 -n >"$TRACE_DIR/.all_uniq"
-
-TUPLE_COUNT=$((`grep -c . "$TRACE_DIR/.all_uniq"`))
-
-echo "[+] Found $TUPLE_COUNT unique tuples across $IN_COUNT files."
-
-#####################################
-# STEP 3: SELECTING CANDIDATE FILES #
-#####################################
-
-# The next step is to find the best candidate for each tuple. The "best"
-# part is understood simply as the smallest input that includes a particular
-# tuple in its trace. Empirical evidence suggests that this produces smaller
-# datasets than more involved algorithms that could be still pulled off in
-# a shell script.
-
-echo "[*] Finding best candidates for each tuple..."
-
-CUR=0
-
-ls -rS "$IN_DIR" | while read -r fn; do
-
- CUR=$((CUR+1))
- printf "\\r Processing file $CUR/$IN_COUNT... "
-
- sed "s#\$# $fn#" "$TRACE_DIR/$fn" >>"$TRACE_DIR/.candidate_list"
-
-done
-
-echo
-
-##############################
-# STEP 4: LOADING CANDIDATES #
-##############################
-
-# At this point, we have a file of tuple-file pairs, sorted by file size
-# in ascending order (as a consequence of ls -rS). By doing sort keyed
-# only by tuple (-k 1,1) and configured to output only the first line for
-# every key (-s -u), we end up with the smallest file for each tuple.
-
-echo "[*] Sorting candidate list (be patient)..."
-
-sort -k1,1 -s -u "$TRACE_DIR/.candidate_list" | \
- sed 's/^/BEST_FILE[/;s/ /]="/;s/$/"/' >"$TRACE_DIR/.candidate_script"
-
-if [ ! -s "$TRACE_DIR/.candidate_script" ]; then
- echo "[-] Error: no traces obtained from test cases, check syntax!" 1>&2
- test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR"
- exit 1
-fi
-
-# The sed command converted the sorted list to a shell script that populates
-# BEST_FILE[tuple]="fname". Let's load that!
-
-. "$TRACE_DIR/.candidate_script"
-
-##########################
-# STEP 5: WRITING OUTPUT #
-##########################
-
-# The final trick is to grab the top pick for each tuple, unless said tuple is
-# already set due to the inclusion of an earlier candidate; and then put all
-# tuples associated with the newly-added file to the "already have" list. The
-# loop works from least popular tuples and toward the most common ones.
-
-echo "[*] Processing candidates and writing output files..."
-
-CUR=0
-
-touch "$TRACE_DIR/.already_have"
-
-while read -r cnt tuple; do
-
- CUR=$((CUR+1))
- printf "\\r Processing tuple $CUR/$TUPLE_COUNT... "
-
- # If we already have this tuple, skip it.
-
- grep -q "^$tuple\$" "$TRACE_DIR/.already_have" && continue
-
- FN=${BEST_FILE[tuple]}
-
- $CP_TOOL "$IN_DIR/$FN" "$OUT_DIR/$FN"
-
- if [ "$((CUR % 5))" = "0" ]; then
- sort -u "$TRACE_DIR/$FN" "$TRACE_DIR/.already_have" >"$TRACE_DIR/.tmp"
- mv -f "$TRACE_DIR/.tmp" "$TRACE_DIR/.already_have"
- else
- cat "$TRACE_DIR/$FN" >>"$TRACE_DIR/.already_have"
- fi
-
-done <"$TRACE_DIR/.all_uniq"
-
-echo
-
-OUT_COUNT=`ls -- "$OUT_DIR" | wc -l`
-
-if [ "$OUT_COUNT" = "1" ]; then
- echo "[!] WARNING: All test cases had the same traces, check syntax!"
-fi
-
-echo "[+] Narrowed down to $OUT_COUNT files, saved in '$OUT_DIR'."
-echo
-
-test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR"
+ }
+
+ if (stdin_file) {
+ # truncate input file
+ printf "" > stdin_file
+ close( stdin_file )
+ }
+
+ if (!ENVIRON["AFL_PATH"]) {
+ if (0 == system("test -f afl-cmin")) {
+ showmap = "./afl-showmap"
+ } else {
+ "which afl-showmap 2>/dev/null" | getline showmap
+ }
+ } else {
+ showmap = ENVIRON["AFL_PATH"] "/afl-showmap"
+ }
+
+ if (!showmap || 0 != system("test -x "showmap )) {
+ print "[-] Error: can't find 'afl-showmap' - please set AFL_PATH." > "/dev/stderr"
+ exit 1
+ }
+
+ # get list of input filenames sorted by size
+ i = 0
+ # yuck, gnu stat is option incompatible to bsd stat
+ # we use a heuristic to differentiate between
+ # GNU stat and other stats
+ "stat --version 2>/dev/null" | getline statversion
+ if (statversion ~ /GNU coreutils/) {
+ stat_format = "-c '%s %n'" # GNU
+ } else {
+ stat_format = "-f '%z %N'" # *BSD, MacOS
+ }
+ cmdline = "cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} \\; | sort -n | cut -d' ' -f2-"
+ while (cmdline | getline) {
+ infilesSmallToBig[i++] = $0
+ }
+ in_count = i
+
+ first_file = infilesSmallToBig[0]
+
+ # Make sure that we're not dealing with a directory.
-exit 0
+ if (0 == system("test -d "in_dir"/"first_file)) {
+ print "[-] Error: The input directory contains subdirectories - please fix." > "/dev/stderr"
+ exit 1
+ }
+
+ if (0 == system("ln "in_dir"/"first_file" "trace_dir"/.link_test")) {
+ cp_tool = "ln"
+ } else {
+ cp_tool = "cp"
+ }
+
+ # Make sure that we can actually get anything out of afl-showmap before we
+ # waste too much time.
+
+ print "[*] Testing the target binary..."
+
+ if (!stdin_file) {
+ system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -- \""target_bin"\" "prog_args_string" <\""in_dir"/"first_file"\"")
+ } else {
+ system("cp "in_dir"/"first_file" "stdin_file)
+ system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
+ }
+
+ first_count = 0
+
+ runtest = trace_dir"/.run_test"
+ while ((getline < runtest) > 0) {
+ ++first_count
+ }
+
+ if (first_count) {
+ print "[+] OK, "first_count" tuples recorded."
+ } else {
+ print "[-] Error: no instrumentation output detected (perhaps crash or timeout)." > "/dev/stderr"
+ if (!ENVIRON["AFL_KEEP_TRACES"]) {
+ system("rm -rf "trace_dir" 2>/dev/null")
+ }
+ exit 1
+ }
+
+ # Let's roll!
+
+ #############################
+ # STEP 1: Collecting traces #
+ #############################
+
+ print "[*] Obtaining traces for "in_count" input files in '"in_dir"'."
+
+ cur = 0;
+ if (!stdin_file) {
+ while (cur < in_count) {
+ fn = infilesSmallToBig[cur]
+ ++cur;
+ printf "\r Processing file "cur"/"in_count
+ system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/"fn"\" -Z "extra_par" -- \""target_bin"\" "prog_args_string" <\""in_dir"/"fn"\"")
+ }
+ } else {
+ while (cur < in_count) {
+ fn = infilesSmallToBig[cur]
+ ++cur
+ printf "\r Processing file "cur"/"in_count
+ system("cp "in_dir"/"fn" "stdin_file)
+ system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/"fn"\" -Z "extra_par" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
+ }
+ }
+
+ print ""
+
+
+ #######################################################
+ # STEP 2: register smallest input file for each tuple #
+ # STEP 3: copy that file (at most once) #
+ #######################################################
+
+ print "[*] Processing traces for input files in '"in_dir"'."
+
+ cur = 0
+ out_count = 0
+ tuple_count = 0
+
+ while (cur < in_count) {
+ fn = infilesSmallToBig[cur]
+ ++cur
+ printf "\r Processing file "cur"/"in_count
+ # create path for the trace file from afl-showmap
+ tracefile_path = trace_dir"/"fn
+ # gather all keys, and count them
+ while ((getline line < tracefile_path) > 0) {
+ key = line
+ if (!(key in key_count)) {
+ ++tuple_count
+ }
+ ++key_count[key]
+ if (! (key in best_file)) {
+ # this is the best file for this key
+ best_file[key] = fn
+ # copy file unless already done
+ if (! (fn in file_already_copied)) {
+ system(cp_tool" "in_dir"/"fn" "out_dir"/"fn)
+ file_already_copied[fn] = ""
+ ++out_count
+ }
+ }
+ }
+ close(tracefile_path)
+ }
+
+ print ""
+ print "[+] Found "tuple_count" unique tuples across "in_count" files."
+
+ if (out_count == 1) {
+ print "[!] WARNING: All test cases had the same traces, check syntax!"
+ }
+ print "[+] Narrowed down to "out_count" files, saved in '"out_dir"'."
+
+ if (!ENVIRON["AFL_KEEP_TRACES"]) {
+ system("rm -rf "trace_dir" 2>/dev/null")
+ }
+
+ exit 0
+}
+EOF
diff --git a/afl-cmin.bash b/afl-cmin.bash
new file mode 100755
index 00000000..1dd782d8
--- /dev/null
+++ b/afl-cmin.bash
@@ -0,0 +1,470 @@
+#!/usr/bin/env bash
+#
+# american fuzzy lop++ - corpus minimization tool
+# ---------------------------------------------
+#
+# Originally written by Michal Zalewski
+#
+# Copyright 2014, 2015 Google Inc. 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 tool tries to find the smallest subset of files in the input directory
+# that still trigger the full range of instrumentation data points seen in
+# the starting corpus. This has two uses:
+#
+# - Screening large corpora of input files before using them as a seed for
+# afl-fuzz. The tool will remove functionally redundant files and likely
+# leave you with a much smaller set.
+#
+# (In this case, you probably also want to consider running afl-tmin on
+# the individual files later on to reduce their size.)
+#
+# - Minimizing the corpus generated organically by afl-fuzz, perhaps when
+# planning to feed it to more resource-intensive tools. The tool achieves
+# this by removing all entries that used to trigger unique behaviors in the
+# past, but have been made obsolete by later finds.
+#
+# Note that the tool doesn't modify the files themselves. For that, you want
+# afl-tmin.
+#
+# This script must use bash because other shells may have hardcoded limits on
+# array sizes.
+#
+
+echo "corpus minimization tool for afl-fuzz by Michal Zalewski"
+echo
+
+#########
+# SETUP #
+#########
+
+# Process command-line options...
+
+MEM_LIMIT=200
+TIMEOUT=none
+
+unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
+ AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE
+
+while getopts "+i:o:f:m:t:eQUCh" opt; do
+
+ case "$opt" in
+
+ "h")
+ ;;
+
+ "i")
+ IN_DIR="$OPTARG"
+ ;;
+
+ "o")
+ OUT_DIR="$OPTARG"
+ ;;
+ "f")
+ STDIN_FILE="$OPTARG"
+ ;;
+ "m")
+ MEM_LIMIT="$OPTARG"
+ MEM_LIMIT_GIVEN=1
+ ;;
+ "t")
+ TIMEOUT="$OPTARG"
+ ;;
+ "e")
+ EXTRA_PAR="$EXTRA_PAR -e"
+ ;;
+ "C")
+ export AFL_CMIN_CRASHES_ONLY=1
+ ;;
+ "Q")
+ EXTRA_PAR="$EXTRA_PAR -Q"
+ test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250
+ QEMU_MODE=1
+ ;;
+ "U")
+ EXTRA_PAR="$EXTRA_PAR -U"
+ test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250
+ UNICORN_MODE=1
+ ;;
+ "?")
+ exit 1
+ ;;
+
+ esac
+
+done
+
+shift $((OPTIND-1))
+
+TARGET_BIN="$1"
+
+if [ "$TARGET_BIN" = "" -o "$IN_DIR" = "" -o "$OUT_DIR" = "" ]; then
+
+ cat 1>&2 <<_EOF_
+Usage: $0 [ options ] -- /path/to/target_app [ ... ]
+
+Required parameters:
+
+ -i dir - input directory with the starting corpus
+ -o dir - output directory for minimized files
+
+Execution control settings:
+
+ -f file - location read by the fuzzed program (stdin)
+ -m megs - memory limit for child process ($MEM_LIMIT MB)
+ -t msec - run time limit for child process (none)
+ -Q - use binary-only instrumentation (QEMU mode)
+ -U - use unicorn-based instrumentation (Unicorn mode)
+
+Minimization settings:
+
+ -C - keep crashing inputs, reject everything else
+ -e - solve for edge coverage only, ignore hit counts
+
+For additional tips, please consult docs/README.
+
+_EOF_
+ exit 1
+fi
+
+# Do a sanity check to discourage the use of /tmp, since we can't really
+# handle this safely from a shell script.
+
+if [ "$AFL_ALLOW_TMP" = "" ]; then
+
+ echo "$IN_DIR" | grep -qE '^(/var)?/tmp/'
+ T1="$?"
+
+ echo "$TARGET_BIN" | grep -qE '^(/var)?/tmp/'
+ T2="$?"
+
+ echo "$OUT_DIR" | grep -qE '^(/var)?/tmp/'
+ T3="$?"
+
+ echo "$STDIN_FILE" | grep -qE '^(/var)?/tmp/'
+ T4="$?"
+
+ echo "$PWD" | grep -qE '^(/var)?/tmp/'
+ T5="$?"
+
+ if [ "$T1" = "0" -o "$T2" = "0" -o "$T3" = "0" -o "$T4" = "0" -o "$T5" = "0" ]; then
+ echo "[-] Error: do not use this script in /tmp or /var/tmp." 1>&2
+ exit 1
+ fi
+
+fi
+
+# If @@ is specified, but there's no -f, let's come up with a temporary input
+# file name.
+
+TRACE_DIR="$OUT_DIR/.traces"
+
+if [ "$STDIN_FILE" = "" ]; then
+
+ if echo "$*" | grep -qF '@@'; then
+ STDIN_FILE="$TRACE_DIR/.cur_input"
+ fi
+
+fi
+
+# Check for obvious errors.
+
+if [ ! "$MEM_LIMIT" = "none" ]; then
+
+ if [ "$MEM_LIMIT" -lt "5" ]; then
+ echo "[-] Error: dangerously low memory limit." 1>&2
+ exit 1
+ fi
+
+fi
+
+if [ ! "$TIMEOUT" = "none" ]; then
+
+ if [ "$TIMEOUT" -lt "10" ]; then
+ echo "[-] Error: dangerously low timeout." 1>&2
+ exit 1
+ fi
+
+fi
+
+if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then
+
+ TNEW="`which "$TARGET_BIN" 2>/dev/null`"
+
+ if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then
+ echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2
+ exit 1
+ fi
+
+ TARGET_BIN="$TNEW"
+
+fi
+
+if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$UNICORN_MODE" = "" ]; then
+
+ if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then
+ echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2
+ exit 1
+ fi
+
+fi
+
+if [ ! -d "$IN_DIR" ]; then
+ echo "[-] Error: directory '$IN_DIR' not found." 1>&2
+ exit 1
+fi
+
+test -d "$IN_DIR/queue" && IN_DIR="$IN_DIR/queue"
+
+find "$OUT_DIR" -name 'id[:_]*' -maxdepth 1 -exec rm -- {} \; 2>/dev/null
+rm -rf "$TRACE_DIR" 2>/dev/null
+
+rmdir "$OUT_DIR" 2>/dev/null
+
+if [ -d "$OUT_DIR" ]; then
+ echo "[-] Error: directory '$OUT_DIR' exists and is not empty - delete it first." 1>&2
+ exit 1
+fi
+
+mkdir -m 700 -p "$TRACE_DIR" || exit 1
+
+if [ ! "$STDIN_FILE" = "" ]; then
+ rm -f "$STDIN_FILE" || exit 1
+ touch "$STDIN_FILE" || exit 1
+fi
+
+if [ "$AFL_PATH" = "" ]; then
+ SHOWMAP="${0%/afl-cmin}/afl-showmap"
+else
+ SHOWMAP="$AFL_PATH/afl-showmap"
+fi
+
+if [ ! -x "$SHOWMAP" ]; then
+ echo "[-] Error: can't find 'afl-showmap' - please set AFL_PATH." 1>&2
+ rm -rf "$TRACE_DIR"
+ exit 1
+fi
+
+IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`))
+
+if [ "$IN_COUNT" = "0" ]; then
+ echo "[+] Hmm, no inputs in the target directory. Nothing to be done."
+ rm -rf "$TRACE_DIR"
+ exit 1
+fi
+
+FIRST_FILE=`ls "$IN_DIR" | head -1`
+
+# Make sure that we're not dealing with a directory.
+
+if [ -d "$IN_DIR/$FIRST_FILE" ]; then
+ echo "[-] Error: The target directory contains subdirectories - please fix." 1>&2
+ rm -rf "$TRACE_DIR"
+ exit 1
+fi
+
+# Check for the more efficient way to copy files...
+
+if ln "$IN_DIR/$FIRST_FILE" "$TRACE_DIR/.link_test" 2>/dev/null; then
+ CP_TOOL=ln
+else
+ CP_TOOL=cp
+fi
+
+# Make sure that we can actually get anything out of afl-showmap before we
+# waste too much time.
+
+echo "[*] Testing the target binary..."
+
+if [ "$STDIN_FILE" = "" ]; then
+
+ AFL_CMIN_ALLOW_ANY=1 "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/.run_test" -Z $EXTRA_PAR -- "$@" <"$IN_DIR/$FIRST_FILE"
+
+else
+
+ cp "$IN_DIR/$FIRST_FILE" "$STDIN_FILE"
+ AFL_CMIN_ALLOW_ANY=1 "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/.run_test" -Z $EXTRA_PAR -A "$STDIN_FILE" -- "$@" </dev/null
+
+fi
+
+FIRST_COUNT=$((`grep -c . "$TRACE_DIR/.run_test"`))
+
+if [ "$FIRST_COUNT" -gt "0" ]; then
+
+ echo "[+] OK, $FIRST_COUNT tuples recorded."
+
+else
+
+ echo "[-] Error: no instrumentation output detected (perhaps crash or timeout)." 1>&2
+ test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR"
+ exit 1
+
+fi
+
+# Let's roll!
+
+#############################
+# STEP 1: COLLECTING TRACES #
+#############################
+
+echo "[*] Obtaining traces for input files in '$IN_DIR'..."
+
+(
+
+ CUR=0
+
+ if [ "$STDIN_FILE" = "" ]; then
+
+ ls "$IN_DIR" | while read -r fn; do
+
+ CUR=$((CUR+1))
+ printf "\\r Processing file $CUR/$IN_COUNT... "
+
+ "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -- "$@" <"$IN_DIR/$fn"
+
+ done
+
+ else
+
+ ls "$IN_DIR" | while read -r fn; do
+
+ CUR=$((CUR+1))
+ printf "\\r Processing file $CUR/$IN_COUNT... "
+
+ cp "$IN_DIR/$fn" "$STDIN_FILE"
+
+ "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -A "$STDIN_FILE" -- "$@" </dev/null
+
+ done
+
+
+ fi
+
+)
+
+echo
+
+##########################
+# STEP 2: SORTING TUPLES #
+##########################
+
+# With this out of the way, we sort all tuples by popularity across all
+# datasets. The reasoning here is that we won't be able to avoid the files
+# that trigger unique tuples anyway, so we will want to start with them and
+# see what's left.
+
+echo "[*] Sorting trace sets (this may take a while)..."
+
+ls "$IN_DIR" | sed "s#^#$TRACE_DIR/#" | tr '\n' '\0' | xargs -0 -n 1 cat | \
+ sort | uniq -c | sort -k 1,1 -n >"$TRACE_DIR/.all_uniq"
+
+TUPLE_COUNT=$((`grep -c . "$TRACE_DIR/.all_uniq"`))
+
+echo "[+] Found $TUPLE_COUNT unique tuples across $IN_COUNT files."
+
+#####################################
+# STEP 3: SELECTING CANDIDATE FILES #
+#####################################
+
+# The next step is to find the best candidate for each tuple. The "best"
+# part is understood simply as the smallest input that includes a particular
+# tuple in its trace. Empirical evidence suggests that this produces smaller
+# datasets than more involved algorithms that could be still pulled off in
+# a shell script.
+
+echo "[*] Finding best candidates for each tuple..."
+
+CUR=0
+
+ls -rS "$IN_DIR" | while read -r fn; do
+
+ CUR=$((CUR+1))
+ printf "\\r Processing file $CUR/$IN_COUNT... "
+
+ sed "s#\$# $fn#" "$TRACE_DIR/$fn" >>"$TRACE_DIR/.candidate_list"
+
+done
+
+echo
+
+##############################
+# STEP 4: LOADING CANDIDATES #
+##############################
+
+# At this point, we have a file of tuple-file pairs, sorted by file size
+# in ascending order (as a consequence of ls -rS). By doing sort keyed
+# only by tuple (-k 1,1) and configured to output only the first line for
+# every key (-s -u), we end up with the smallest file for each tuple.
+
+echo "[*] Sorting candidate list (be patient)..."
+
+sort -k1,1 -s -u "$TRACE_DIR/.candidate_list" | \
+ sed 's/^/BEST_FILE[/;s/ /]="/;s/$/"/' >"$TRACE_DIR/.candidate_script"
+
+if [ ! -s "$TRACE_DIR/.candidate_script" ]; then
+ echo "[-] Error: no traces obtained from test cases, check syntax!" 1>&2
+ test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR"
+ exit 1
+fi
+
+# The sed command converted the sorted list to a shell script that populates
+# BEST_FILE[tuple]="fname". Let's load that!
+
+. "$TRACE_DIR/.candidate_script"
+
+##########################
+# STEP 5: WRITING OUTPUT #
+##########################
+
+# The final trick is to grab the top pick for each tuple, unless said tuple is
+# already set due to the inclusion of an earlier candidate; and then put all
+# tuples associated with the newly-added file to the "already have" list. The
+# loop works from least popular tuples and toward the most common ones.
+
+echo "[*] Processing candidates and writing output files..."
+
+CUR=0
+
+touch "$TRACE_DIR/.already_have"
+
+while read -r cnt tuple; do
+
+ CUR=$((CUR+1))
+ printf "\\r Processing tuple $CUR/$TUPLE_COUNT... "
+
+ # If we already have this tuple, skip it.
+
+ grep -q "^$tuple\$" "$TRACE_DIR/.already_have" && continue
+
+ FN=${BEST_FILE[tuple]}
+
+ $CP_TOOL "$IN_DIR/$FN" "$OUT_DIR/$FN"
+
+ if [ "$((CUR % 5))" = "0" ]; then
+ sort -u "$TRACE_DIR/$FN" "$TRACE_DIR/.already_have" >"$TRACE_DIR/.tmp"
+ mv -f "$TRACE_DIR/.tmp" "$TRACE_DIR/.already_have"
+ else
+ cat "$TRACE_DIR/$FN" >>"$TRACE_DIR/.already_have"
+ fi
+
+done <"$TRACE_DIR/.all_uniq"
+
+echo
+
+OUT_COUNT=`ls -- "$OUT_DIR" | wc -l`
+
+if [ "$OUT_COUNT" = "1" ]; then
+ echo "[!] WARNING: All test cases had the same traces, check syntax!"
+fi
+
+echo "[+] Narrowed down to $OUT_COUNT files, saved in '$OUT_DIR'."
+echo
+
+test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR"
+
+exit 0
diff --git a/afl-system-config b/afl-system-config
index 2a7df17f..1e180d8b 100755
--- a/afl-system-config
+++ b/afl-system-config
@@ -1,6 +1,6 @@
#!/bin/sh
test "$1" = "-h" && {
- echo afl-system-config by Marc Heuse
+ echo 'afl-system-config by Marc Heuse <mh@mh-sec.de>'
echo
echo $0
echo
@@ -12,55 +12,72 @@ test "$1" = "-h" && {
exit 1
}
+DONE=
PLATFORM=`uname -s`
-echo This reconfigures the system to have a better fuzzing performance
+echo This reconfigures the system to have a better fuzzing performance.
if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
- echo Error you need to be root to run this
- exit 1
+ echo "Warning: you need to be root to run this!"
+ # we do not exit as other mechanisms exist that allows to do this than
+ # being root. let the errors speak for themselves.
fi
if [ "$PLATFORM" = "Linux" ] ; then
-sysctl -w kernel.core_pattern=core
-sysctl -w kernel.randomize_va_space=0
-sysctl -w kernel.sched_child_runs_first=1
-sysctl -w kernel.sched_autogroup_enabled=1
-sysctl -w kernel.sched_migration_cost_ns=50000000
-sysctl -w kernel.sched_latency_ns=250000000
-echo never > /sys/kernel/mm/transparent_hugepage/enabled
-test -e /sys/devices/system/cpu/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/scaling_governor
-test -e /sys/devices/system/cpu/cpufreq/policy0/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/policy*/scaling_governor
-test -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
-test -e /sys/devices/system/cpu/intel_pstate/no_turbo && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
-test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost
-echo
-echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
-echo '/etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"'
+{
+ sysctl -w kernel.core_pattern=core
+ sysctl -w kernel.randomize_va_space=0
+ sysctl -w kernel.sched_child_runs_first=1
+ sysctl -w kernel.sched_autogroup_enabled=1
+ sysctl -w kernel.sched_migration_cost_ns=50000000
+ sysctl -w kernel.sched_latency_ns=250000000
+ echo never > /sys/kernel/mm/transparent_hugepage/enabled
+ test -e /sys/devices/system/cpu/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/scaling_governor
+ test -e /sys/devices/system/cpu/cpufreq/policy0/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/policy*/scaling_governor
+ test -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
+ test -e /sys/devices/system/cpu/intel_pstate/no_turbo && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
+ test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost
+} > /dev/null
+ echo Settings applied.
+ dmesg | egrep -q 'nospectre_v2|spectre_v2=off' || {
+ echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
+ echo ' /etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"'
+ }
+ DONE=1
fi
if [ "$PLATFORM" = "FreeBSD" ] ; then
-sysctl kern.elf32.aslr.enable=0
-sysctl kern.elf64.aslr.enable=0
-echo
-echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
-echo 'sysctl hw.ibrs_disable=1'
-echo
-echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
+{
+ sysctl kern.elf32.aslr.enable=0
+ sysctl kern.elf64.aslr.enable=0
+} > /dev/null
+ echo Settings applied.
+ echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
+ echo ' sysctl hw.ibrs_disable=1'
+ echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
+ DONE=1
fi
if [ "$PLATFORM" = "OpenBSD" ] ; then
-echo
-echo 'System security features cannot be disabled on OpenBSD.'
+ echo
+ echo 'System security features cannot be disabled on OpenBSD.'
+ DONE=1
fi
if [ "$PLATFORM" = "NetBSD" ] ; then
-echo
-echo It is recommended to enable unprivileged users to set cpu affinity
-echo to be able to use afl-gotcpu meaningfully.
-/sbin/sysctl -w security.models.extensions.user_set_cpu_affinity=1
+{
+ #echo It is recommended to enable unprivileged users to set cpu affinity
+ #echo to be able to use afl-gotcpu meaningfully.
+ /sbin/sysctl -w security.models.extensions.user_set_cpu_affinity=1
+} > /dev/null
+ echo Settings applied.
+ DONE=1
fi
if [ "$PLATFORM" = "Darwin" ] ; then
if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then
-echo We unload the default crash reporter here
-SL=/System/Library; PL=com.apple.ReportCrash
-launchctl unload -w ${SL}/LaunchAgents/${PL}.plist
-sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist
+ echo We unload the default crash reporter here
+ SL=/System/Library; PL=com.apple.ReportCrash
+ launchctl unload -w ${SL}/LaunchAgents/${PL}.plist
+ sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist
+ echo Settings applied.
+ else
+ echo Nothing to do.
fi
+ DONE=1
fi
-echo
-echo Also use AFL_TMPDIR to use a tmpfs for the input file
+test -z "$DONE" && echo Error: Unknown platform: $PLATFORM
+test -z "$AFL_TMPDIR" && echo Also use AFL_TMPDIR and point it to a tmpfs for the input file caching
diff --git a/docs/ChangeLog b/docs/ChangeLog
index 5347d244..5017a803 100644
--- a/docs/ChangeLog
+++ b/docs/ChangeLog
@@ -21,9 +21,15 @@ Version ++2.60d (develop):
- afl-fuzz:
- now prints the real python version support compiled in
- set stronger performance compile options and little tweaks
- - afl-clang-fast now shows in the help output for which llvm version it
- was compiled for
- - added blacklisted function check in llvm_mode
+ - Android: prefer bigcores when selecting a CPU
+ - afl-clang-fast:
+ - show in the help output for which llvm version it was compiled for
+ - now does not need to be recompiled between trace-pc and pass
+ instrumentation. compile normally and set AFL_LLVM_USE_TRACE_PC :)
+ - llvm 11 is supported
+ - afl-cmin is now a sh script (invoking awk) instead of bash for portability
+ the original script is still present as afl-cmin.bash
+ - added blacklist and whitelisting function check in all modules of llvm_mode
- added fix from Debian project to compile libdislocator and libtokencap
diff --git a/docs/binaryonly_fuzzing.md b/docs/binaryonly_fuzzing.md
new file mode 100644
index 00000000..6eff30d7
--- /dev/null
+++ b/docs/binaryonly_fuzzing.md
@@ -0,0 +1,161 @@
+# Fuzzing binary-only programs with afl++
+
+ afl++, libfuzzer and others are great if you have the source code, and
+ it allows for very fast and coverage guided fuzzing.
+
+ However, if there is only the binary program and no source code available,
+ then standard `afl-fuzz -n` (dumb mode) is not effective.
+
+ The following is a description of how these binaries can be fuzzed with afl++
+
+ !!!!!
+ TL;DR: try DYNINST with afl-dyninst. If it produces too many crashes then
+ use afl -Q qemu_mode, or better: use both in parallel.
+ !!!!!
+
+
+## QEMU
+
+ Qemu is the "native" solution to the program.
+ It is available in the ./qemu_mode/ directory and once compiled it can
+ be accessed by the afl-fuzz -Q command line option.
+ The speed decrease is at about 50%.
+ It is the easiest to use alternative and even works for cross-platform binaries.
+
+ Note that there is also honggfuzz: [https://github.com/google/honggfuzz](https://github.com/google/honggfuzz)
+ which now has a qemu_mode, but its performance is just 1.5%!
+
+ As it is included in afl++ this needs no URL.
+
+
+## WINE+QEMU
+
+ Wine mode can run Win32 PE binaries with the QEMU instrumentation.
+ It needs Wine, python3 and the pefile python package installed.
+
+ As it is included in afl++ this needs no URL.
+
+
+## UNICORN
+
+ Unicorn is a fork of QEMU. The instrumentation is, therefore, very similar.
+ In contrast to QEMU, Unicorn does not offer a full system or even userland
+ emulation. Runtime environment and/or loaders have to be written from scratch,
+ if needed. On top, block chaining has been removed. This means the speed boost
+ introduced in the patched QEMU Mode of afl++ cannot simply be ported over to
+ Unicorn. For further information, check out ./unicorn_mode.txt.
+
+ As it is included in afl++ this needs no URL.
+
+
+## DYNINST
+
+ Dyninst is a binary instrumentation framework similar to Pintool and
+ Dynamorio (see far below). However whereas Pintool and Dynamorio work at
+ runtime, dyninst instruments the target at load time, and then let it run -
+ or save the binary with the changes.
+ This is great for some things, e.g. fuzzing, and not so effective for others,
+ e.g. malware analysis.
+
+ So what we can do with dyninst is taking every basic block, and put afl's
+ instrumention code in there - and then save the binary.
+ Afterwards we can just fuzz the newly saved target binary with afl-fuzz.
+ Sounds great? It is. The issue though - it is a non-trivial problem to
+ insert instructions, which change addresses in the process space, so that
+ everything is still working afterwards. Hence more often than not binaries
+ crash when they are run.
+
+ The speed decrease is about 15-35%, depending on the optimization options
+ used with afl-dyninst.
+
+ So if Dyninst works, it is the best option available. Otherwise it just
+ doesn't work well.
+
+ [https://github.com/vanhauser-thc/afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst)
+
+
+## INTEL-PT
+
+ If you have a newer Intel CPU, you can make use of Intels processor trace.
+ The big issue with Intel's PT is the small buffer size and the complex
+ encoding of the debug information collected through PT.
+ This makes the decoding very CPU intensive and hence slow.
+ As a result, the overall speed decrease is about 70-90% (depending on
+ the implementation and other factors).
+
+ There are two afl intel-pt implementations:
+
+ 1. [https://github.com/junxzm1990/afl-pt](https://github.com/junxzm1990/afl-pt)
+ => this needs Ubuntu 14.04.05 without any updates and the 4.4 kernel.
+
+ 2. [https://github.com/hunter-ht-2018/ptfuzzer](https://github.com/hunter-ht-2018/ptfuzzer)
+ => this needs a 4.14 or 4.15 kernel. the "nopti" kernel boot option must
+ be used. This one is faster than the other.
+
+ Note that there is also honggfuzz: https://github.com/google/honggfuzz
+ But its IPT performance is just 6%!
+
+
+## CORESIGHT
+
+ Coresight is ARM's answer to Intel's PT.
+ There is no implementation so far which handle coresight and getting
+ it working on an ARM Linux is very difficult due to custom kernel building
+ on embedded systems is difficult. And finding one that has coresight in
+ the ARM chip is difficult too.
+ My guess is that it is slower than Qemu, but faster than Intel PT.
+
+ If anyone finds any coresight implementation for afl please ping me: vh@thc.org
+
+
+## FRIDA
+
+ Frida is a dynamic instrumentation engine like Pintool, Dyninst and Dynamorio.
+ What is special is that it is written Python, and scripted with Javascript.
+ It is mostly used to reverse binaries on mobile phones however can be used
+ everywhere.
+
+ There is a WIP fuzzer available at [https://github.com/andreafioraldi/frida-fuzzer](https://github.com/andreafioraldi/frida-fuzzer)
+
+
+## PIN & DYNAMORIO
+
+ Pintool and Dynamorio are dynamic instrumentation engines, and they can be
+ used for getting basic block information at runtime.
+ Pintool is only available for Intel x32/x64 on Linux, Mac OS and Windows
+ whereas Dynamorio is additionally available for ARM and AARCH64.
+ Dynamorio is also 10x faster than Pintool.
+
+ The big issue with Dynamorio (and therefore Pintool too) is speed.
+ Dynamorio has a speed decrease of 98-99%
+ Pintool has a speed decrease of 99.5%
+
+ Hence Dynamorio is the option to go for if everything fails, and Pintool
+ only if Dynamorio fails too.
+
+ Dynamorio solutions:
+ * [https://github.com/vanhauser-thc/afl-dynamorio](https://github.com/vanhauser-thc/afl-dynamorio)
+ * [https://github.com/mxmssh/drAFL](https://github.com/mxmssh/drAFL)
+ * [https://github.com/googleprojectzero/winafl/](https://github.com/googleprojectzero/winafl/) <= very good but windows only
+
+ Pintool solutions:
+ * [https://github.com/vanhauser-thc/afl-pin](https://github.com/vanhauser-thc/afl-pin)
+ * [https://github.com/mothran/aflpin](https://github.com/mothran/aflpin)
+ * [https://github.com/spinpx/afl_pin_mode](https://github.com/spinpx/afl_pin_mode) <= only old Pintool version supported
+
+
+## Non-AFL solutions
+
+ There are many binary-only fuzzing frameworks.
+ Some are great for CTFs but don't work with large binaries, others are very
+ slow but have good path discovery, some are very hard to set-up ...
+
+ * QSYM: [https://github.com/sslab-gatech/qsym](https://github.com/sslab-gatech/qsym)
+ * Manticore: [https://github.com/trailofbits/manticore](https://github.com/trailofbits/manticore)
+ * S2E: [https://github.com/S2E](https://github.com/S2E)
+ * ... please send me any missing that are good
+
+
+## Closing words
+
+ That's it! News, corrections, updates? Send an email to vh@thc.org
diff --git a/docs/binaryonly_fuzzing.txt b/docs/binaryonly_fuzzing.txt
deleted file mode 100644
index 239fb4b0..00000000
--- a/docs/binaryonly_fuzzing.txt
+++ /dev/null
@@ -1,144 +0,0 @@
-
-Fuzzing binary-only programs with afl++
-=======================================
-
-afl++, libfuzzer and others are great if you have the source code, and
-it allows for very fast and coverage guided fuzzing.
-
-However, if there is only the binary program and not source code available,
-then standard afl++ (dumb mode) is not effective.
-
-The following is a description of how these can be fuzzed with afl++
-
-!!!!!
-TL;DR: try DYNINST with afl-dyninst. If it produces too many crashes then
- use afl -Q qemu_mode, or better: use both in parallel.
-!!!!!
-
-
-QEMU
-----
-Qemu is the "native" solution to the program.
-It is available in the ./qemu_mode/ directory and once compiled it can
-be accessed by the afl-fuzz -Q command line option.
-The speed decrease is at about 50%
-It is the easiest to use alternative and even works for cross-platform binaries.
-
-As it is included in afl++ this needs no URL.
-
-WINE+QEMU
----------
-Wine mode can run Win32 PE with the QEMU instrumentation.
-It needs Wine, python3 and the pefile python package installed.
-
-UNICORN
--------
-Unicorn is a fork of QEMU. The instrumentation is, therefore, very similar.
-In contrast to QEMU, Unicorn does not offer a full system or even userland emulation.
-Runtime environment and/or loaders have to be written from scratch, if needed.
-On top, block chaining has been removed. This means the speed boost introduced in
-to the patched QEMU Mode of afl++ cannot simply be ported over to Unicorn.
-For further information, check out ./unicorn_mode.txt.
-
-
-DYNINST
--------
-Dyninst is a binary instrumentation framework similar to Pintool and Dynamorio
-(see far below). However whereas Pintool and Dynamorio work at runtime, dyninst
-instruments the target at load time, and then let it run.
-This is great for some things, e.g. fuzzing, and not so effective for others,
-e.g. malware analysis.
-
-So what we can do with dyninst is taking every basic block, and put afl's
-instrumention code in there - and then save the binary.
-Afterwards we can just fuzz the newly saved target binary with afl-fuzz.
-Sounds great? It is. The issue though - it is a non-trivial problem to
-insert instructions, which change addresses in the process space, so
-everything is still working afterwards. Hence more often than not binaries
-crash when they are run (because of instrumentation).
-
-The speed decrease is about 15-35%, depending on the optimization options
-used with afl-dyninst.
-
-So if dyninst works, it is the best option available. Otherwise it just doesn't
-work well.
-
-https://github.com/vanhauser-thc/afl-dyninst
-
-
-INTEL-PT
---------
-If you have a newer Intel CPU, you can make use of Intels processor trace.
-The big issue with Intel's PT is the small buffer size and the complex
-encoding of the debug information collected through PT.
-This makes the decoding very CPU intensive and hence slow.
-As a result, the overall speed decrease is about 70-90% (depending on
-the implementation and other factors).
-
-There are two afl intel-pt implementations:
-
-1. https://github.com/junxzm1990/afl-pt
- => this needs Ubuntu 14.04.05 without any updates and the 4.4 kernel.
-
-2. https://github.com/hunter-ht-2018/ptfuzzer
- => this needs a 4.14 or 4.15 kernel. the "nopti" kernel boot option must
- be used. This one is faster than the other.
-
-
-CORESIGHT
----------
-
-Coresight is ARM's answer to Intel's PT.
-There is no implementation so far which handle coresight and getting
-it working on an ARM Linux is very difficult due to custom kernel building
-on embedded systems is difficult. And finding one that has coresight in
-the ARM chip is difficult too.
-My guess is that it is slower than Qemu, but faster than Intel PT.
-If anyone finds any coresight implementation for afl please ping me:
-vh@thc.org
-
-
-PIN & DYNAMORIO
----------------
-
-Pintool and Dynamorio are dynamic instrumentation engines, and they can be
-used for getting basic block information at runtime.
-Pintool is only available for Intel x32/x64 on Linux, Mac OS and Windows
-whereas Dynamorio is additionally available for ARM and AARCH64.
-Dynamorio is also 10x faster than Pintool.
-
-The big issue with Dynamorio (and therefore Pintool too) is speed.
-Dynamorio has a speed decrease of 98-99%
-Pintool has a speed decrease of 99.5%
-
-Hence Dynamorio is the option to go for if everything fails, and Pintool
-only if Dynamorio fails too.
-
-Dynamorio solutions:
- https://github.com/vanhauser-thc/afl-dynamorio
- https://github.com/mxmssh/drAFL
- https://github.com/googleprojectzero/winafl/ <= very good but windows only
-
-Pintool solutions:
- https://github.com/vanhauser-thc/afl-pin
- https://github.com/mothran/aflpin
- https://github.com/spinpx/afl_pin_mode <= only old Pintool version supported
-
-
-Non-AFL solutions
------------------
-
-There are many binary-only fuzzing frameworks. Some are great for CTFs but don't
-work with large binaries, others are very slow but have good path discovery,
-some are very hard to set-up ...
-
-QSYM: https://github.com/sslab-gatech/qsym
-Manticore: https://github.com/trailofbits/manticore
-S2E: https://github.com/S2E
-<please send me any missing that are good>
-
-
-
-That's it!
-News, corrections, updates?
-Email vh@thc.org
diff --git a/experimental/README.experiments b/experimental/README.experiments
index af9739bd..543c078c 100644
--- a/experimental/README.experiments
+++ b/experimental/README.experiments
@@ -28,6 +28,9 @@ Here's a quick overview of the stuff you can find in this directory:
mode to speed up certain fuzzing jobs.
- post_library - an example of how to build postprocessors for AFL.
+
+ - socket_fuzzing - a LD_PRELOAD library 'redirects' a socket to stdin
+ for fuzzing access with afl++
Note that the minimize_corpus.sh tool has graduated from the experimental/
directory and is now available as ../afl-cmin. The LLVM mode has likewise
diff --git a/include/afl-as.h b/include/afl-as.h
index 3af42205..bd5e734a 100644
--- a/include/afl-as.h
+++ b/include/afl-as.h
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index cd53c703..967e16fe 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/include/alloc-inl.h b/include/alloc-inl.h
index 48598ed3..5592b295 100644
--- a/include/alloc-inl.h
+++ b/include/alloc-inl.h
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/include/android-ashmem.h b/include/android-ashmem.h
index 35a5ba5e..adddc05f 100755
--- a/include/android-ashmem.h
+++ b/include/android-ashmem.h
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/include/common.h b/include/common.h
index 8ab78b41..3b953470 100644
--- a/include/common.h
+++ b/include/common.h
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/include/config.h b/include/config.h
index c5a48df0..8b8924f5 100644
--- a/include/config.h
+++ b/include/config.h
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
@@ -67,7 +67,8 @@
#else
#define MEM_LIMIT 50
#endif /* ^!WORD_SIZE_64 */
-#else
+#else /* NetBSD's kernel needs more space for stack, see discussion for issue \
+ #165 */
#define MEM_LIMIT 200
#endif
/* Default memory limit when running in QEMU mode (MB): */
diff --git a/include/debug.h b/include/debug.h
index 68109927..d6c04935 100644
--- a/include/debug.h
+++ b/include/debug.h
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/include/forkserver.h b/include/forkserver.h
index 17bc65af..0fdcba48 100644
--- a/include/forkserver.h
+++ b/include/forkserver.h
@@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com>
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/include/sharedmem.h b/include/sharedmem.h
index 3540386d..f92fd8be 100644
--- a/include/sharedmem.h
+++ b/include/sharedmem.h
@@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com>
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/include/types.h b/include/types.h
index d5be5920..6aad9762 100644
--- a/include/types.h
+++ b/include/types.h
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/libdislocator/libdislocator.so.c b/libdislocator/libdislocator.so.c
index 20649470..221a629b 100644
--- a/libdislocator/libdislocator.so.c
+++ b/libdislocator/libdislocator.so.c
@@ -397,6 +397,29 @@ void* aligned_alloc(size_t align, size_t len) {
}
+/* specific BSD api mainly checking possible overflow for the size */
+
+void* reallocarray(void* ptr, size_t elem_len, size_t elem_cnt) {
+
+ const size_t elem_lim = 1UL << (sizeof(size_t) * 4);
+ const size_t elem_tot = elem_len * elem_cnt;
+ void* ret = NULL;
+
+ if ((elem_len >= elem_lim || elem_cnt >= elem_lim) && elem_len > 0 &&
+ elem_cnt > (SIZE_MAX / elem_len)) {
+
+ DEBUGF("reallocarray size overflow (%zu)", elem_tot);
+
+ } else {
+
+ ret = realloc(ptr, elem_tot);
+
+ }
+
+ return ret;
+
+}
+
__attribute__((constructor)) void __dislocator_init(void) {
u8* tmp = (u8*)getenv("AFL_LD_LIMIT_MB");
diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc
index 11451b43..5b7b79e1 100644
--- a/llvm_mode/LLVMInsTrim.so.cc
+++ b/llvm_mode/LLVMInsTrim.so.cc
@@ -3,10 +3,23 @@
#include <stdarg.h>
#include <unistd.h>
+#include "llvm/Config/llvm-config.h"
+#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5
+typedef long double max_align_t;
+#endif
+
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
#include "llvm/IR/CFG.h"
#include "llvm/IR/Dominators.h"
+#include "llvm/IR/DebugInfo.h"
+#else
+#include "llvm/Support/CFG.h"
+#include "llvm/Analysis/Dominators.h"
+#include "llvm/DebugInfo.h"
+#endif
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
@@ -16,9 +29,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
-#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/BasicBlock.h"
-#include "llvm/IR/CFG.h"
#include <unordered_set>
#include <random>
#include <list>
@@ -97,7 +108,7 @@ struct InsTrim : public ModulePass {
// ripped from aflgo
static bool isBlacklisted(const Function *F) {
- static const SmallVector<std::string, 4> Blacklist = {
+ static const char *Blacklist[] = {
"asan.",
"llvm.",
@@ -144,19 +155,6 @@ struct InsTrim : public ModulePass {
// this is our default
MarkSetOpt = true;
- /* // I dont think this makes sense to port into LLVMInsTrim
- char* inst_ratio_str = getenv("AFL_INST_RATIO");
- unsigned int inst_ratio = 100;
- if (inst_ratio_str) {
-
- if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio ||
- inst_ratio > 100) FATAL("Bad value of AFL_INST_RATIO (must be between 1
- and 100)");
-
- }
-
- */
-
LLVMContext &C = M.getContext();
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
@@ -186,6 +184,8 @@ struct InsTrim : public ModulePass {
StringRef instFilename;
unsigned int instLine = 0;
+#if LLVM_VERSION_MAJOR >= 4 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
for (auto &BB : F) {
BasicBlock::iterator IP = BB.getFirstInsertionPt();
@@ -240,6 +240,48 @@ struct InsTrim : public ModulePass {
}
+#else
+ for (auto &BB : F) {
+
+ BasicBlock::iterator IP = BB.getFirstInsertionPt();
+ IRBuilder<> IRB(&(*IP));
+ if (Loc.isUnknown()) Loc = IP->getDebugLoc();
+
+ }
+
+ if (!Loc.isUnknown()) {
+
+ DILocation cDILoc(Loc.getAsMDNode(C));
+
+ instLine = cDILoc.getLineNumber();
+ instFilename = cDILoc.getFilename();
+
+ /* Continue only if we know where we actually are */
+ if (!instFilename.str().empty()) {
+
+ for (std::list<std::string>::iterator it = myWhitelist.begin();
+ it != myWhitelist.end(); ++it) {
+
+ if (instFilename.str().length() >= it->length()) {
+
+ if (instFilename.str().compare(
+ instFilename.str().length() - it->length(),
+ it->length(), *it) == 0) {
+
+ instrumentBlock = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+#endif
/* Either we couldn't figure out our location or the location is
* not whitelisted, so we skip instrumentation. */
if (!instrumentBlock) {
@@ -432,28 +474,19 @@ struct InsTrim : public ModulePass {
IRB.CreateStore(Incr, MapPtrIdx)
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
- /* Set prev_loc to cur_loc >> 1 */
- /*
- StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, L >> 1),
- OldPrev); Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C,
- None));
- */
-
total_instr++;
}
}
- OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n" /*", ratio
- %u%%)."*/
- ,
- total_instr, total_rs, total_hs,
+ OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n", total_instr,
+ total_rs, total_hs,
getenv("AFL_HARDEN")
? "hardened"
: ((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN"))
? "ASAN/MSAN"
- : "non-hardened") /*, inst_ratio*/);
+ : "non-hardened"));
return false;
}
diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile
index 9ee6fc8b..6e0a27af 100644
--- a/llvm_mode/Makefile
+++ b/llvm_mode/Makefile
@@ -29,14 +29,14 @@ 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.8.0 up to 10) -> e.g. "pkg_add llvm-7.0.1p9")
+ $(error llvm_mode needs a complete llvm installation (versions 3.8.0 up to 11) -> e.g. "pkg_add llvm-7.0.1p9")
endif
else
LLVM_CONFIG ?= llvm-config
endif
LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null )
-LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-7]|^1[1-9]' && echo 1 || echo 0 )
+LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-7]|^1[2-9]' && echo 1 || echo 0 )
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-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)
@@ -48,7 +48,7 @@ ifeq "$(LLVMVER)" ""
endif
ifeq "$(LLVM_UNSUPPORTED)" "1"
- $(warning llvm_mode only supports llvm versions 3.8.0 up to 10)
+ $(warning llvm_mode only supports llvm versions 3.8.0 up to 11)
endif
ifeq "$(LLVM_MAJOR)" "9"
@@ -201,7 +201,7 @@ endif
ln -sf afl-clang-fast ../afl-clang-fast++
../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc | test_deps
- $(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL)
+ -$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL)
../afl-llvm-pass.so: afl-llvm-pass.so.cc | test_deps
$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL)
diff --git a/llvm_mode/MarkNodes.cc b/llvm_mode/MarkNodes.cc
index 2aeeda8d..7b22bac0 100644
--- a/llvm_mode/MarkNodes.cc
+++ b/llvm_mode/MarkNodes.cc
@@ -3,11 +3,22 @@
#include <queue>
#include <set>
#include <vector>
+
+#include "llvm/Config/llvm-config.h"
+#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5
+typedef long double max_align_t;
+#endif
+
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/BasicBlock.h"
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
#include "llvm/IR/CFG.h"
+#else
+#include "llvm/Support/CFG.h"
+#endif
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
@@ -65,16 +76,11 @@ void buildCFG(Function *F) {
}
- // uint32_t FakeID = 0;
for (auto S = F->begin(), E = F->end(); S != E; ++S) {
BasicBlock *BB = &*S;
uint32_t MyID = LMap[BB];
- // if (succ_begin(BB) == succ_end(BB)) {
- // Succs[MyID].push_back(FakeID);
- // Marked.insert(MyID);
- //}
for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) {
Succs[MyID].push_back(LMap[*I]);
@@ -113,7 +119,7 @@ void DFStree(size_t now_id) {
}
-void turnCFGintoDAG(Function *F) {
+void turnCFGintoDAG() {
tSuccs = Succs;
tag.resize(Blocks.size());
@@ -176,7 +182,7 @@ void DFS(uint32_t now) {
}
-void DominatorTree(Function *F) {
+void DominatorTree() {
if (Blocks.empty()) return;
uint32_t s = start_point;
@@ -390,7 +396,7 @@ void MarkSubGraph(uint32_t ss, uint32_t tt) {
}
-void MarkVertice(Function *F) {
+void MarkVertice() {
uint32_t s = start_point;
@@ -411,8 +417,6 @@ void MarkVertice(Function *F) {
timeStamp = 0;
uint32_t t = 0;
- // MarkSubGraph(s, t);
- // return;
while (s != t) {
@@ -432,9 +436,9 @@ std::pair<std::vector<BasicBlock *>, std::vector<BasicBlock *> > markNodes(
reset();
labelEachBlock(F);
buildCFG(F);
- turnCFGintoDAG(F);
- DominatorTree::DominatorTree(F);
- MarkVertice(F);
+ turnCFGintoDAG();
+ DominatorTree::DominatorTree();
+ MarkVertice();
std::vector<BasicBlock *> Result, ResultAbove;
for (uint32_t x : Markabove) {
diff --git a/llvm_mode/README.md b/llvm_mode/README.md
index 5afa4dfd..54788aba 100644
--- a/llvm_mode/README.md
+++ b/llvm_mode/README.md
@@ -5,7 +5,7 @@
## 1) Introduction
-! llvm_mode works with llvm versions 3.8.0 up to 10 !
+! llvm_mode works with llvm versions 3.8.0 up to 11 !
The code in this directory allows you to instrument programs for AFL using
true compiler-level instrumentation, instead of the more crude
@@ -198,24 +198,23 @@ PS. Because there are task switches still involved, the mode isn't as fast as
faster than the normal fork() model, and compared to in-process fuzzing,
should be a lot more robust.
-## 8) Bonus feature #3: new 'trace-pc-guard' mode
+## 8) Bonus feature #3: 'trace-pc-guard' mode
-Recent versions of LLVM are shipping with a built-in execution tracing feature
+LLVM is shipping with a built-in execution tracing feature
that provides AFL with the necessary tracing data without the need to
post-process the assembly or install any compiler plugins. See:
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
-If you have a sufficiently recent compiler and want to give it a try, build
-afl-clang-fast this way:
+If you have not an outdated compiler and want to give it a try, build
+targets this way:
```
- AFL_TRACE_PC=1 make clean all
+ libtarget-1.0 $ AFL_LLVM_USE_TRACE_PC=1 make
```
-Note that this mode is currently about 20% slower than "vanilla" afl-clang-fast,
+Note that this mode is about 20% slower than "vanilla" afl-clang-fast,
and about 5-10% slower than afl-clang. This is likely because the
-instrumentation is not inlined, and instead involves a function call. On systems
-that support it, compiling your target with -flto should help.
-
-
+instrumentation is not inlined, and instead involves a function call.
+On systems that support it, compiling your target with -flto can help
+a bit.
diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c
index ddaa2388..4fbaf9ec 100644
--- a/llvm_mode/afl-clang-fast.c
+++ b/llvm_mode/afl-clang-fast.c
@@ -212,13 +212,24 @@ static void edit_params(u32 argc, char** argv) {
// "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
// cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
#else
- cc_params[cc_par_cnt++] = "-Xclang";
- cc_params[cc_par_cnt++] = "-load";
- cc_params[cc_par_cnt++] = "-Xclang";
- if (getenv("AFL_LLVM_INSTRIM") != NULL || getenv("INSTRIM_LIB") != NULL)
- cc_params[cc_par_cnt++] = alloc_printf("%s/libLLVMInsTrim.so", obj_path);
- else
- cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
+ if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
+ getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) {
+
+ cc_params[cc_par_cnt++] =
+ "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
+
+ } else {
+
+ cc_params[cc_par_cnt++] = "-Xclang";
+ cc_params[cc_par_cnt++] = "-load";
+ cc_params[cc_par_cnt++] = "-Xclang";
+ if (getenv("AFL_LLVM_INSTRIM") != NULL || getenv("INSTRIM_LIB") != NULL)
+ cc_params[cc_par_cnt++] = alloc_printf("%s/libLLVMInsTrim.so", obj_path);
+ else
+ cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
+
+ }
+
#endif /* ^USE_TRACE_PC */
}
@@ -292,8 +303,10 @@ static void edit_params(u32 argc, char** argv) {
#ifdef USE_TRACE_PC
- if (getenv("AFL_INST_RATIO"))
- FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'.");
+ if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
+ getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC"))
+ if (getenv("AFL_INST_RATIO"))
+ FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'.");
#endif /* USE_TRACE_PC */
@@ -474,6 +487,8 @@ int main(int argc, char** argv) {
#ifdef USE_TRACE_PC
SAYF(cCYA "afl-clang-fast" VERSION cRST
" [tpcg] by <lszekeres@google.com>\n");
+#warning \
+ "You do not need to specifically compile with USE_TRACE_PC anymore, setting the environment variable AFL_LLVM_USE_TRACE_PC is enough."
#else
SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n");
#endif /* ^USE_TRACE_PC */
diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc
index 15cc6127..2cd23adf 100644
--- a/llvm_mode/afl-llvm-pass.so.cc
+++ b/llvm_mode/afl-llvm-pass.so.cc
@@ -37,14 +37,26 @@
#include <fstream>
#include <sys/time.h>
-#include "llvm/IR/DebugInfo.h"
-#include "llvm/IR/BasicBlock.h"
+#include "llvm/Config/llvm-config.h"
+#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5
+typedef long double max_align_t;
+#endif
+
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
+#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/CFG.h"
+#else
+#include "llvm/DebugInfo.h"
+#include "llvm/Support/CFG.h"
+#endif
using namespace llvm;
@@ -78,7 +90,7 @@ class AFLCoverage : public ModulePass {
// ripped from aflgo
static bool isBlacklisted(const Function *F) {
- static const SmallVector<std::string, 4> Blacklist = {
+ static const char *Blacklist[] = {
"asan.",
"llvm.",
@@ -197,6 +209,8 @@ bool AFLCoverage::runOnModule(Module &M) {
* For now, just instrument the block if we are not able
* to determine our location. */
DebugLoc Loc = IP->getDebugLoc();
+#if LLVM_VERSION_MAJOR >= 4 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
if (Loc) {
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
@@ -249,6 +263,47 @@ bool AFLCoverage::runOnModule(Module &M) {
}
+#else
+ if (!Loc.isUnknown()) {
+
+ DILocation cDILoc(Loc.getAsMDNode(C));
+
+ unsigned int instLine = cDILoc.getLineNumber();
+ StringRef instFilename = cDILoc.getFilename();
+
+ (void)instLine;
+
+ /* Continue only if we know where we actually are */
+ if (!instFilename.str().empty()) {
+
+ for (std::list<std::string>::iterator it = myWhitelist.begin();
+ it != myWhitelist.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()) {
+
+ if (instFilename.str().compare(
+ instFilename.str().length() - it->length(),
+ it->length(), *it) == 0) {
+
+ instrumentBlock = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+#endif
+
/* Either we couldn't figure out our location or the location is
* not whitelisted, so we skip instrumentation. */
if (!instrumentBlock) continue;
@@ -273,13 +328,19 @@ bool AFLCoverage::runOnModule(Module &M) {
// result: a little more speed and less map pollution
int more_than_one = -1;
// fprintf(stderr, "BB %u: ", cur_loc);
- for (BasicBlock *Pred : predecessors(&BB)) {
+ for (pred_iterator PI = pred_begin(&BB), E = pred_end(&BB); PI != E;
+ ++PI) {
+
+ BasicBlock *Pred = *PI;
int count = 0;
if (more_than_one == -1) more_than_one = 0;
// fprintf(stderr, " %p=>", Pred);
- for (BasicBlock *Succ : successors(Pred)) {
+ for (succ_iterator SI = succ_begin(Pred), E = succ_end(Pred); SI != E;
+ ++SI) {
+
+ BasicBlock *Succ = *SI;
// if (count > 0)
// fprintf(stderr, "|");
diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc
index 0ccce875..e1332a9d 100644
--- a/llvm_mode/compare-transform-pass.so.cc
+++ b/llvm_mode/compare-transform-pass.so.cc
@@ -18,6 +18,12 @@
#include <stdlib.h>
#include <unistd.h>
+#include <list>
+#include <string>
+#include <fstream>
+#include <sys/time.h>
+#include "llvm/Config/llvm-config.h"
+
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
@@ -26,10 +32,19 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
-#include "llvm/IR/Verifier.h"
#include "llvm/Pass.h"
#include "llvm/Analysis/ValueTracking.h"
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
+#include "llvm/IR/Verifier.h"
+#include "llvm/IR/DebugInfo.h"
+#else
+#include "llvm/Analysis/Verifier.h"
+#include "llvm/DebugInfo.h"
+#define nullptr 0
+#endif
+
#include <set>
using namespace llvm;
@@ -42,6 +57,23 @@ class CompareTransform : public ModulePass {
static char ID;
CompareTransform() : ModulePass(ID) {
+ char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
+ if (instWhiteListFilename) {
+
+ std::string line;
+ std::ifstream fileStream;
+ fileStream.open(instWhiteListFilename);
+ if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
+ getline(fileStream, line);
+ while (fileStream) {
+
+ myWhitelist.push_back(line);
+ getline(fileStream, line);
+
+ }
+
+ }
+
}
bool runOnModule(Module &M) override;
@@ -57,6 +89,9 @@ class CompareTransform : public ModulePass {
}
+ protected:
+ std::list<std::string> myWhitelist;
+
private:
bool transformCmps(Module &M, const bool processStrcmp,
const bool processMemcmp, const bool processStrncmp,
@@ -89,7 +124,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty
#if LLVM_VERSION_MAJOR < 5
,
- nullptr
+ NULL
#endif
);
#if LLVM_VERSION_MAJOR < 9
@@ -104,6 +139,117 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
for (auto &BB : F) {
+ if (!myWhitelist.empty()) {
+
+ BasicBlock::iterator IP = BB.getFirstInsertionPt();
+
+ bool instrumentBlock = false;
+
+ /* Get the current location using debug information.
+ * For now, just instrument the block if we are not able
+ * to determine our location. */
+ DebugLoc Loc = IP->getDebugLoc();
+#if LLVM_VERSION_MAJOR >= 4 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
+ if (Loc) {
+
+ DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
+
+ unsigned int instLine = cDILoc->getLine();
+ StringRef instFilename = cDILoc->getFilename();
+
+ if (instFilename.str().empty()) {
+
+ /* If the original location is empty, try using the inlined location
+ */
+ DILocation *oDILoc = cDILoc->getInlinedAt();
+ if (oDILoc) {
+
+ instFilename = oDILoc->getFilename();
+ instLine = oDILoc->getLine();
+
+ }
+
+ }
+
+ (void)instLine;
+
+ /* Continue only if we know where we actually are */
+ if (!instFilename.str().empty()) {
+
+ for (std::list<std::string>::iterator it = myWhitelist.begin();
+ it != myWhitelist.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()) {
+
+ if (instFilename.str().compare(
+ instFilename.str().length() - it->length(),
+ it->length(), *it) == 0) {
+
+ instrumentBlock = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+#else
+ if (!Loc.isUnknown()) {
+
+ DILocation cDILoc(Loc.getAsMDNode(C));
+
+ unsigned int instLine = cDILoc.getLineNumber();
+ StringRef instFilename = cDILoc.getFilename();
+
+ (void)instLine;
+
+ /* Continue only if we know where we actually are */
+ if (!instFilename.str().empty()) {
+
+ for (std::list<std::string>::iterator it = myWhitelist.begin();
+ it != myWhitelist.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()) {
+
+ if (instFilename.str().compare(
+ instFilename.str().length() - it->length(),
+ it->length(), *it) == 0) {
+
+ instrumentBlock = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+#endif
+
+ /* Either we couldn't figure out our location or the location is
+ * not whitelisted, so we skip instrumentation. */
+ if (!instrumentBlock) continue;
+
+ }
+
for (auto &IN : BB) {
CallInst *callInst = nullptr;
diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc
index eeac4a55..e16993d6 100644
--- a/llvm_mode/split-compares-pass.so.cc
+++ b/llvm_mode/split-compares-pass.so.cc
@@ -15,15 +15,34 @@
* limitations under the License.
*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <list>
+#include <string>
+#include <fstream>
+#include <sys/time.h>
+
+#include "llvm/Config/llvm-config.h"
+
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
-#include "llvm/IR/Verifier.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/IRBuilder.h"
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
+#include "llvm/IR/Verifier.h"
+#include "llvm/IR/DebugInfo.h"
+#else
+#include "llvm/Analysis/Verifier.h"
+#include "llvm/DebugInfo.h"
+#define nullptr 0
+#endif
using namespace llvm;
@@ -35,6 +54,41 @@ class SplitComparesTransform : public ModulePass {
static char ID;
SplitComparesTransform() : ModulePass(ID) {
+ char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
+ if (instWhiteListFilename) {
+
+ std::string line;
+ std::ifstream fileStream;
+ fileStream.open(instWhiteListFilename);
+ if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
+ getline(fileStream, line);
+ while (fileStream) {
+
+ myWhitelist.push_back(line);
+ getline(fileStream, line);
+
+ }
+
+ }
+
+ }
+
+ static bool isBlacklisted(const Function *F) {
+
+ static const char *Blacklist[] = {
+
+ "asan.", "llvm.", "sancov.", "__ubsan_handle_", "ign."
+
+ };
+
+ for (auto const &BlacklistFunc : Blacklist) {
+
+ if (F->getName().startswith(BlacklistFunc)) { return true; }
+
+ }
+
+ return false;
+
}
bool runOnModule(Module &M) override;
@@ -49,6 +103,9 @@ class SplitComparesTransform : public ModulePass {
}
+ protected:
+ std::list<std::string> myWhitelist;
+
private:
int enableFPSplit;
@@ -77,8 +134,121 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
* all integer comparisons with >= and <= predicates to the icomps vector */
for (auto &F : M) {
+ if (isBlacklisted(&F)) continue;
+
for (auto &BB : F) {
+ if (!myWhitelist.empty()) {
+
+ bool instrumentBlock = false;
+
+ BasicBlock::iterator IP = BB.getFirstInsertionPt();
+
+ /* Get the current location using debug information.
+ * For now, just instrument the block if we are not able
+ * to determine our location. */
+ DebugLoc Loc = IP->getDebugLoc();
+#if LLVM_VERSION_MAJOR >= 4 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
+ if (Loc) {
+
+ DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
+
+ unsigned int instLine = cDILoc->getLine();
+ StringRef instFilename = cDILoc->getFilename();
+
+ if (instFilename.str().empty()) {
+
+ /* If the original location is empty, try using the inlined location
+ */
+ DILocation *oDILoc = cDILoc->getInlinedAt();
+ if (oDILoc) {
+
+ instFilename = oDILoc->getFilename();
+ instLine = oDILoc->getLine();
+
+ }
+
+ }
+
+ (void)instLine;
+
+ /* Continue only if we know where we actually are */
+ if (!instFilename.str().empty()) {
+
+ for (std::list<std::string>::iterator it = myWhitelist.begin();
+ it != myWhitelist.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()) {
+
+ if (instFilename.str().compare(
+ instFilename.str().length() - it->length(),
+ it->length(), *it) == 0) {
+
+ instrumentBlock = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+#else
+ if (!Loc.isUnknown()) {
+
+ DILocation cDILoc(Loc.getAsMDNode(C));
+
+ unsigned int instLine = cDILoc.getLineNumber();
+ StringRef instFilename = cDILoc.getFilename();
+
+ (void)instLine;
+
+ /* Continue only if we know where we actually are */
+ if (!instFilename.str().empty()) {
+
+ for (std::list<std::string>::iterator it = myWhitelist.begin();
+ it != myWhitelist.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()) {
+
+ if (instFilename.str().compare(
+ instFilename.str().length() - it->length(),
+ it->length(), *it) == 0) {
+
+ instrumentBlock = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+#endif
+
+ /* Either we couldn't figure out our location or the location is
+ * not whitelisted, so we skip instrumentation. */
+ if (!instrumentBlock) continue;
+
+ }
+
for (auto &IN : BB) {
CmpInst *selectcmpInst = nullptr;
@@ -165,7 +335,8 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
* 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);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_np);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+ icmp_np);
/* create a new basic block which holds the new EQ icmp */
Instruction *icmp_eq;
@@ -230,7 +401,8 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
* 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);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), fcmp_np);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+ fcmp_np);
/* create a new basic block which holds the new EQ fcmp */
Instruction *fcmp_eq;
@@ -351,20 +523,21 @@ bool SplitComparesTransform::simplifyIntSignedness(Module &M) {
s_op0 = BinaryOperator::Create(Instruction::LShr, op0,
ConstantInt::get(IntType, bitw - 1));
- bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op0);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op0);
t_op0 = new TruncInst(s_op0, Int1Ty);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op0);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_op0);
s_op1 = BinaryOperator::Create(Instruction::LShr, op1,
ConstantInt::get(IntType, bitw - 1));
- bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op1);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op1);
t_op1 = new TruncInst(s_op1, Int1Ty);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op1);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_op1);
/* compare of the sign bits */
icmp_sign_bit =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_op0, t_op1);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_sign_bit);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+ icmp_sign_bit);
/* create a new basic block which is executed if the signedness bit is
* different */
@@ -439,6 +612,8 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
LLVMContext &C = M.getContext();
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7)
const DataLayout &dl = M.getDataLayout();
/* define unions with floating point and (sign, exponent, mantissa) triples
@@ -453,6 +628,8 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
}
+#endif
+
std::vector<CmpInst *> fcomps;
/* get all EQ, NE, GT, and LT fcmps. if the other two
@@ -551,11 +728,11 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
Instruction *b_op0, *b_op1;
b_op0 = CastInst::Create(Instruction::BitCast, op0,
IntegerType::get(C, op_size));
- bb->getInstList().insert(bb->getTerminator()->getIterator(), b_op0);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), b_op0);
b_op1 = CastInst::Create(Instruction::BitCast, op1,
IntegerType::get(C, op_size));
- bb->getInstList().insert(bb->getTerminator()->getIterator(), b_op1);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), b_op1);
/* isolate signs of value of floating point type */
@@ -566,21 +743,22 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
s_s0 =
BinaryOperator::Create(Instruction::LShr, b_op0,
ConstantInt::get(b_op0->getType(), op_size - 1));
- bb->getInstList().insert(bb->getTerminator()->getIterator(), s_s0);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_s0);
t_s0 = new TruncInst(s_s0, Int1Ty);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), t_s0);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_s0);
s_s1 =
BinaryOperator::Create(Instruction::LShr, b_op1,
ConstantInt::get(b_op1->getType(), op_size - 1));
- bb->getInstList().insert(bb->getTerminator()->getIterator(), s_s1);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_s1);
t_s1 = new TruncInst(s_s1, Int1Ty);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), t_s1);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_s1);
/* compare of the sign bits */
icmp_sign_bit =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_sign_bit);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+ icmp_sign_bit);
/* create a new basic block which is executed if the signedness bits are
* equal */
@@ -612,16 +790,16 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
Instruction::LShr, b_op1,
ConstantInt::get(b_op1->getType(), shiftR_exponent));
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), s_e0);
+ BasicBlock::iterator(signequal_bb->getTerminator()), s_e0);
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), s_e1);
+ BasicBlock::iterator(signequal_bb->getTerminator()), s_e1);
t_e0 = new TruncInst(s_e0, IntExponentTy);
t_e1 = new TruncInst(s_e1, IntExponentTy);
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), t_e0);
+ BasicBlock::iterator(signequal_bb->getTerminator()), t_e0);
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), t_e1);
+ BasicBlock::iterator(signequal_bb->getTerminator()), t_e1);
if (sizeInBits - precision < exTySizeBytes * 8) {
@@ -632,9 +810,9 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
Instruction::And, t_e1,
ConstantInt::get(t_e1->getType(), mask_exponent));
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), m_e0);
+ BasicBlock::iterator(signequal_bb->getTerminator()), m_e0);
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), m_e1);
+ BasicBlock::iterator(signequal_bb->getTerminator()), m_e1);
} else {
@@ -662,7 +840,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
icmp_exponent =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, m_e0, m_e1);
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), icmp_exponent);
+ BasicBlock::iterator(signequal_bb->getTerminator()), icmp_exponent);
icmp_exponent_result =
BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0);
break;
@@ -671,7 +849,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
icmp_exponent =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, m_e0, m_e1);
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), icmp_exponent);
+ BasicBlock::iterator(signequal_bb->getTerminator()), icmp_exponent);
icmp_exponent_result =
BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0);
break;
@@ -680,7 +858,8 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
}
signequal_bb->getInstList().insert(
- signequal_bb->getTerminator()->getIterator(), icmp_exponent_result);
+ BasicBlock::iterator(signequal_bb->getTerminator()),
+ icmp_exponent_result);
{
@@ -704,19 +883,19 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
m_f1 = BinaryOperator::Create(
Instruction::And, b_op1,
ConstantInt::get(b_op1->getType(), mask_fraction));
- middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(),
- m_f0);
- middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(),
- m_f1);
+ middle_bb->getInstList().insert(
+ BasicBlock::iterator(middle_bb->getTerminator()), m_f0);
+ middle_bb->getInstList().insert(
+ BasicBlock::iterator(middle_bb->getTerminator()), m_f1);
if (needTrunc) {
t_f0 = new TruncInst(m_f0, IntFractionTy);
t_f1 = new TruncInst(m_f1, IntFractionTy);
middle_bb->getInstList().insert(
- middle_bb->getTerminator()->getIterator(), t_f0);
+ BasicBlock::iterator(middle_bb->getTerminator()), t_f0);
middle_bb->getInstList().insert(
- middle_bb->getTerminator()->getIterator(), t_f1);
+ BasicBlock::iterator(middle_bb->getTerminator()), t_f1);
} else {
@@ -732,9 +911,9 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
t_f0 = new TruncInst(b_op0, IntFractionTy);
t_f1 = new TruncInst(b_op1, IntFractionTy);
middle_bb->getInstList().insert(
- middle_bb->getTerminator()->getIterator(), t_f0);
+ BasicBlock::iterator(middle_bb->getTerminator()), t_f0);
middle_bb->getInstList().insert(
- middle_bb->getTerminator()->getIterator(), t_f1);
+ BasicBlock::iterator(middle_bb->getTerminator()), t_f1);
} else {
@@ -764,7 +943,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
icmp_fraction =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1);
middle_bb->getInstList().insert(
- middle_bb->getTerminator()->getIterator(), icmp_fraction);
+ BasicBlock::iterator(middle_bb->getTerminator()), icmp_fraction);
icmp_fraction_result =
BinaryOperator::Create(Instruction::Xor, icmp_fraction, t_s0);
break;
@@ -773,7 +952,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
icmp_fraction =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1);
middle_bb->getInstList().insert(
- middle_bb->getTerminator()->getIterator(), icmp_fraction);
+ BasicBlock::iterator(middle_bb->getTerminator()), icmp_fraction);
icmp_fraction_result =
BinaryOperator::Create(Instruction::Xor, icmp_fraction, t_s0);
break;
@@ -781,8 +960,8 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
}
- middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(),
- icmp_fraction_result);
+ middle_bb->getInstList().insert(
+ BasicBlock::iterator(middle_bb->getTerminator()), icmp_fraction_result);
PHINode *PN = PHINode::Create(Int1Ty, 3, "");
@@ -919,18 +1098,21 @@ size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) {
s_op0 = BinaryOperator::Create(Instruction::LShr, op0,
ConstantInt::get(OldIntType, bitw / 2));
- bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op0);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op0);
op0_high = new TruncInst(s_op0, NewIntType);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), op0_high);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+ op0_high);
s_op1 = BinaryOperator::Create(Instruction::LShr, op1,
ConstantInt::get(OldIntType, bitw / 2));
- bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op1);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op1);
op1_high = new TruncInst(s_op1, NewIntType);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), op1_high);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+ op1_high);
icmp_high = CmpInst::Create(Instruction::ICmp, pred, op0_high, op1_high);
- bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_high);
+ bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+ icmp_high);
/* now we have to destinguish between == != and > < */
if (pred == CmpInst::ICMP_EQ || pred == CmpInst::ICMP_NE) {
@@ -1076,13 +1258,19 @@ bool SplitComparesTransform::runOnModule(Module &M) {
<< "bit: " << splitIntCompares(M, bitw) << " splitted\n";
bitw >>= 1;
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7)
[[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */
+#endif
case 32:
errs() << "Split-integer-compare-pass " << bitw
<< "bit: " << splitIntCompares(M, bitw) << " splitted\n";
bitw >>= 1;
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7)
[[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */
+#endif
case 16:
errs() << "Split-integer-compare-pass " << bitw
<< "bit: " << splitIntCompares(M, bitw) << " splitted\n";
diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc
index 2743a71a..9101dc26 100644
--- a/llvm_mode/split-switches-pass.so.cc
+++ b/llvm_mode/split-switches-pass.so.cc
@@ -18,6 +18,13 @@
#include <stdlib.h>
#include <unistd.h>
+#include <list>
+#include <string>
+#include <fstream>
+#include <sys/time.h>
+
+#include "llvm/Config/llvm-config.h"
+
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
@@ -26,10 +33,20 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
-#include "llvm/IR/Verifier.h"
#include "llvm/Pass.h"
#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/IR/IRBuilder.h"
+#if LLVM_VERSION_MAJOR > 3 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
+#include "llvm/IR/Verifier.h"
+#include "llvm/IR/DebugInfo.h"
+#else
+#include "llvm/Analysis/Verifier.h"
+#include "llvm/DebugInfo.h"
+#define nullptr 0
+#endif
+
#include <set>
using namespace llvm;
@@ -42,6 +59,41 @@ class SplitSwitchesTransform : public ModulePass {
static char ID;
SplitSwitchesTransform() : ModulePass(ID) {
+ char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
+ if (instWhiteListFilename) {
+
+ std::string line;
+ std::ifstream fileStream;
+ fileStream.open(instWhiteListFilename);
+ if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
+ getline(fileStream, line);
+ while (fileStream) {
+
+ myWhitelist.push_back(line);
+ getline(fileStream, line);
+
+ }
+
+ }
+
+ }
+
+ static bool isBlacklisted(const Function *F) {
+
+ static const char *Blacklist[] = {
+
+ "asan.", "llvm.", "sancov.", "__ubsan_handle_", "ign."
+
+ };
+
+ for (auto const &BlacklistFunc : Blacklist) {
+
+ if (F->getName().startswith(BlacklistFunc)) { return true; }
+
+ }
+
+ return false;
+
}
bool runOnModule(Module &M) override;
@@ -71,6 +123,9 @@ class SplitSwitchesTransform : public ModulePass {
typedef std::vector<CaseExpr> CaseVector;
+ protected:
+ std::list<std::string> myWhitelist;
+
private:
bool splitSwitches(Module &M);
bool transformCmps(Module &M, const bool processStrcmp,
@@ -96,7 +151,7 @@ BasicBlock *SplitSwitchesTransform::switchConvert(
IntegerType * ByteType = IntegerType::get(OrigBlock->getContext(), 8);
unsigned BytesInValue = bytesChecked.size();
std::vector<uint8_t> setSizes;
- std::vector<std::set<uint8_t>> byteSets(BytesInValue, std::set<uint8_t>());
+ std::vector<std::set<uint8_t> > byteSets(BytesInValue, std::set<uint8_t>());
assert(ValTypeBitWidth >= 8 && ValTypeBitWidth <= 64);
@@ -169,8 +224,25 @@ BasicBlock *SplitSwitchesTransform::switchConvert(
NewNode->getInstList().push_back(Comp);
bytesChecked[smallestIndex] = true;
- if (std::all_of(bytesChecked.begin(), bytesChecked.end(),
- [](bool b) { return b; })) {
+ bool allBytesAreChecked = true;
+
+ for (std::vector<bool>::iterator BCI = bytesChecked.begin(),
+ E = bytesChecked.end();
+ BCI != E; ++BCI) {
+
+ if (!*BCI) {
+
+ allBytesAreChecked = false;
+ break;
+
+ }
+
+ }
+
+ // if (std::all_of(bytesChecked.begin(), bytesChecked.end(),
+ // [](bool b) { return b; })) {
+
+ if (allBytesAreChecked) {
assert(Cases.size() == 1);
BranchInst::Create(Cases[0].BB, NewDefault, Comp, NewNode);
@@ -262,16 +334,132 @@ BasicBlock *SplitSwitchesTransform::switchConvert(
bool SplitSwitchesTransform::splitSwitches(Module &M) {
+#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7)
+ LLVMContext &C = M.getContext();
+#endif
+
std::vector<SwitchInst *> switches;
/* iterate over all functions, bbs and instruction and add
* all switches to switches vector for later processing */
for (auto &F : M) {
+ if (isBlacklisted(&F)) continue;
+
for (auto &BB : F) {
SwitchInst *switchInst = nullptr;
+ if (!myWhitelist.empty()) {
+
+ bool instrumentBlock = false;
+ BasicBlock::iterator IP = BB.getFirstInsertionPt();
+
+ /* Get the current location using debug information.
+ * For now, just instrument the block if we are not able
+ * to determine our location. */
+ DebugLoc Loc = IP->getDebugLoc();
+#if LLVM_VERSION_MAJOR >= 4 || \
+ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
+ if (Loc) {
+
+ DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
+
+ unsigned int instLine = cDILoc->getLine();
+ StringRef instFilename = cDILoc->getFilename();
+
+ if (instFilename.str().empty()) {
+
+ /* If the original location is empty, try using the inlined location
+ */
+ DILocation *oDILoc = cDILoc->getInlinedAt();
+ if (oDILoc) {
+
+ instFilename = oDILoc->getFilename();
+ instLine = oDILoc->getLine();
+
+ }
+
+ }
+
+ (void)instLine;
+
+ /* Continue only if we know where we actually are */
+ if (!instFilename.str().empty()) {
+
+ for (std::list<std::string>::iterator it = myWhitelist.begin();
+ it != myWhitelist.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()) {
+
+ if (instFilename.str().compare(
+ instFilename.str().length() - it->length(),
+ it->length(), *it) == 0) {
+
+ instrumentBlock = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+#else
+ if (!Loc.isUnknown()) {
+
+ DILocation cDILoc(Loc.getAsMDNode(C));
+
+ unsigned int instLine = cDILoc.getLineNumber();
+ StringRef instFilename = cDILoc.getFilename();
+
+ (void)instLine;
+
+ /* Continue only if we know where we actually are */
+ if (!instFilename.str().empty()) {
+
+ for (std::list<std::string>::iterator it = myWhitelist.begin();
+ it != myWhitelist.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()) {
+
+ if (instFilename.str().compare(
+ instFilename.str().length() - it->length(),
+ it->length(), *it) == 0) {
+
+ instrumentBlock = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+#endif
+
+ /* Either we couldn't figure out our location or the location is
+ * not whitelisted, so we skip instrumentation. */
+ if (!instrumentBlock) continue;
+
+ }
+
if ((switchInst = dyn_cast<SwitchInst>(BB.getTerminator()))) {
if (switchInst->getNumCases() < 1) continue;
@@ -313,8 +501,7 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
* if the default block is set as an unreachable we avoid creating one
* because will never be a valid target.*/
BasicBlock *NewDefault = nullptr;
- NewDefault = BasicBlock::Create(SI->getContext(), "NewDefault");
- NewDefault->insertInto(F, Default);
+ NewDefault = BasicBlock::Create(SI->getContext(), "NewDefault", F, Default);
BranchInst::Create(Default, NewDefault);
/* Prepare cases vector. */
diff --git a/src/afl-analyze.c b/src/afl-analyze.c
index 3d4e636e..3de8c037 100644
--- a/src/afl-analyze.c
+++ b/src/afl-analyze.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-as.c b/src/afl-as.c
index 77ac2f97..8d689385 100644
--- a/src/afl-as.c
+++ b/src/afl-as.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-common.c b/src/afl-common.c
index 8c2f2b9a..6cb97cdf 100644
--- a/src/afl-common.c
+++ b/src/afl-common.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index de50c73c..77e1d648 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com>
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c
index 3f8256b4..3ffda284 100644
--- a/src/afl-fuzz-bitmap.c
+++ b/src/afl-fuzz-bitmap.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c
index fcc7749d..6c6dc28c 100644
--- a/src/afl-fuzz-extras.c
+++ b/src/afl-fuzz-extras.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-globals.c b/src/afl-fuzz-globals.c
index 863ee9ad..e92558d3 100644
--- a/src/afl-fuzz-globals.c
+++ b/src/afl-fuzz-globals.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index 5fe3689e..6efa6227 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
@@ -184,11 +184,21 @@ void bind_to_free_cpu(void) {
"For this platform we do not have free CPU binding code yet. If possible, please supply a PR to https://github.com/vanhauser-thc/AFLplusplus"
#endif
- for (i = 0; i < cpu_core_count; ++i)
- if (!cpu_used[i]) break;
+ size_t cpu_start = 0;
+ try:
+#ifndef __ANDROID__
+ for (i = cpu_start; i < cpu_core_count; i++)
+ if (!cpu_used[i]) break;
if (i == cpu_core_count) {
+#else
+ for (i = cpu_core_count - cpu_start - 1; i > -1; i--)
+ if (!cpu_used[i]) break;
+ if (i == -1) {
+
+#endif
+
SAYF("\n" cLRD "[-] " cRST
"Uh-oh, looks like all %d CPU cores on your system are allocated to\n"
" other instances of afl-fuzz (or similar CPU-locked tasks). "
@@ -197,12 +207,11 @@ void bind_to_free_cpu(void) {
"you are\n"
" absolutely sure, you can set AFL_NO_AFFINITY and try again.\n",
cpu_core_count);
-
FATAL("No more free CPU cores");
}
- OKF("Found a free CPU core, binding to #%u.", i);
+ OKF("Found a free CPU core, try binding to #%u.", i);
cpu_aff = i;
@@ -212,22 +221,31 @@ void bind_to_free_cpu(void) {
#elif defined(__NetBSD__)
c = cpuset_create();
if (c == NULL) PFATAL("cpuset_create failed");
-
cpuset_set(i, c);
#endif
#if defined(__linux__)
- if (sched_setaffinity(0, sizeof(c), &c)) PFATAL("sched_setaffinity failed");
+ if (sched_setaffinity(0, sizeof(c), &c)) {
+
+ if (cpu_start == cpu_core_count)
+ PFATAL("sched_setaffinity failed for CPU %d, exit", i);
+ WARNF("sched_setaffinity failed to CPU %d, trying next CPU", i);
+ cpu_start++;
+ goto try
+ ;
+
+ }
+
#elif defined(__FreeBSD__) || defined(__DragonFly__)
if (pthread_setaffinity_np(pthread_self(), sizeof(c), &c))
PFATAL("pthread_setaffinity failed");
#elif defined(__NetBSD__)
- if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c))
- PFATAL("pthread_setaffinity failed");
+if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c))
+ PFATAL("pthread_setaffinity failed");
- cpuset_destroy(c);
+cpuset_destroy(c);
#else
- // this will need something for other platforms
+// this will need something for other platforms
#endif
}
@@ -1940,17 +1958,17 @@ void check_binary(u8* fname) {
}
- if ((qemu_mode || unicorn_mode) &&
+ if ((qemu_mode) &&
memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) {
SAYF("\n" cLRD "[-] " cRST
"This program appears to be instrumented with afl-gcc, but is being "
"run in\n"
- " QEMU or Unicorn mode (-Q or -U). This is probably not what you "
+ " QEMU mode (-Q). This is probably not what you "
"want -\n"
" this setup will be slow and offer no practical benefits.\n");
- FATAL("Instrumentation found in -Q or -U mode");
+ FATAL("Instrumentation found in -Q mode");
}
diff --git a/src/afl-fuzz-misc.c b/src/afl-fuzz-misc.c
index b8f376be..0da0cb0a 100644
--- a/src/afl-fuzz-misc.c
+++ b/src/afl-fuzz-misc.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index 4c3a5b95..199b3ea8 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c
index f1cdecde..f06c8e25 100644
--- a/src/afl-fuzz-python.c
+++ b/src/afl-fuzz-python.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c
index 1b51e3aa..0880de75 100644
--- a/src/afl-fuzz-queue.c
+++ b/src/afl-fuzz-queue.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index 78708402..79573932 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c
index 14ffd41a..d00c6750 100644
--- a/src/afl-fuzz-stats.c
+++ b/src/afl-fuzz-stats.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 50356315..74bc0ee2 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-gcc.c b/src/afl-gcc.c
index 301e2034..e46fe5cd 100644
--- a/src/afl-gcc.c
+++ b/src/afl-gcc.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-gotcpu.c b/src/afl-gotcpu.c
index 9a56159c..5be30238 100644
--- a/src/afl-gotcpu.c
+++ b/src/afl-gotcpu.c
@@ -4,7 +4,7 @@
Originally written by Michal Zalewski
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c
index f8ed4e51..3f552881 100644
--- a/src/afl-sharedmem.c
+++ b/src/afl-sharedmem.c
@@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com>
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index 8c899c9d..b9da3208 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com>
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/src/afl-tmin.c b/src/afl-tmin.c
index 3e33b72f..7ce0ccaa 100644
--- a/src/afl-tmin.c
+++ b/src/afl-tmin.c
@@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com>
- Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
diff --git a/test/test-unsigaction.c b/test/test-unsigaction.c
new file mode 100644
index 00000000..1a5e4b26
--- /dev/null
+++ b/test/test-unsigaction.c
@@ -0,0 +1,25 @@
+#include <signal.h> /* sigemptyset(), sigaction(), kill(), SIGUSR1 */
+#include <stdlib.h> /* exit() */
+#include <unistd.h> /* getpid() */
+#include <errno.h> /* errno */
+#include <stdio.h> /* fprintf() */
+
+static void mysig_handler(int sig)
+{
+ exit(2);
+}
+
+int main()
+{
+ /* setup sig handler */
+ struct sigaction sa;
+ sa.sa_handler = mysig_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ if (sigaction(SIGCHLD, &sa, NULL)) {
+ fprintf(stderr, "could not set signal handler %d, aborted\n", errno);
+ exit(1);
+ }
+ kill(getpid(), SIGCHLD);
+ return 0;
+}
diff --git a/test/test.sh b/test/test.sh
index 8f40773c..9676d22d 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -150,13 +150,13 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" && {
}
echo 000000000000000000000000 > in/in2
mkdir -p in2
- ../afl-cmin -i in -o in2 -- ./test-instr.plain > /dev/null 2>&1
+ ../afl-cmin -i in -o in2 -- ./test-instr.plain > /dev/null
CNT=`ls in2/ | wc -l`
case "$CNT" in
-1| *1) $ECHO "$GREEN[+] afl-cmin correctly minimized testcase numbers" ;;
-*) $ECHO "$RED[!] afl-cmin did not correctly minimize testcase numbers"
- CODE=1
- ;;
+ *1) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;;
+ *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases"
+ CODE=1
+ ;;
esac
../afl-tmin -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1
SIZE=`ls -l in2/in2 2> /dev/null | awk '{print$5}'`
@@ -176,14 +176,16 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" && {
$ECHO "$YELLOW[-] not an intel platform, cannot test afl-gcc"
}
-$ECHO "$BLUE[*] Testing: llvm_mode"
+$ECHO "$BLUE[*] Testing: llvm_mode, afl-showmap, afl-fuzz, afl-cmin and afl-tmin"
test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
# on FreeBSD need to set AFL_CC
- if which clang >/dev/null; then
- export AFL_CC=`which clang`
- else
- export AFL_CC=`$LLVM_CONFIG --bindir`/clang
- fi
+ test `uname -s` = 'FreeBSD' && {
+ if which clang >/dev/null; then
+ export AFL_CC=`which clang`
+ else
+ export AFL_CC=`$LLVM_CONFIG --bindir`/clang
+ fi
+ }
../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1
AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1
test -e test-instr.plain && {
@@ -251,6 +253,26 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
$ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode"
CODE=1
}
+ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" || {
+ echo 000000000000000000000000 > in/in2
+ mkdir -p in2
+ ../afl-cmin -i in -o in2 -- ./test-instr.plain > /dev/null
+ CNT=`ls in2/ | wc -l`
+ case "$CNT" in
+ *1) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;;
+ *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases"
+ CODE=1
+ ;;
+ esac
+ ../afl-tmin -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1
+ SIZE=`ls -l in2/in2 2> /dev/null | awk '{print$5}'`
+ test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase"
+ test "$SIZE" = 1 || {
+ $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE"
+ CODE=1
+ }
+ rm -rf in2
+ }
rm -rf in out errors
}
rm -f test-instr.plain
@@ -334,7 +356,7 @@ test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && {
$ECHO "$GREEN[+] gcc_plugin run reported $TUPLES instrumented locations which is fine"
} || {
$ECHO "$RED[!] gcc_plugin instrumentation produces a weird number of instrumented locations: $TUPLES"
- $ECHO "$YELLOW[-] the gcc_plugin instrumentation issue is not flagged as an error because travis builds would all fail otherwise :-("
+ $ECHO "$YELLOW[-] this is a known issue in gcc, not afl++. It is not flagged as an error because travis builds would all fail otherwise :-("
#CODE=1
}
}
@@ -457,6 +479,15 @@ test -e ../libdislocator.so && {
}
rm -f test-compcov
test -e ../libradamsa.so && {
+ # on FreeBSD need to set AFL_CC
+
+ test `uname -s` = 'FreeBSD' && {
+ if which clang >/dev/null; then
+ export AFL_CC=`which clang`
+ else
+ export AFL_CC=`$LLVM_CONFIG --bindir`/clang
+ fi
+ }
test -e test-instr.plain || ../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1
test -e test-instr.plain || ../afl-gcc-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1
test -e test-instr.plain || ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1
@@ -560,8 +591,64 @@ test -e ../afl-qemu-trace && {
CODE=1
exit 1
}
- $ECHO "$YELLOW[-] we need a test case for qemu_mode unsigaction library"
rm -rf in out errors
+ test -e ../qemu_mode/unsigaction/unsigaction32.so && {
+ ${AFL_CC} -o test-unsigaction32 -m32 test-unsigaction.c >> errors 2>&1 && {
+ ./test-unsigaction32
+ RETVAL_NORMAL32=$?
+ LD_PRELOAD=../qemu_mode/unsigaction/unsigaction32.so ./test-unsigaction32
+ RETVAL_LIBUNSIGACTION32=$?
+ test $RETVAL_NORMAL32 = "2" -a $RETVAL_LIBUNSIGACTION32 = "0" && {
+ $ECHO "$GREEN[+] qemu_mode unsigaction library (32 bit) ignores signals"
+ } || {
+ test $RETVAL_NORMAL32 != "2" && {
+ $ECHO "$RED[!] cannot trigger signal in test program (32 bit)"
+ }
+ test $RETVAL_LIBUNSIGACTION32 != "0" && {
+ $ECHO "$RED[!] signal in test program (32 bit) is not ignored with unsigaction"
+ }
+ CODE=1
+ }
+ } || {
+ echo CUT------------------------------------------------------------------CUT
+ cat errors
+ echo CUT------------------------------------------------------------------CUT
+ $ECHO "$RED[!] cannot compile test program (32 bit) for unsigaction library"
+ CODE=1
+ }
+ } || {
+ $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (32 bit) because it is not present"
+ INCOMPLETE=1
+ }
+ test -e ../qemu_mode/unsigaction/unsigaction64.so && {
+ ${AFL_CC} -o test-unsigaction64 -m64 test-unsigaction.c >> errors 2>&1 && {
+ ./test-unsigaction64
+ RETVAL_NORMAL64=$?
+ LD_PRELOAD=../qemu_mode/unsigaction/unsigaction64.so ./test-unsigaction64
+ RETVAL_LIBUNSIGACTION64=$?
+ test $RETVAL_NORMAL64 = "2" -a $RETVAL_LIBUNSIGACTION64 = "0" && {
+ $ECHO "$GREEN[+] qemu_mode unsigaction library (64 bit) ignores signals"
+ } || {
+ test $RETVAL_NORMAL64 != "2" && {
+ $ECHO "$RED[!] cannot trigger signal in test program (64 bit)"
+ }
+ test $RETVAL_LIBUNSIGACTION64 != "0" && {
+ $ECHO "$RED[!] signal in test program (64 bit) is not ignored with unsigaction"
+ }
+ CODE=1
+ }
+ } || {
+ echo CUT------------------------------------------------------------------CUT
+ cat errors
+ echo CUT------------------------------------------------------------------CUT
+ $ECHO "$RED[!] cannot compile test program (64 bit) for unsigaction library"
+ CODE=1
+ }
+ } || {
+ $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (64 bit) because it is not present"
+ INCOMPLETE=1
+ }
+ rm -rf errors test-unsigaction32 test-unsigaction64
}
} || {
$ECHO "$RED[!] gcc compilation of test targets failed - what is going on??"
diff --git a/unicorn_mode/samples/c/a.out b/unicorn_mode/samples/c/a.out
deleted file mode 100644
index 176c25e1..00000000
--- a/unicorn_mode/samples/c/a.out
+++ /dev/null
Binary files differ
diff --git a/unicorn_mode/samples/c/harness.c b/unicorn_mode/samples/c/harness.c
index a987b8e1..31416ae2 100644
--- a/unicorn_mode/samples/c/harness.c
+++ b/unicorn_mode/samples/c/harness.c
@@ -28,35 +28,56 @@
#include <unicorn/unicorn.h>
// Path to the file containing the binary to emulate
-#define BINARY_FILE ("simple_target_x86_64")
+#define BINARY_FILE ("persistent_target_x86_64")
// Memory map for the code to be tested
// Arbitrary address where code to test will be loaded
-#define BASE_ADDRESS (0x100000)
-#define CODE_ADDRESS (0x101119)
-#define END_ADDRESS (0x1011d7)
+static const int64_t BASE_ADDRESS = 0x100000;
+static const int64_t CODE_ADDRESS = 0x101139;
+static const int64_t END_ADDRESS = 0x10120d;
// Address of the stack (Some random address again)
-#define STACK_ADDRESS (((int64_t) 0x01) << 58)
+static const int64_t STACK_ADDRESS = (((int64_t) 0x01) << 58);
// Size of the stack (arbitrarily chosen, just make it big enough)
-#define STACK_SIZE (0x10000)
+static const int64_t STACK_SIZE = 0x10000;
// Location where the input will be placed (make sure the emulated program knows this somehow, too ;) )
-#define INPUT_LOCATION (0x10000)
+static const int64_t INPUT_LOCATION = 0x10000;
// Inside the location, we have an ofset in our special case
-#define INPUT_OFFSET (0x16)
+static const int64_t INPUT_OFFSET = 0x16;
// Maximum allowable size of mutated data from AFL
-#define INPUT_SIZE_MAX (0x10000)
+static const int64_t INPUT_SIZE_MAX = 0x10000;
// Alignment for unicorn mappings (seems to be needed)
-#define ALIGNMENT ((uint64_t) 0x1000)
+static const int64_t ALIGNMENT = 0x1000;
+
+// In our special case, we emulate main(), so argc is needed.
+static const uint64_t EMULATED_ARGC = 2;
+
+// The return from our fake strlen
+static size_t current_input_len = 0;
static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
}
-static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
-{
+static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
}
+/*
+The sample uses strlen, since we don't have a loader or libc, we'll fake it.
+We know the strlen will return the lenght of argv[1] that we just planted.
+It will be a lot faster than an actual strlen for this specific purpose.
+*/
+static void hook_strlen(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
+ //Hook
+ //116b: e8 c0 fe ff ff call 1030 <strlen@plt>
+ // We place the return at RAX
+ //printf("Strlen hook at addr 0x%lx (size: 0x%x), result: %ld\n", address, size, current_input_len);
+ uc_reg_write(uc, UC_X86_REG_RAX, &current_input_len);
+ // We skip the actual call by updating RIP
+ uint64_t next_addr = address + size;
+ uc_reg_write(uc, UC_X86_REG_RIP, &next_addr);
+}
+
/* Unicorn page needs to be 0x1000 aligned, apparently */
static uint64_t pad(uint64_t size) {
if (size % ALIGNMENT == 0) return size;
@@ -99,11 +120,25 @@ static bool place_input_callback(
void *data
){
// printf("Placing input with len %ld to %x\n", input_len, DATA_ADDRESS);
- if (input_len >= INPUT_SIZE_MAX - INPUT_OFFSET) {
- // Test input too long, ignore this testcase
+ if (input_len < 1 || input_len >= INPUT_SIZE_MAX - INPUT_OFFSET) {
+ // Test input too short or too long, ignore this testcase
return false;
}
+
+ // For persistent mode, we have to set up stack and memory each time.
+ uc_reg_write(uc, UC_X86_REG_RIP, &CODE_ADDRESS); // Set the instruction pointer back
+ // Set up the function parameters accordingly RSI, RDI (see calling convention/disassembly)
+ uc_reg_write(uc, UC_X86_REG_RSI, &INPUT_LOCATION); // argv
+ uc_reg_write(uc, UC_X86_REG_RDI, &EMULATED_ARGC); // argc == 2
+
+ // We need a valid c string, make sure it never goes out of bounds.
+ input[input_len-1] = '\0';
+ // Write the testcase to unicorn.
uc_mem_write(uc, INPUT_LOCATION + INPUT_OFFSET, input, input_len);
+
+ // store input_len for the faux strlen hook
+ current_input_len = input_len;
+
return true;
}
@@ -187,12 +222,7 @@ int main(int argc, char **argv, char **envp) {
uc_mem_write(uc, 0x10008, "\x16\x00\x01", 3); // little endian of 0x10016, see above
- // Set up the function parameters accordingly RSI, RDI (see calling convention/disassembly)
- uint64_t input_location = INPUT_LOCATION;
- uc_reg_write(uc, UC_X86_REG_RSI, &input_location); // argv
- uint64_t emulated_argc = 2;
- uc_reg_write(uc, UC_X86_REG_RDI, &emulated_argc); // argc == 2
-
+
// If we want tracing output, set the callbacks here
if (tracing) {
// tracing all basic blocks with customized callback
@@ -200,6 +230,11 @@ int main(int argc, char **argv, char **envp) {
uc_hook_add(uc, &hooks[1], UC_HOOK_CODE, hook_code, NULL, BASE_ADDRESS, BASE_ADDRESS + len - 1);
}
+ // Add our strlen hook (for this specific testcase only)
+ int strlen_hook_pos = BASE_ADDRESS + 0x116b;
+ uc_hook strlen_hook;
+ uc_hook_add(uc, &strlen_hook, UC_HOOK_CODE, hook_strlen, NULL, strlen_hook_pos, strlen_hook_pos);
+
printf("Starting to fuzz :)\n");
fflush(stdout);
@@ -211,9 +246,9 @@ int main(int argc, char **argv, char **envp) {
&end_address, // Where to exit (this is an array)
1, // Count of end addresses
NULL, // Optional calback to run after each exec
- false,
- 1, // For persistent mode: How many rounds to run
- NULL
+ false, // true, if the optional callback should be run also for non-crashes
+ 100, // For persistent mode: How many rounds to run
+ NULL // additional data pointer
);
switch(afl_ret) {
case UC_AFL_RET_ERROR:
diff --git a/unicorn_mode/samples/c/persistent_target.c b/unicorn_mode/samples/c/persistent_target.c
new file mode 100644
index 00000000..5b866f86
--- /dev/null
+++ b/unicorn_mode/samples/c/persistent_target.c
@@ -0,0 +1,39 @@
+/*
+ * Sample target file to test afl-unicorn fuzzing capabilities.
+ * This is a very trivial example that will crash pretty easily
+ * in several different exciting ways.
+ *
+ * Input is assumed to come from a buffer located at DATA_ADDRESS
+ * (0x00300000), so make sure that your Unicorn emulation of this
+ * puts user data there.
+ *
+ * Written by Nathan Voss <njvoss99@gmail.com>
+ * Adapted by Lukas Seidel <seidel.1@campus.tu-berlin.de>
+ */
+#include <stdint.h>
+#include <string.h>
+
+
+int main(int argc, char** argv) {
+ if (argc < 2) return -1;
+
+ char *data_buf = argv[1];
+ uint64_t data_len = strlen(data_buf);
+ if (data_len < 20) return -2;
+
+ for (; data_len --> 0 ;) {
+ if (data_len >= 18) continue;
+ if (data_len > 2 && data_len < 18) {
+ ((char *)data_len)[(uint64_t)data_buf] = data_buf[data_len + 1];
+ } else if (data_buf[9] == 0x90 && data_buf[10] != 0x00 && data_buf[11] == 0x90) {
+ // Cause a crash if data[10] is not zero, but [9] and [11] are zero
+ unsigned char invalid_read = *(unsigned char *) 0x00000000;
+ }
+ }
+ if (data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) {
+ // Cause an 'invalid read' crash if (0x10 < data[0] < 0x20) and data[1] > data[2]
+ unsigned char invalid_read = *(unsigned char *) 0x00000000;
+ }
+
+ return 0;
+}
diff --git a/unicorn_mode/samples/c/persistent_target_x86_64 b/unicorn_mode/samples/c/persistent_target_x86_64
new file mode 100644
index 00000000..22e04357
--- /dev/null
+++ b/unicorn_mode/samples/c/persistent_target_x86_64
Binary files differ
diff --git a/unicorn_mode/samples/persistent/.gitignore b/unicorn_mode/samples/persistent/.gitignore
new file mode 100644
index 00000000..3e446132
--- /dev/null
+++ b/unicorn_mode/samples/persistent/.gitignore
@@ -0,0 +1,3 @@
+harness
+harness-debug
+out
diff --git a/unicorn_mode/samples/persistent/COMPILE.md b/unicorn_mode/samples/persistent/COMPILE.md
new file mode 100644
index 00000000..781f15c0
--- /dev/null
+++ b/unicorn_mode/samples/persistent/COMPILE.md
@@ -0,0 +1,24 @@
+# C Sample
+
+This shows a simple persistent harness for unicornafl in C
+In contrast to the normal c harness, this harness manually resets the unicorn state on each new input.
+Thanks to this, we can rerun the testcase in unicorn multiple times, without the need to fork again.
+
+## Compiling sample.c
+
+The target can be built using the `make` command.
+Just make sure you have built unicorn support first:
+```bash
+cd /path/to/afl/unicorn_mode
+./build_unicorn_support.sh
+```
+
+## Compiling persistent_target.c
+
+You don't need to compile persistent_target.c since a X86_64 binary version is
+pre-built and shipped in this sample folder. This file documents how the binary
+was built in case you want to rebuild it or recompile it for any reason.
+
+The pre-built binary (persistent_target_x86_64.bin) was built using -g -O0 in gcc.
+
+We then load the binary we execute the main function directly.
diff --git a/unicorn_mode/samples/persistent/Makefile b/unicorn_mode/samples/persistent/Makefile
new file mode 100644
index 00000000..fe100490
--- /dev/null
+++ b/unicorn_mode/samples/persistent/Makefile
@@ -0,0 +1,42 @@
+# UnicornAFL Usage
+# Original Unicorn Example Makefile by Nguyen Anh Quynh <aquynh@gmail.com>, 2015
+# Adapted for AFL++ by domenukk <domenukk@gmail.com>, 2020
+
+UNAME_S := $(shell uname -s)
+
+LIBDIR = ../../unicornafl
+BIN_EXT =
+AR_EXT = a
+
+# Verbose output?
+V ?= 0
+
+CFLAGS += -Wall -Werror -I../../unicornafl/include
+
+LDFLAGS += -L$(LIBDIR) -lpthread -lm
+ifeq ($(UNAME_S), Linux)
+LDFLAGS += -lrt
+endif
+
+ifneq ($(CROSS),)
+CC = $(CROSS)gcc
+endif
+
+.PHONY: all clean
+
+all: harness
+
+clean:
+ rm -rf *.o harness harness-debug
+
+harness.o: harness.c ../../unicornafl/include/unicorn/*.h
+ ${CC} ${CFLAGS} -O3 -c $<
+
+harness-debug.o: harness.c ../../unicornafl/include/unicorn/*.h
+ ${CC} ${CFLAGS} -g -c $< -o $@
+
+harness: harness.o
+ ${CC} -L${LIBDIR} $< ../../unicornafl/libunicornafl.a $(LDFLAGS) -o $@
+
+debug: harness-debug.o
+ ${CC} -L${LIBDIR} $< ../../unicornafl/libunicornafl.a $(LDFLAGS) -o harness-debug
diff --git a/unicorn_mode/samples/persistent/harness.c b/unicorn_mode/samples/persistent/harness.c
new file mode 100644
index 00000000..d8ebffbc
--- /dev/null
+++ b/unicorn_mode/samples/persistent/harness.c
@@ -0,0 +1,269 @@
+/*
+ Persistent test harness for AFL++'s unicornafl c mode.
+
+ This loads the persistent_target.bin binary (precompiled as X86_64 code) into
+ Unicorn's memory map for emulation, places the specified input into
+ the argv buffer (handed in as first parameter), and executes 'main()'.
+ Any crashes during emulation will automatically be handled by the afl-fuzz() function.
+
+ Run under AFL as follows:
+
+ $ cd <afl_path>/unicorn_mode/samples/persistent/
+ $ make
+ $ ../../../afl-fuzz -m none -i sample_inputs -o out -- ./harness @@
+
+ (Re)run a simgle input with block tracing using:
+
+ $ ./harness -t [inputfile]
+*/
+
+// This is not your everyday Unicorn.
+#define UNICORN_AFL
+
+#include <string.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <unicorn/unicorn.h>
+
+// Path to the file containing the binary to emulate
+#define BINARY_FILE ("persistent_target_x86_64")
+
+// Memory map for the code to be tested
+// Arbitrary address where code to test will be loaded
+static const int64_t BASE_ADDRESS = 0x100000;
+static const int64_t CODE_ADDRESS = 0x101139;
+static const int64_t END_ADDRESS = 0x10120d;
+// Address of the stack (Some random address again)
+static const int64_t STACK_ADDRESS = (((int64_t) 0x01) << 58);
+// Size of the stack (arbitrarily chosen, just make it big enough)
+static const int64_t STACK_SIZE = 0x10000;
+// Location where the input will be placed (make sure the emulated program knows this somehow, too ;) )
+static const int64_t INPUT_LOCATION = 0x10000;
+// Inside the location, we have an ofset in our special case
+static const int64_t INPUT_OFFSET = 0x16;
+// Maximum allowable size of mutated data from AFL
+static const int64_t INPUT_SIZE_MAX = 0x10000;
+// Alignment for unicorn mappings (seems to be needed)
+static const int64_t ALIGNMENT = 0x1000;
+
+// In our special case, we emulate main(), so argc is needed.
+static const uint64_t EMULATED_ARGC = 2;
+
+// The return from our fake strlen
+static size_t current_input_len = 0;
+
+static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
+ printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
+}
+
+static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
+ printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
+}
+
+/*
+The sample uses strlen, since we don't have a loader or libc, we'll fake it.
+We know the strlen will return the lenght of argv[1] that we just planted.
+It will be a lot faster than an actual strlen for this specific purpose.
+*/
+static void hook_strlen(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
+ //Hook
+ //116b: e8 c0 fe ff ff call 1030 <strlen@plt>
+ // We place the return at RAX
+ uc_reg_write(uc, UC_X86_REG_RAX, &current_input_len);
+ // We skip the actual call by updating RIP
+ //printf("Strlen hook at addr 0x%lx (size: 0x%x), result: %ld\n", address, size, current_input_len);
+ uint64_t next_addr = address + size;
+ uc_reg_write(uc, UC_X86_REG_RIP, &next_addr);
+}
+
+/* Unicorn page needs to be 0x1000 aligned, apparently */
+static uint64_t pad(uint64_t size) {
+ if (size % ALIGNMENT == 0) return size;
+ return ((size / ALIGNMENT) + 1) * ALIGNMENT;
+}
+
+/* returns the filesize in bytes, -1 or error. */
+static off_t afl_mmap_file(char *filename, char **buf_ptr) {
+
+ off_t ret = -1;
+
+ int fd = open(filename, O_RDONLY);
+
+ struct stat st = {0};
+ if (fstat(fd, &st)) goto exit;
+
+ off_t in_len = st.st_size;
+ if (in_len == -1) {
+ /* This can only ever happen on 32 bit if the file is exactly 4gb. */
+ fprintf(stderr, "Filesize of %s too large", filename);
+ goto exit;
+ }
+
+ *buf_ptr = mmap(0, in_len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+
+ if (*buf_ptr != MAP_FAILED) ret = in_len;
+
+exit:
+ close(fd);
+ return ret;
+
+}
+
+/* Place the input at the right spot inside unicorn */
+static bool place_input_callback(
+ uc_engine *uc,
+ char *input,
+ size_t input_len,
+ uint32_t persistent_round,
+ void *data
+){
+ // printf("Placing input with len %ld to %x\n", input_len, DATA_ADDRESS);
+ if (input_len < 1 || input_len >= INPUT_SIZE_MAX - INPUT_OFFSET) {
+ // Test input too short or too long, ignore this testcase
+ return false;
+ }
+
+ // For persistent mode, we have to set up stack and memory each time.
+ uc_reg_write(uc, UC_X86_REG_RIP, &CODE_ADDRESS); // Set the instruction pointer back
+ // Set up the function parameters accordingly RSI, RDI (see calling convention/disassembly)
+ uc_reg_write(uc, UC_X86_REG_RSI, &INPUT_LOCATION); // argv
+ uc_reg_write(uc, UC_X86_REG_RDI, &EMULATED_ARGC); // argc == 2
+
+ // We need a valid c string, make sure it never goes out of bounds.
+ input[input_len-1] = '\0';
+ // Write the testcase to unicorn.
+ uc_mem_write(uc, INPUT_LOCATION + INPUT_OFFSET, input, input_len);
+
+ // store input_len for the faux strlen hook
+ current_input_len = input_len;
+
+ return true;
+}
+
+static void mem_map_checked(uc_engine *uc, uint64_t addr, size_t size, uint32_t mode) {
+ size = pad(size);
+ //printf("SIZE %lx, align: %lx\n", size, ALIGNMENT);
+ uc_err err = uc_mem_map(uc, addr, size, mode);
+ if (err != UC_ERR_OK) {
+ printf("Error mapping %ld bytes at 0x%lx: %s (mode: %d)\n", size, addr, uc_strerror(err), mode);
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv, char **envp) {
+ if (argc == 1) {
+ printf("Test harness for simple_target.bin. Usage: harness [-t] <inputfile>\n");
+ exit(1);
+ }
+ bool tracing = false;
+ char *filename = argv[1];
+ if (argc > 2 && !strcmp(argv[1], "-t")) {
+ tracing = true;
+ filename = argv[2];
+ }
+
+ uc_engine *uc;
+ uc_err err;
+ uc_hook hooks[2];
+ char *file_contents;
+
+ // Initialize emulator in X86_64 mode
+ err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
+ if (err) {
+ printf("Failed on uc_open() with error returned: %u (%s)\n",
+ err, uc_strerror(err));
+ return -1;
+ }
+
+ printf("Loading data input from %s\n", BINARY_FILE);
+ off_t len = afl_mmap_file(BINARY_FILE, &file_contents);
+ if (len < 0) {
+ perror("Could not read binary to emulate");
+ return -2;
+ }
+ if (len == 0) {
+ fprintf(stderr, "File at '%s' is empty\n", BINARY_FILE);
+ return -3;
+ }
+
+ // Map memory.
+ mem_map_checked(uc, BASE_ADDRESS, len, UC_PROT_ALL);
+ printf("Len: %lx", len);
+ fflush(stdout);
+
+ // write machine code to be emulated to memory
+ if (uc_mem_write(uc, BASE_ADDRESS, file_contents, len) != UC_ERR_OK) {
+ printf("Error writing to CODE");
+ }
+
+ // Release copied contents
+ munmap(file_contents, len);
+
+ // Set the program counter to the start of the code
+ uint64_t start_address = CODE_ADDRESS; // address of entry point of main()
+ uint64_t end_address = END_ADDRESS; // Address of last instruction in main()
+ uc_reg_write(uc, UC_X86_REG_RIP, &start_address); // address of entry point of main()
+
+ // Setup the Stack
+ mem_map_checked(uc, STACK_ADDRESS - STACK_SIZE, STACK_SIZE, UC_PROT_READ | UC_PROT_WRITE);
+ uint64_t stack_val = STACK_ADDRESS;
+ printf("%ld", stack_val);
+ uc_reg_write(uc, UC_X86_REG_RSP, &stack_val);
+
+ // reserve some space for our input data
+ mem_map_checked(uc, INPUT_LOCATION, INPUT_SIZE_MAX, UC_PROT_READ);
+
+ // build a "dummy" argv with lenth 2 at 0x10000:
+ // 0x10000 argv[0] NULL
+ // 0x10008 argv[1] (char *)0x10016 --. points to the next offset.
+ // 0x10016 argv[1][0], ... <-^ contains the acutal input data. (INPUT_LOCATION + INPUT_OFFSET)
+
+ uc_mem_write(uc, 0x10008, "\x16\x00\x01", 3); // little endian of 0x10016, see above
+
+
+ // If we want tracing output, set the callbacks here
+ if (tracing) {
+ // tracing all basic blocks with customized callback
+ uc_hook_add(uc, &hooks[0], UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
+ uc_hook_add(uc, &hooks[1], UC_HOOK_CODE, hook_code, NULL, BASE_ADDRESS, BASE_ADDRESS + len - 1);
+ }
+
+ // Add our strlen hook (for this specific testcase only)
+ int strlen_hook_pos = BASE_ADDRESS + 0x116b;
+ uc_hook strlen_hook;
+ uc_hook_add(uc, &strlen_hook, UC_HOOK_CODE, hook_strlen, NULL, strlen_hook_pos, strlen_hook_pos);
+
+ printf("Starting to fuzz :)\n");
+ fflush(stdout);
+
+ // let's gooo
+ uc_afl_ret afl_ret = uc_afl_fuzz(
+ uc, // The unicorn instance we prepared
+ filename, // Filename of the input to process. In AFL this is usually the '@@' placeholder, outside it's any input file.
+ place_input_callback, // Callback that places the input (automatically loaded from the file at filename) in the unicorninstance
+ &end_address, // Where to exit (this is an array)
+ 1, // Count of end addresses
+ NULL, // Optional calback to run after each exec
+ false, // true, if the optional callback should be run also for non-crashes
+ 1000, // For persistent mode: How many rounds to run
+ NULL // additional data pointer
+ );
+ switch(afl_ret) {
+ case UC_AFL_RET_ERROR:
+ printf("Error starting to fuzz");
+ return -3;
+ break;
+ case UC_AFL_RET_NO_AFL:
+ printf("No AFL attached - We are done with a single run.");
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/unicorn_mode/samples/persistent/persistent_target.c b/unicorn_mode/samples/persistent/persistent_target.c
new file mode 100644
index 00000000..5b866f86
--- /dev/null
+++ b/unicorn_mode/samples/persistent/persistent_target.c
@@ -0,0 +1,39 @@
+/*
+ * Sample target file to test afl-unicorn fuzzing capabilities.
+ * This is a very trivial example that will crash pretty easily
+ * in several different exciting ways.
+ *
+ * Input is assumed to come from a buffer located at DATA_ADDRESS
+ * (0x00300000), so make sure that your Unicorn emulation of this
+ * puts user data there.
+ *
+ * Written by Nathan Voss <njvoss99@gmail.com>
+ * Adapted by Lukas Seidel <seidel.1@campus.tu-berlin.de>
+ */
+#include <stdint.h>
+#include <string.h>
+
+
+int main(int argc, char** argv) {
+ if (argc < 2) return -1;
+
+ char *data_buf = argv[1];
+ uint64_t data_len = strlen(data_buf);
+ if (data_len < 20) return -2;
+
+ for (; data_len --> 0 ;) {
+ if (data_len >= 18) continue;
+ if (data_len > 2 && data_len < 18) {
+ ((char *)data_len)[(uint64_t)data_buf] = data_buf[data_len + 1];
+ } else if (data_buf[9] == 0x90 && data_buf[10] != 0x00 && data_buf[11] == 0x90) {
+ // Cause a crash if data[10] is not zero, but [9] and [11] are zero
+ unsigned char invalid_read = *(unsigned char *) 0x00000000;
+ }
+ }
+ if (data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) {
+ // Cause an 'invalid read' crash if (0x10 < data[0] < 0x20) and data[1] > data[2]
+ unsigned char invalid_read = *(unsigned char *) 0x00000000;
+ }
+
+ return 0;
+}
diff --git a/unicorn_mode/samples/persistent/persistent_target_x86_64 b/unicorn_mode/samples/persistent/persistent_target_x86_64
new file mode 100644
index 00000000..22e04357
--- /dev/null
+++ b/unicorn_mode/samples/persistent/persistent_target_x86_64
Binary files differ
diff --git a/unicorn_mode/samples/persistent/sample_all.sh b/unicorn_mode/samples/persistent/sample_all.sh
new file mode 100644
index 00000000..01daf365
--- /dev/null
+++ b/unicorn_mode/samples/persistent/sample_all.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+[ -z "${UNAME}" ] && UNAME=$(uname)
+
+DIR=`dirname $0`
+
+if [ "$UNAME" = Darwin ]; then
+ export DYLD_LIBRARY_PATH=../../unicorn
+else
+ export LD_LIBRARY_PATH=../../unicorn
+fi
+
+
+
+if [ ! test -e $DIR/harness]; then
+ echo "[!] harness not found in $DIR"
+ exit 1
+fi \ No newline at end of file
diff --git a/unicorn_mode/samples/persistent/sample_inputs/sample1.bin b/unicorn_mode/samples/persistent/sample_inputs/sample1.bin
new file mode 100644
index 00000000..85df5078
--- /dev/null
+++ b/unicorn_mode/samples/persistent/sample_inputs/sample1.bin
@@ -0,0 +1 @@
+abcd \ No newline at end of file
diff --git a/unicorn_mode/samples/persistent/sample_inputs/sample2.bin b/unicorn_mode/samples/persistent/sample_inputs/sample2.bin
new file mode 100644
index 00000000..f76dd238
--- /dev/null
+++ b/unicorn_mode/samples/persistent/sample_inputs/sample2.bin
Binary files differ
diff --git a/unicorn_mode/samples/persistent/sample_inputs/sample3.bin b/unicorn_mode/samples/persistent/sample_inputs/sample3.bin
new file mode 100644
index 00000000..6b2aaa76
--- /dev/null
+++ b/unicorn_mode/samples/persistent/sample_inputs/sample3.bin
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/unicorn_mode/samples/persistent/sample_inputs/sample4.bin b/unicorn_mode/samples/persistent/sample_inputs/sample4.bin
new file mode 100644
index 00000000..71bd63e6
--- /dev/null
+++ b/unicorn_mode/samples/persistent/sample_inputs/sample4.bin
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/unicorn_mode/samples/persistent/sample_inputs/sample5.bin b/unicorn_mode/samples/persistent/sample_inputs/sample5.bin
new file mode 100644
index 00000000..aed2973e
--- /dev/null
+++ b/unicorn_mode/samples/persistent/sample_inputs/sample5.bin
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/unicorn_mode/samples/c/simple_target.c b/unicorn_mode/samples/persistent/simple_target_noncrashing.c
index dbf10911..00764473 100644
--- a/unicorn_mode/samples/c/simple_target.c
+++ b/unicorn_mode/samples/persistent/simple_target_noncrashing.c
@@ -19,12 +19,11 @@ int main(int argc, char** argv) {
char *data_buf = argv[1];
+ if len(data_buf < 20) {
if (data_buf[20] != 0) {
- // Cause an 'invalid read' crash if data[0..3] == '\x01\x02\x03\x04'
- unsigned char invalid_read = *(unsigned char *) 0x00000000;
+ printf("Not crashing");
} else if (data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) {
- // Cause an 'invalid read' crash if (0x10 < data[0] < 0x20) and data[1] > data[2]
- unsigned char invalid_read = *(unsigned char *) 0x00000000;
+ printf("Also not crashing with databuf[0] == %c", data_buf[0])
} else if (data_buf[9] == 0x00 && data_buf[10] != 0x00 && data_buf[11] == 0x00) {
// Cause a crash if data[10] is not zero, but [9] and [11] are zero
unsigned char invalid_read = *(unsigned char *) 0x00000000;
diff --git a/unicorn_mode/samples/persistent/simple_target_x86_64 b/unicorn_mode/samples/persistent/simple_target_x86_64
new file mode 100644
index 00000000..560264fd
--- /dev/null
+++ b/unicorn_mode/samples/persistent/simple_target_x86_64
Binary files differ