Merge branch 'master' of github.com:vanhauser-thc/AFLplusplus

This commit is contained in:
Dominik Maier
2020-01-27 13:30:11 +01:00
52 changed files with 1709 additions and 670 deletions

View File

@ -9,6 +9,9 @@ RUN apt-get update && apt-get install -y \
clang \ clang \
clang-9 \ clang-9 \
flex \ flex \
git \
python3.7 \
python3.7-dev \
gcc-9 \ gcc-9 \
gcc-9-plugin-dev \ gcc-9-plugin-dev \
gcc-9-multilib \ gcc-9-multilib \
@ -23,10 +26,12 @@ RUN apt-get update && apt-get install -y \
ca-certificates \ ca-certificates \
libpixman-1-dev \ libpixman-1-dev \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
ARG CC=gcc-9 ARG CC=gcc-9
ARG CXX=g++-9 ARG CXX=g++-9
ARG LLVM_CONFIG=llvm-config-9 ARG LLVM_CONFIG=llvm-config-9
COPY . /app
RUN cd /app && make clean && make distrib && \ RUN git clone https://github.com/vanhauser-thc/AFLplusplus
make install && cd .. && rm -rf /app
WORKDIR /work RUN cd AFLplusplus && make clean && make distrib && \
make install && cd .. && rm -rf AFLplusplus

View File

@ -29,27 +29,51 @@ VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
# PROGS intentionally omit afl-as, which gets installed elsewhere. # PROGS intentionally omit afl-as, which gets installed elsewhere.
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze 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) MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8)
CFLAGS ?= -O3 -funroll-loops 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"
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ \ CFLAGS_FLTO ?= -flto=full
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ else
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
CFLAGS_FLTO ?= -flto=thin
else
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
CFLAGS_FLTO ?= -flto
endif
endif
endif
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
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)\" \
-DDOC_PATH=\"$(DOC_PATH)\" -Wno-unused-function -DDOC_PATH=\"$(DOC_PATH)\" -Wno-unused-function
AFL_FUZZ_FILES = $(wildcard src/afl-fuzz*.c) 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_INCLUDE ?= $(shell python3.7m-config --includes)
PYTHON_LIB ?= $(shell python3.7m-config --ldflags) PYTHON_LIB ?= $(shell python3.7m-config --ldflags)
PYTHON_VERSION = 3.7m PYTHON_VERSION = 3.7m
else 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_INCLUDE ?= $(shell python3.7-config --includes)
PYTHON_LIB ?= $(shell python3.7-config --ldflags) PYTHON_LIB ?= $(shell python3.7-config --ldflags)
PYTHON_VERSION = 3.7 PYTHON_VERSION = 3.7
else 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_INCLUDE ?= $(shell python2.7-config --includes)
PYTHON_LIB ?= $(shell python2.7-config --ldflags) PYTHON_LIB ?= $(shell python2.7-config --ldflags)
PYTHON_VERSION = 2.7 PYTHON_VERSION = 2.7
@ -61,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/python3.7 && echo /usr/include/python3.7)
PYTHON_INCLUDE ?= $(shell test -e /usr/include/python2.7 && echo /usr/include/python2.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_VERSION ?= 3.7m
PYTHON_LIB ?= -lpython3.7m PYTHON_LIB ?= -lpython3.7m
else else
ifneq "($filter %3.7, $(PYTHON_INCLUDE))" "" ifneq "$(filter %3.7, $(PYTHON_INCLUDE))" ""
PYTHON_VERSION ?= 3.7 PYTHON_VERSION ?= 3.7
else else
ifneq "($filter %2.7, $(PYTHON_INCLUDE))" "" ifneq "$(filter %2.7, $(PYTHON_INCLUDE))" ""
PYTHON_VERSION ?= 2.7 PYTHON_VERSION ?= 2.7
PYTHON_LIB ?= -lpython2.7 PYTHON_LIB ?= -lpython2.7
else else
@ -229,31 +253,31 @@ afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
ln -sf afl-as as ln -sf afl-as as
src/afl-common.o : src/afl-common.c include/common.h src/afl-common.o : src/afl-common.c include/common.h
$(CC) $(CFLAGS) -c src/afl-common.c -o src/afl-common.o $(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-common.c -o src/afl-common.o
src/afl-forkserver.o : src/afl-forkserver.c include/forkserver.h src/afl-forkserver.o : src/afl-forkserver.c include/forkserver.h
$(CC) $(CFLAGS) -c src/afl-forkserver.c -o src/afl-forkserver.o $(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-forkserver.c -o src/afl-forkserver.o
src/afl-sharedmem.o : src/afl-sharedmem.c include/sharedmem.h src/afl-sharedmem.o : src/afl-sharedmem.c include/sharedmem.h
$(CC) $(CFLAGS) -c src/afl-sharedmem.c -o src/afl-sharedmem.o $(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-sharedmem.c -o src/afl-sharedmem.o
radamsa: src/third_party/libradamsa/libradamsa.so radamsa: src/third_party/libradamsa/libradamsa.so
cp src/third_party/libradamsa/libradamsa.so . cp src/third_party/libradamsa/libradamsa.so .
src/third_party/libradamsa/libradamsa.so: src/third_party/libradamsa/libradamsa.c src/third_party/libradamsa/radamsa.h src/third_party/libradamsa/libradamsa.so: src/third_party/libradamsa/libradamsa.c src/third_party/libradamsa/radamsa.h
$(MAKE) -C src/third_party/libradamsa/ $(MAKE) -C src/third_party/libradamsa/ CFLAGS="$(CFLAGS)"
afl-fuzz: include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86 afl-fuzz: include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(PYFLAGS) $(LDFLAGS) $(CC) $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(PYFLAGS) $(LDFLAGS)
afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o $(COMM_HDR) | test_x86 afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS) $(CC) $(CFLAGS) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS)
afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86 afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS) $(CC) $(CFLAGS) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS)
afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o $(COMM_HDR) | test_x86 afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS) $(CC) $(CFLAGS) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS)
afl-gotcpu: src/afl-gotcpu.c $(COMM_HDR) | test_x86 afl-gotcpu: src/afl-gotcpu.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS) $(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)

6
TODO
View File

@ -2,6 +2,12 @@
Roadmap 2.61+: Roadmap 2.61+:
============== ==============
Makefile:
- -march=native -Ofast -flto=full
afl-fuzz:
- sync_fuzzers(): only masters sync from all, slaves only sync from master
gcc_plugin: gcc_plugin:
- laf-intel - laf-intel
- better instrumentation - better instrumentation

864
afl-cmin
View File

@ -1,470 +1,464 @@
#!/usr/bin/env bash #!/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
# #
# american fuzzy lop++ - corpus minimization tool # 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)
# #
# Originally written by Michal Zalewski # uses getopt.awk package from Arnold Robbins
#
# 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.
# #
# 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 :-)
echo "corpus minimization tool for afl-fuzz by Michal Zalewski" # getopt.awk --- Do C library getopt(3) function in awk
echo
######### # External variables:
# SETUP # # 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
# Process command-line options... # Returns:
# -1 at end of options
# "?" for unrecognized option
# <c> a character representing the current option
MEM_LIMIT=200 # Private Data:
TIMEOUT=none # _opti -- index in multiflag option, e.g., -abc
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \ function getopt(argc, argv, options, thisopt, i)
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE {
if (length(options) == 0) # no options given
return -1
while getopts "+i:o:f:m:t:eQUCh" opt; do 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
}
case "$opt" in 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
}
"h") function exists_and_is_executable(binarypath) {
;; return 0 == system("test -f "binarypath" -a -x "binarypath)
}
"i") BEGIN {
IN_DIR="$OPTARG" print "corpus minimization tool for afl++ (awk version)\n"
;;
"o") # defaults
OUT_DIR="$OPTARG" extra_par = ""
;; # process options
"f") Opterr = 1 # default is to diagnose
STDIN_FILE="$OPTARG" Optind = 1 # skip ARGV[0]
;; while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eCQU?")) != -1) {
"m") if (_go_c == "i") {
MEM_LIMIT="$OPTARG" if (!Optarg) usage()
MEM_LIMIT_GIVEN=1 if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
;; in_dir = Optarg
"t") continue
TIMEOUT="$OPTARG" } else
;; if (_go_c == "o") {
"e") if (!Optarg) usage()
EXTRA_PAR="$EXTRA_PAR -e" if (out_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
;; out_dir = Optarg
"C") continue
export AFL_CMIN_CRASHES_ONLY=1 } else
;; if (_go_c == "f") {
"Q") if (!Optarg) usage()
EXTRA_PAR="$EXTRA_PAR -Q" if (stdin_file) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250 stdin_file = Optarg
QEMU_MODE=1 continue
;; } else
"U") if (_go_c == "m") {
EXTRA_PAR="$EXTRA_PAR -U" if (!Optarg) usage()
test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250 if (mem_limit) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
UNICORN_MODE=1 mem_limit = Optarg
;; mem_limit_given = 1
"?") continue
exit 1 } 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
esac if (!mem_limit) mem_limit = 200
if (!timeout) timeout = "none"
done # 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]
}
shift $((OPTIND-1)) # sanity checks
if (!prog_args[0] || !in_dir || !out_dir) usage()
TARGET_BIN="$1" target_bin = prog_args[0]
if [ "$TARGET_BIN" = "" -o "$IN_DIR" = "" -o "$OUT_DIR" = "" ]; then # Do a sanity check to discourage the use of /tmp, since we can't really
# handle this safely from an awk script.
cat 1>&2 <<_EOF_ if (!ENVIRON["AFL_ALLOW_TMP"]) {
Usage: $0 [ options ] -- /path/to/target_app [ ... ] 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]
Required parameters: if (dir ~ /^(\/var)?\/tmp/) {
print "[-] Error: do not use this script in /tmp or /var/tmp." > "/dev/stderr"
exit 1
}
}
delete dirlist
}
-i dir - input directory with the starting corpus # If @@ is specified, but there's no -f, let's come up with a temporary input
-o dir - output directory for minimized files # file name.
Execution control settings: trace_dir = out_dir "/.traces"
-f file - location read by the fuzzed program (stdin) if (!stdin_file) {
-m megs - memory limit for child process ($MEM_LIMIT MB) found_atat = 0
-t msec - run time limit for child process (none) for (prog_args_ind in prog_args) {
-Q - use binary-only instrumentation (QEMU mode) if ("@@" == prog_args[prog_args_ind]) {
-U - use unicorn-based instrumentation (Unicorn mode) 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
}
if (timeout && timeout != "none" && timeout < 10) {
print "[-] Error: dangerously low timeout." > "/dev/stderr"
exit 1
}
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
}
if (0 == system( "test -d "in_dir"/queue" )) {
in_dir = in_dir "/queue"
}
system("rm -rf "trace_dir" 2>/dev/null");
system("rm "out_dir"/id[:_]* 2>/dev/null")
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
}
# 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
}
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
}
Minimization settings: # 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 . -maxdepth 1 -type f -exec stat "stat_format" \\{\\} \\; | sort -n | cut -d' ' -f2-"
while (cmdline | getline) {
infilesSmallToBig[i++] = $0
}
in_count = i
-C - keep crashing inputs, reject everything else first_file = infilesSmallToBig[0]
-e - solve for edge coverage only, ignore hit counts
# Make sure that we're not dealing with a directory.
For additional tips, please consult docs/README. if (0 == system("test -d "in_dir"/"first_file)) {
print "[-] Error: The input directory contains subdirectories - please fix." > "/dev/stderr"
_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 exit 1
fi }
fi if (0 == system("ln "in_dir"/"first_file" "trace_dir"/.link_test")) {
cp_tool = "ln"
} else {
cp_tool = "cp"
}
# If @@ is specified, but there's no -f, let's come up with a temporary input # Make sure that we can actually get anything out of afl-showmap before we
# file name. # waste too much time.
TRACE_DIR="$OUT_DIR/.traces" print "[*] Testing the target binary..."
if [ "$STDIN_FILE" = "" ]; then 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")
}
if echo "$*" | grep -qF '@@'; then first_count = 0
STDIN_FILE="$TRACE_DIR/.cur_input"
fi
fi runtest = trace_dir"/.run_test"
while ((getline < runtest) > 0) {
++first_count
}
# Check for obvious errors. if (first_count) {
print "[+] OK, "first_count" tuples recorded."
if [ ! "$MEM_LIMIT" = "none" ]; then } else {
print "[-] Error: no instrumentation output detected (perhaps crash or timeout)." > "/dev/stderr"
if [ "$MEM_LIMIT" -lt "5" ]; then if (!ENVIRON["AFL_KEEP_TRACES"]) {
echo "[-] Error: dangerously low memory limit." 1>&2 system("rm -rf "trace_dir" 2>/dev/null")
}
exit 1 exit 1
fi }
fi # Let's roll!
if [ ! "$TIMEOUT" = "none" ]; then #############################
# STEP 1: Collecting traces #
if [ "$TIMEOUT" -lt "10" ]; then #############################
echo "[-] Error: dangerously low timeout." 1>&2
exit 1 print "[*] Obtaining traces for "in_count" input files in '"in_dir"'."
fi
cur = 0;
fi if (!stdin_file) {
while (cur < in_count) {
if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then fn = infilesSmallToBig[cur]
++cur;
TNEW="`which "$TARGET_BIN" 2>/dev/null`" 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"\"")
if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then }
echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2 } else {
exit 1 while (cur < in_count) {
fi fn = infilesSmallToBig[cur]
++cur
TARGET_BIN="$TNEW" printf "\r Processing file "cur"/"in_count
system("cp "in_dir"/"fn" "stdin_file)
fi 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")
}
if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$UNICORN_MODE" = "" ]; then }
if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then print ""
echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2
exit 1
fi #######################################################
# STEP 2: register smallest input file for each tuple #
fi # STEP 3: copy that file (at most once) #
#######################################################
if [ ! -d "$IN_DIR" ]; then
echo "[-] Error: directory '$IN_DIR' not found." 1>&2 print "[*] Processing traces for input files in '"in_dir"'."
exit 1
fi cur = 0
out_count = 0
test -d "$IN_DIR/queue" && IN_DIR="$IN_DIR/queue" tuple_count = 0
find "$OUT_DIR" -name 'id[:_]*' -maxdepth 1 -exec rm -- {} \; 2>/dev/null while (cur < in_count) {
rm -rf "$TRACE_DIR" 2>/dev/null fn = infilesSmallToBig[cur]
++cur
rmdir "$OUT_DIR" 2>/dev/null printf "\r Processing file "cur"/"in_count
# create path for the trace file from afl-showmap
if [ -d "$OUT_DIR" ]; then tracefile_path = trace_dir"/"fn
echo "[-] Error: directory '$OUT_DIR' exists and is not empty - delete it first." 1>&2 # gather all keys, and count them
exit 1 while ((getline line < tracefile_path) > 0) {
fi key = line
if (!(key in key_count)) {
mkdir -m 700 -p "$TRACE_DIR" || exit 1 ++tuple_count
}
if [ ! "$STDIN_FILE" = "" ]; then ++key_count[key]
rm -f "$STDIN_FILE" || exit 1 if (! (key in best_file)) {
touch "$STDIN_FILE" || exit 1 # this is the best file for this key
fi best_file[key] = fn
# copy file unless already done
if [ "$AFL_PATH" = "" ]; then if (! (fn in file_already_copied)) {
SHOWMAP="${0%/afl-cmin}/afl-showmap" system(cp_tool" "in_dir"/"fn" "out_dir"/"fn)
else file_already_copied[fn] = ""
SHOWMAP="$AFL_PATH/afl-showmap" ++out_count
fi }
}
if [ ! -x "$SHOWMAP" ]; then }
echo "[-] Error: can't find 'afl-showmap' - please set AFL_PATH." 1>&2 close(tracefile_path)
rm -rf "$TRACE_DIR" }
exit 1
fi print ""
print "[+] Found "tuple_count" unique tuples across "in_count" files."
IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`))
if (out_count == 1) {
if [ "$IN_COUNT" = "0" ]; then print "[!] WARNING: All test cases had the same traces, check syntax!"
echo "[+] Hmm, no inputs in the target directory. Nothing to be done." }
rm -rf "$TRACE_DIR" print "[+] Narrowed down to "out_count" files, saved in '"out_dir"'."
exit 1
fi if (!ENVIRON["AFL_KEEP_TRACES"]) {
system("rm -rf "trace_dir" 2>/dev/null")
FIRST_FILE=`ls "$IN_DIR" | head -1` }
# Make sure that we're not dealing with a directory. exit 0
}
if [ -d "$IN_DIR/$FIRST_FILE" ]; then EOF
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

470
afl-cmin.bash Executable file
View File

@ -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

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
test "$1" = "-h" && { test "$1" = "-h" && {
echo afl-system-config by Marc Heuse echo 'afl-system-config by Marc Heuse <mh@mh-sec.de>'
echo echo
echo $0 echo $0
echo echo
@ -12,55 +12,72 @@ test "$1" = "-h" && {
exit 1 exit 1
} }
DONE=
PLATFORM=`uname -s` 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 if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
echo Error you need to be root to run this echo "Warning: you need to be root to run this!"
exit 1 # we do not exit as other mechanisms exist that allows to do this than
# being root. let the errors speak for themselves.
fi fi
if [ "$PLATFORM" = "Linux" ] ; then if [ "$PLATFORM" = "Linux" ] ; then
sysctl -w kernel.core_pattern=core {
sysctl -w kernel.randomize_va_space=0 sysctl -w kernel.core_pattern=core
sysctl -w kernel.sched_child_runs_first=1 sysctl -w kernel.randomize_va_space=0
sysctl -w kernel.sched_autogroup_enabled=1 sysctl -w kernel.sched_child_runs_first=1
sysctl -w kernel.sched_migration_cost_ns=50000000 sysctl -w kernel.sched_autogroup_enabled=1
sysctl -w kernel.sched_latency_ns=250000000 sysctl -w kernel.sched_migration_cost_ns=50000000
echo never > /sys/kernel/mm/transparent_hugepage/enabled sysctl -w kernel.sched_latency_ns=250000000
test -e /sys/devices/system/cpu/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/scaling_governor echo never > /sys/kernel/mm/transparent_hugepage/enabled
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/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/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/cpufreq/policy0/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/policy*/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/cpu0/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost test -e /sys/devices/system/cpu/intel_pstate/no_turbo && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
echo test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost
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: } > /dev/null
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"' 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 fi
if [ "$PLATFORM" = "FreeBSD" ] ; then if [ "$PLATFORM" = "FreeBSD" ] ; then
sysctl kern.elf32.aslr.enable=0 {
sysctl kern.elf64.aslr.enable=0 sysctl kern.elf32.aslr.enable=0
echo sysctl kern.elf64.aslr.enable=0
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: } > /dev/null
echo 'sysctl hw.ibrs_disable=1' echo Settings applied.
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 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.' 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 fi
if [ "$PLATFORM" = "OpenBSD" ] ; then if [ "$PLATFORM" = "OpenBSD" ] ; then
echo echo
echo 'System security features cannot be disabled on OpenBSD.' echo 'System security features cannot be disabled on OpenBSD.'
DONE=1
fi fi
if [ "$PLATFORM" = "NetBSD" ] ; then if [ "$PLATFORM" = "NetBSD" ] ; then
echo {
echo It is recommended to enable unprivileged users to set cpu affinity #echo It is recommended to enable unprivileged users to set cpu affinity
echo to be able to use afl-gotcpu meaningfully. #echo to be able to use afl-gotcpu meaningfully.
/sbin/sysctl -w security.models.extensions.user_set_cpu_affinity=1 /sbin/sysctl -w security.models.extensions.user_set_cpu_affinity=1
} > /dev/null
echo Settings applied.
DONE=1
fi fi
if [ "$PLATFORM" = "Darwin" ] ; then if [ "$PLATFORM" = "Darwin" ] ; then
if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then
echo We unload the default crash reporter here echo We unload the default crash reporter here
SL=/System/Library; PL=com.apple.ReportCrash SL=/System/Library; PL=com.apple.ReportCrash
launchctl unload -w ${SL}/LaunchAgents/${PL}.plist launchctl unload -w ${SL}/LaunchAgents/${PL}.plist
sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist
echo Settings applied.
else
echo Nothing to do.
fi fi
DONE=1
fi fi
echo test -z "$DONE" && echo Error: Unknown platform: $PLATFORM
echo Also use AFL_TMPDIR to use a tmpfs for the input file test -z "$AFL_TMPDIR" && echo Also use AFL_TMPDIR and point it to a tmpfs for the input file caching

View File

@ -17,10 +17,17 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
Version ++2.60d (develop): Version ++2.60d (develop):
-------------------------- --------------------------
- use -march=native if available
- afl-fuzz: - afl-fuzz:
- now prints the real python version support compiled in - now prints the real python version support compiled in
- afl-clang-fast now shows in the help output for which llvm version it - set stronger performance compile options and little tweaks
was compiled for. - 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 :)
- 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 - added fix from Debian project to compile libdislocator and libtokencap

View File

@ -1,14 +1,12 @@
#Fuzzing binary-only programs with afl++
Fuzzing binary-only programs with afl++
=======================================
afl++, libfuzzer and others are great if you have the source code, and afl++, libfuzzer and others are great if you have the source code, and
it allows for very fast and coverage guided fuzzing. it allows for very fast and coverage guided fuzzing.
However, if there is only the binary program and not source code available, However, if there is only the binary program and no source code available,
then standard afl++ (dumb mode) is not effective. then standard `afl-fuzz -n` (dumb mode) is not effective.
The following is a description of how these can be fuzzed with afl++ 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 TL;DR: try DYNINST with afl-dyninst. If it produces too many crashes then
@ -16,36 +14,42 @@ TL;DR: try DYNINST with afl-dyninst. If it produces too many crashes then
!!!!! !!!!!
QEMU ##QEMU
----
Qemu is the "native" solution to the program. Qemu is the "native" solution to the program.
It is available in the ./qemu_mode/ directory and once compiled it can It is available in the ./qemu_mode/ directory and once compiled it can
be accessed by the afl-fuzz -Q command line option. be accessed by the afl-fuzz -Q command line option.
The speed decrease is at about 50% The speed decrease is at about 50%
It is the easiest to use alternative and even works for cross-platform binaries. 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. As it is included in afl++ this needs no URL.
WINE+QEMU
--------- ##WINE+QEMU
Wine mode can run Win32 PE with the QEMU instrumentation. Wine mode can run Win32 PE binaries with the QEMU instrumentation.
It needs Wine, python3 and the pefile python package installed. It needs Wine, python3 and the pefile python package installed.
UNICORN As it is included in afl++ this needs no URL.
-------
##UNICORN
Unicorn is a fork of QEMU. The instrumentation is, therefore, very similar. 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. In contrast to QEMU, Unicorn does not offer a full system or even userland
Runtime environment and/or loaders have to be written from scratch, if needed. emulation. Runtime environment and/or loaders have to be written from scratch,
On top, block chaining has been removed. This means the speed boost introduced in if needed. On top, block chaining has been removed. This means the speed boost
to the patched QEMU Mode of afl++ cannot simply be ported over to Unicorn. introduced in the patched QEMU Mode of afl++ cannot simply be ported over to
For further information, check out ./unicorn_mode.txt. Unicorn. For further information, check out ./unicorn_mode.txt.
As it is included in afl++ this needs no URL.
DYNINST ##DYNINST
-------
Dyninst is a binary instrumentation framework similar to Pintool and Dynamorio Dyninst is a binary instrumentation framework similar to Pintool and Dynamorio
(see far below). However whereas Pintool and Dynamorio work at runtime, dyninst (see far below). However whereas Pintool and Dynamorio work at runtime, dyninst
instruments the target at load time, and then let it run. 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, This is great for some things, e.g. fuzzing, and not so effective for others,
e.g. malware analysis. e.g. malware analysis.
@ -53,9 +57,9 @@ 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. instrumention code in there - and then save the binary.
Afterwards we can just fuzz the newly saved target binary with afl-fuzz. 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 Sounds great? It is. The issue though - it is a non-trivial problem to
insert instructions, which change addresses in the process space, so insert instructions, which change addresses in the process space, so that
everything is still working afterwards. Hence more often than not binaries everything is still working afterwards. Hence more often than not binaries
crash when they are run (because of instrumentation). crash when they are run.
The speed decrease is about 15-35%, depending on the optimization options The speed decrease is about 15-35%, depending on the optimization options
used with afl-dyninst. used with afl-dyninst.
@ -63,11 +67,10 @@ used with afl-dyninst.
So if dyninst works, it is the best option available. Otherwise it just doesn't So if dyninst works, it is the best option available. Otherwise it just doesn't
work well. work well.
https://github.com/vanhauser-thc/afl-dyninst [https://github.com/vanhauser-thc/afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst)
INTEL-PT ##INTEL-PT
--------
If you have a newer Intel CPU, you can make use of Intels processor trace. 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 The big issue with Intel's PT is the small buffer size and the complex
encoding of the debug information collected through PT. encoding of the debug information collected through PT.
@ -77,30 +80,39 @@ the implementation and other factors).
There are two afl intel-pt implementations: There are two afl intel-pt implementations:
1. https://github.com/junxzm1990/afl-pt 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. => this needs Ubuntu 14.04.05 without any updates and the 4.4 kernel.
2. https://github.com/hunter-ht-2018/ptfuzzer 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 => this needs a 4.14 or 4.15 kernel. the "nopti" kernel boot option must
be used. This one is faster than the other. 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
Coresight is ARM's answer to Intel's PT. Coresight is ARM's answer to Intel's PT.
There is no implementation so far which handle coresight and getting 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 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 on embedded systems is difficult. And finding one that has coresight in
the ARM chip is difficult too. the ARM chip is difficult too.
My guess is that it is slower than Qemu, but faster than Intel PT. 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: If anyone finds any coresight implementation for afl please ping me:
vh@thc.org vh@thc.org
PIN & DYNAMORIO ##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 Pintool and Dynamorio are dynamic instrumentation engines, and they can be
used for getting basic block information at runtime. used for getting basic block information at runtime.
Pintool is only available for Intel x32/x64 on Linux, Mac OS and Windows Pintool is only available for Intel x32/x64 on Linux, Mac OS and Windows
@ -115,30 +127,27 @@ Hence Dynamorio is the option to go for if everything fails, and Pintool
only if Dynamorio fails too. only if Dynamorio fails too.
Dynamorio solutions: Dynamorio solutions:
https://github.com/vanhauser-thc/afl-dynamorio * [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/mxmssh/drAFL)
https://github.com/googleprojectzero/winafl/ <= very good but windows only * [https://github.com/googleprojectzero/winafl/](https://github.com/googleprojectzero/winafl/) <= very good but windows only
Pintool solutions: Pintool solutions:
https://github.com/vanhauser-thc/afl-pin * [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/mothran/aflpin)
https://github.com/spinpx/afl_pin_mode <= only old Pintool version supported * [https://github.com/spinpx/afl_pin_mode](https://github.com/spinpx/afl_pin_mode) <= only old Pintool version supported
Non-AFL solutions ##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 ...
There are many binary-only fuzzing frameworks. Some are great for CTFs but don't * QSYM: [https://github.com/sslab-gatech/qsym](https://github.com/sslab-gatech/qsym)
work with large binaries, others are very slow but have good path discovery, * Manticore: [https://github.com/trailofbits/manticore](https://github.com/trailofbits/manticore)
some are very hard to set-up ... * S2E: [https://github.com/S2E](https://github.com/S2E)
* <please send me any missing that are good>
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>
## Closing words
That's it! That's it! News, corrections, updates? Send an email to vh@thc.org
News, corrections, updates?
Email vh@thc.org

View File

@ -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. mode to speed up certain fuzzing jobs.
- post_library - an example of how to build postprocessors for AFL. - 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/ 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 directory and is now available as ../afl-cmin. The LLVM mode has likewise

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>
@ -61,12 +61,16 @@
/* Default memory limit for child process (MB): */ /* Default memory limit for child process (MB): */
#ifndef __NetBSD__
#ifndef WORD_SIZE_64 #ifndef WORD_SIZE_64
#define MEM_LIMIT 25 #define MEM_LIMIT 25
#else #else
#define MEM_LIMIT 50 #define MEM_LIMIT 50
#endif /* ^!WORD_SIZE_64 */ #endif /* ^!WORD_SIZE_64 */
#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): */ /* Default memory limit when running in QEMU mode (MB): */
#define MEM_LIMIT_QEMU 200 #define MEM_LIMIT_QEMU 200

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com> 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com> 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -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) { __attribute__((constructor)) void __dislocator_init(void) {
u8* tmp = (u8*)getenv("AFL_LD_LIMIT_MB"); u8* tmp = (u8*)getenv("AFL_LD_LIMIT_MB");

View File

@ -94,6 +94,28 @@ struct InsTrim : public ModulePass {
} }
// ripped from aflgo
static bool isBlacklisted(const Function *F) {
static const SmallVector<std::string, 4> Blacklist = {
"asan.",
"llvm.",
"sancov.",
"__ubsan_handle_",
};
for (auto const &BlacklistFunc : Blacklist) {
if (F->getName().startswith(BlacklistFunc)) { return true; }
}
return false;
}
bool runOnModule(Module &M) override { bool runOnModule(Module &M) override {
char be_quiet = 0; char be_quiet = 0;
@ -122,19 +144,6 @@ struct InsTrim : public ModulePass {
// this is our default // this is our default
MarkSetOpt = true; 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(); LLVMContext &C = M.getContext();
IntegerType *Int8Ty = IntegerType::getInt8Ty(C); IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C); IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
@ -181,8 +190,7 @@ struct InsTrim : public ModulePass {
if (instFilename.str().empty()) { if (instFilename.str().empty()) {
/* If the original location is empty, try using the inlined location /* If the original location is empty, try using the inlined location */
*/
DILocation *oDILoc = cDILoc->getInlinedAt(); DILocation *oDILoc = cDILoc->getInlinedAt();
if (oDILoc) { if (oDILoc) {
@ -240,6 +248,8 @@ struct InsTrim : public ModulePass {
} }
if (isBlacklisted(&F)) continue;
std::unordered_set<BasicBlock *> MS; std::unordered_set<BasicBlock *> MS;
if (!MarkSetOpt) { if (!MarkSetOpt) {
@ -408,28 +418,19 @@ struct InsTrim : public ModulePass {
IRB.CreateStore(Incr, MapPtrIdx) IRB.CreateStore(Incr, MapPtrIdx)
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); ->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++; total_instr++;
} }
} }
OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n" /*", ratio OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n",
%u%%)."*/
,
total_instr, total_rs, total_hs, total_instr, total_rs, total_hs,
getenv("AFL_HARDEN") getenv("AFL_HARDEN")
? "hardened" ? "hardened"
: ((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) : ((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN"))
? "ASAN/MSAN" ? "ASAN/MSAN"
: "non-hardened") /*, inst_ratio*/); : "non-hardened"));
return false; return false;
} }

View File

@ -65,16 +65,11 @@ void buildCFG(Function *F) {
} }
// uint32_t FakeID = 0;
for (auto S = F->begin(), E = F->end(); S != E; ++S) { for (auto S = F->begin(), E = F->end(); S != E; ++S) {
BasicBlock *BB = &*S; BasicBlock *BB = &*S;
uint32_t MyID = LMap[BB]; 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) { for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) {
Succs[MyID].push_back(LMap[*I]); Succs[MyID].push_back(LMap[*I]);
@ -113,7 +108,7 @@ void DFStree(size_t now_id) {
} }
void turnCFGintoDAG(Function *F) { void turnCFGintoDAG() {
tSuccs = Succs; tSuccs = Succs;
tag.resize(Blocks.size()); tag.resize(Blocks.size());
@ -176,7 +171,7 @@ void DFS(uint32_t now) {
} }
void DominatorTree(Function *F) { void DominatorTree() {
if (Blocks.empty()) return; if (Blocks.empty()) return;
uint32_t s = start_point; uint32_t s = start_point;
@ -390,7 +385,7 @@ void MarkSubGraph(uint32_t ss, uint32_t tt) {
} }
void MarkVertice(Function *F) { void MarkVertice() {
uint32_t s = start_point; uint32_t s = start_point;
@ -411,8 +406,6 @@ void MarkVertice(Function *F) {
timeStamp = 0; timeStamp = 0;
uint32_t t = 0; uint32_t t = 0;
// MarkSubGraph(s, t);
// return;
while (s != t) { while (s != t) {
@ -432,9 +425,9 @@ std::pair<std::vector<BasicBlock *>, std::vector<BasicBlock *> > markNodes(
reset(); reset();
labelEachBlock(F); labelEachBlock(F);
buildCFG(F); buildCFG(F);
turnCFGintoDAG(F); turnCFGintoDAG();
DominatorTree::DominatorTree(F); DominatorTree::DominatorTree();
MarkVertice(F); MarkVertice();
std::vector<BasicBlock *> Result, ResultAbove; std::vector<BasicBlock *> Result, ResultAbove;
for (uint32_t x : Markabove) { for (uint32_t x : Markabove) {

View File

@ -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, faster than the normal fork() model, and compared to in-process fuzzing,
should be a lot more robust. 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 that provides AFL with the necessary tracing data without the need to
post-process the assembly or install any compiler plugins. See: post-process the assembly or install any compiler plugins. See:
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards 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 If you have not an outdated compiler and want to give it a try, build
afl-clang-fast this way: 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 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 instrumentation is not inlined, and instead involves a function call.
that support it, compiling your target with -flto should help. On systems that support it, compiling your target with -flto can help
a bit.

View File

@ -204,13 +204,24 @@ static void edit_params(u32 argc, char** argv) {
// "-fsanitize-coverage=trace-cmp,trace-div,trace-gep"; // "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
// cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; // cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
#else #else
cc_params[cc_par_cnt++] = "-Xclang"; if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
cc_params[cc_par_cnt++] = "-load"; getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) {
cc_params[cc_par_cnt++] = "-Xclang";
if (getenv("AFL_LLVM_INSTRIM") != NULL || getenv("INSTRIM_LIB") != NULL) cc_params[cc_par_cnt++] =
cc_params[cc_par_cnt++] = alloc_printf("%s/libLLVMInsTrim.so", obj_path); "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
else
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path); } 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 */ #endif /* ^USE_TRACE_PC */
cc_params[cc_par_cnt++] = "-Qunused-arguments"; cc_params[cc_par_cnt++] = "-Qunused-arguments";
@ -282,8 +293,10 @@ static void edit_params(u32 argc, char** argv) {
#ifdef USE_TRACE_PC #ifdef USE_TRACE_PC
if (getenv("AFL_INST_RATIO")) if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
FATAL("AFL_INST_RATIO not available at compile time with '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 */ #endif /* USE_TRACE_PC */
@ -444,7 +457,8 @@ int main(int argc, char** argv) {
"You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. " "You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. "
"Setting\n" "Setting\n"
"AFL_HARDEN enables hardening optimizations in the compiled code.\n\n" "AFL_HARDEN enables hardening optimizations in the compiled code.\n\n"
"afl-clang-fast was built for llvm %s with the llvm binary path of \"%s\".\n\n", "afl-clang-fast was built for llvm %s with the llvm binary path of "
"\"%s\".\n\n",
BIN_PATH, BIN_PATH, LLVM_VERSION, LLVM_BINDIR); BIN_PATH, BIN_PATH, LLVM_VERSION, LLVM_BINDIR);
exit(1); exit(1);
@ -454,6 +468,8 @@ int main(int argc, char** argv) {
#ifdef USE_TRACE_PC #ifdef USE_TRACE_PC
SAYF(cCYA "afl-clang-fast" VERSION cRST SAYF(cCYA "afl-clang-fast" VERSION cRST
" [tpcg] by <lszekeres@google.com>\n"); " [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 #else
SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n"); SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n");
#endif /* ^USE_TRACE_PC */ #endif /* ^USE_TRACE_PC */

View File

@ -75,6 +75,28 @@ class AFLCoverage : public ModulePass {
} }
// ripped from aflgo
static bool isBlacklisted(const Function *F) {
static const SmallVector<std::string, 4> Blacklist = {
"asan.",
"llvm.",
"sancov.",
"__ubsan_handle_",
};
for (auto const &BlacklistFunc : Blacklist) {
if (F->getName().startswith(BlacklistFunc)) { return true; }
}
return false;
}
bool runOnModule(Module &M) override; bool runOnModule(Module &M) override;
// StringRef getPassName() const override { // StringRef getPassName() const override {
@ -156,13 +178,11 @@ bool AFLCoverage::runOnModule(Module &M) {
/* Instrument all the things! */ /* Instrument all the things! */
const char *IntrinsicPrefix = "llvm.";
int inst_blocks = 0; int inst_blocks = 0;
for (auto &F : M) { for (auto &F : M) {
auto Fname = F.getName(); if (isBlacklisted(&F)) continue;
if (Fname.startswith(IntrinsicPrefix)) continue;
for (auto &BB : F) { for (auto &BB : F) {
@ -377,6 +397,7 @@ bool AFLCoverage::runOnModule(Module &M) {
inst_blocks++; inst_blocks++;
} }
} }
/* Say something nice. */ /* Say something nice. */

View File

@ -18,7 +18,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <list>
#include <string>
#include <fstream>
#include <sys/time.h>
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
@ -42,6 +48,23 @@ class CompareTransform : public ModulePass {
static char ID; static char ID;
CompareTransform() : ModulePass(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; bool runOnModule(Module &M) override;
@ -57,6 +80,9 @@ class CompareTransform : public ModulePass {
} }
protected:
std::list<std::string> myWhitelist;
private: private:
bool transformCmps(Module &M, const bool processStrcmp, bool transformCmps(Module &M, const bool processStrcmp,
const bool processMemcmp, const bool processStrncmp, const bool processMemcmp, const bool processStrncmp,
@ -104,6 +130,74 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
for (auto &BB : F) { 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 (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;
}
}
}
}
}
/* 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) { for (auto &IN : BB) {
CallInst *callInst = nullptr; CallInst *callInst = nullptr;

View File

@ -15,7 +15,17 @@
* limitations under the License. * 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/Pass.h" #include "llvm/Pass.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h"
@ -35,6 +45,41 @@ class SplitComparesTransform : public ModulePass {
static char ID; static char ID;
SplitComparesTransform() : ModulePass(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 SmallVector<std::string, 5> 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; bool runOnModule(Module &M) override;
@ -49,6 +94,9 @@ class SplitComparesTransform : public ModulePass {
} }
protected:
std::list<std::string> myWhitelist;
private: private:
int enableFPSplit; int enableFPSplit;
@ -77,8 +125,78 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
* all integer comparisons with >= and <= predicates to the icomps vector */ * all integer comparisons with >= and <= predicates to the icomps vector */
for (auto &F : M) { for (auto &F : M) {
if (isBlacklisted(&F)) continue;
for (auto &BB : F) { 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 (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;
}
}
}
}
}
/* 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) { for (auto &IN : BB) {
CmpInst *selectcmpInst = nullptr; CmpInst *selectcmpInst = nullptr;

View File

@ -18,7 +18,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <list>
#include <string>
#include <fstream>
#include <sys/time.h>
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
@ -42,6 +48,41 @@ class SplitSwitchesTransform : public ModulePass {
static char ID; static char ID;
SplitSwitchesTransform() : ModulePass(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 SmallVector<std::string, 5> 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; bool runOnModule(Module &M) override;
@ -71,6 +112,9 @@ class SplitSwitchesTransform : public ModulePass {
typedef std::vector<CaseExpr> CaseVector; typedef std::vector<CaseExpr> CaseVector;
protected:
std::list<std::string> myWhitelist;
private: private:
bool splitSwitches(Module &M); bool splitSwitches(Module &M);
bool transformCmps(Module &M, const bool processStrcmp, bool transformCmps(Module &M, const bool processStrcmp,
@ -268,10 +312,79 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
* all switches to switches vector for later processing */ * all switches to switches vector for later processing */
for (auto &F : M) { for (auto &F : M) {
if (isBlacklisted(&F)) continue;
for (auto &BB : F) { for (auto &BB : F) {
SwitchInst *switchInst = nullptr; 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 (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;
}
}
}
}
}
/* 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 = dyn_cast<SwitchInst>(BB.getTerminator()))) {
if (switchInst->getNumCases() < 1) continue; if (switchInst->getNumCases() < 1) continue;

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com> 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>
@ -524,7 +524,12 @@ u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {
struct queue_entry* q = queue; struct queue_entry* q = queue;
while (q) { while (q) {
if (q->exec_cksum == cksum) q->n_fuzz = q->n_fuzz + 1; if (q->exec_cksum == cksum) {
q->n_fuzz = q->n_fuzz + 1;
break;
}
q = q->next; q = q->next;

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>
@ -334,9 +334,9 @@ void show_stats(void) {
/* Lord, forgive me this. */ /* Lord, forgive me this. */
SAYF(SET_G1 bSTG bLT bH bSTOP cCYA SAYF(SET_G1 bSTG bLT bH bSTOP cCYA
" process timing " bSTG bH30 bH5 bH bHB bH bSTOP cCYA " process timing " bSTG bH30 bH5 bH bHB bH bSTOP cCYA
" overall results " bSTG bH2 bH2 bRT "\n"); " overall results " bSTG bH2 bH2 bRT "\n");
if (dumb_mode) { if (dumb_mode) {
@ -413,9 +413,9 @@ void show_stats(void) {
" uniq hangs : " cRST "%-6s" bSTG bV "\n", " uniq hangs : " cRST "%-6s" bSTG bV "\n",
DTD(cur_ms, last_hang_time), tmp); DTD(cur_ms, last_hang_time), tmp);
SAYF(bVR bH bSTOP cCYA SAYF(bVR bH bSTOP cCYA
" cycle progress " bSTG bH10 bH5 bH2 bH2 bHB bH bSTOP cCYA " cycle progress " bSTG bH10 bH5 bH2 bH2 bHB bH bSTOP cCYA
" map coverage " bSTG bH bHT bH20 bH2 bVL "\n"); " map coverage " bSTG bH bHT bH20 bH2 bVL "\n");
/* This gets funny because we want to print several variable-length variables /* This gets funny because we want to print several variable-length variables
together, but then cram them into a fixed-width field - so we need to together, but then cram them into a fixed-width field - so we need to
@ -443,9 +443,9 @@ void show_stats(void) {
SAYF(bSTOP " count coverage : " cRST "%-21s" bSTG bV "\n", tmp); SAYF(bSTOP " count coverage : " cRST "%-21s" bSTG bV "\n", tmp);
SAYF(bVR bH bSTOP cCYA SAYF(bVR bH bSTOP cCYA
" stage progress " bSTG bH10 bH5 bH2 bH2 bX bH bSTOP cCYA " stage progress " bSTG bH10 bH5 bH2 bH2 bX bH bSTOP cCYA
" findings in depth " bSTG bH10 bH5 bH2 bH2 bVL "\n"); " findings in depth " bSTG bH10 bH5 bH2 bH2 bVL "\n");
sprintf(tmp, "%s (%0.02f%%)", DI(queued_favored), sprintf(tmp, "%s (%0.02f%%)", DI(queued_favored),
((double)queued_favored) * 100 / queued_paths); ((double)queued_favored) * 100 / queued_paths);
@ -514,7 +514,7 @@ void show_stats(void) {
/* Aaaalmost there... hold on! */ /* Aaaalmost there... hold on! */
SAYF(bVR bH cCYA bSTOP SAYF(bVR bH cCYA bSTOP
" fuzzing strategy yields " bSTG bH10 bHT bH10 bH5 bHB bH bSTOP cCYA " fuzzing strategy yields " bSTG bH10 bHT bH10 bH5 bHB bH bSTOP cCYA
" path geometry " bSTG bH5 bH2 bVL "\n"); " path geometry " bSTG bH5 bH2 bVL "\n");

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -4,7 +4,7 @@
Originally written by Michal Zalewski 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com> 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com> 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -6,7 +6,7 @@
Forkserver design by Jann Horn <jannhorn@googlemail.com> 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 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>

View File

@ -2,17 +2,23 @@ CUR_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
all: libradamsa.so all: libradamsa.so
# These can be overriden:
CFLAGS ?= -march=native $(CFLAGS_FLTO)
# These are required: (otherwise radamsa gets very very slooooow)
CFLAGS += -O3 -funroll-loops
libradamsa.so: libradamsa.a libradamsa.so: libradamsa.a
$(CC) -O3 -shared libradamsa.a -o libradamsa.so $(CC) $(CFLAGS) -shared libradamsa.a -o libradamsa.so
libradamsa.a: libradamsa.c radamsa.h libradamsa.a: libradamsa.c radamsa.h
@echo " ***************************************************************" @echo " ***************************************************************"
@echo " * Compiling libradamsa, wait some minutes (~3 on modern CPUs) *" @echo " * Compiling libradamsa, wait some minutes (~3 on modern CPUs) *"
@echo " ***************************************************************" @echo " ***************************************************************"
$(CC) -fPIC -O3 -I $(CUR_DIR) -o libradamsa.a -c libradamsa.c $(CC) -fPIC $(CFLAGS) -I $(CUR_DIR) -o libradamsa.a -c libradamsa.c
test: libradamsa.a libradamsa-test.c test: libradamsa.a libradamsa-test.c
cc -O3 -I $(CUR_DIR) -o libradamsa-test libradamsa-test.c libradamsa.a $(CC) $(CFLAGS) -I $(CUR_DIR) -o libradamsa-test libradamsa-test.c libradamsa.a
./libradamsa-test libradamsa-test.c | grep "library test passed" ./libradamsa-test libradamsa-test.c | grep "library test passed"
rm /tmp/libradamsa-*.fuzz rm /tmp/libradamsa-*.fuzz

25
test/test-unsigaction.c Normal file
View File

@ -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;
}

View File

@ -150,15 +150,15 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" && {
} }
echo 000000000000000000000000 > in/in2 echo 000000000000000000000000 > in/in2
mkdir -p 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` CNT=`ls in2/ | wc -l`
case "$CNT" in case "$CNT" in
1| *1) $ECHO "$GREEN[+] afl-cmin correctly minimized testcase numbers" ;; *1) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;;
*) $ECHO "$RED[!] afl-cmin did not correctly minimize testcase numbers" *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases"
CODE=1 CODE=1
;; ;;
esac esac
../afl-tmin -m200 -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 ../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}'` 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 "$GREEN[+] afl-tmin correctly minimized the testcase"
test "$SIZE" = 1 || { test "$SIZE" = 1 || {
@ -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 "$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 && { test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
# on FreeBSD need to set AFL_CC # on FreeBSD need to set AFL_CC
if which clang >/dev/null; then test `uname -s` = 'FreeBSD' && {
export AFL_CC=`which clang` if which clang >/dev/null; then
else export AFL_CC=`which clang`
export AFL_CC=`$LLVM_CONFIG --bindir`/clang else
fi export AFL_CC=`$LLVM_CONFIG --bindir`/clang
fi
}
../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 ../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 AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1
test -e test-instr.plain && { 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" $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode"
CODE=1 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 -rf in out errors
} }
rm -f test-instr.plain 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 "$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 "$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 #CODE=1
} }
} }
@ -457,6 +479,15 @@ test -e ../libdislocator.so && {
} }
rm -f test-compcov rm -f test-compcov
test -e ../libradamsa.so && { 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-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-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 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 CODE=1
exit 1 exit 1
} }
$ECHO "$YELLOW[-] we need a test case for qemu_mode unsigaction library"
rm -rf in out errors 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??" $ECHO "$RED[!] gcc compilation of test targets failed - what is going on??"

View File

@ -1,11 +1,10 @@
/* /*
Simple test harness for AFL++'s unicornafl c mode. Simple test harness for AFL++'s unicornafl c mode.
This loads the simple_target.bin binary (precompiled as MIPS code) into This loads the simple_target_x86_64 binary into
Unicorn's memory map for emulation, places the specified input into Unicorn's memory map for emulation, places the specified input into
simple_target's buffer (hardcoded to be at 0x300000), and executes 'main()'. argv[1], sets up argv, and argc and executes 'main()'.
If any crashes occur during emulation, this script throws a matching signal If run inside AFL, afl_fuzz automatically does the "right thing"
to tell AFL that a crash occurred.
Run under AFL as follows: Run under AFL as follows: