mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-14 02:58:08 +00:00
Merge branch 'master' of github.com:vanhauser-thc/AFLplusplus
This commit is contained in:
13
Dockerfile
13
Dockerfile
@ -9,6 +9,9 @@ RUN apt-get update && apt-get install -y \
|
||||
clang \
|
||||
clang-9 \
|
||||
flex \
|
||||
git \
|
||||
python3.7 \
|
||||
python3.7-dev \
|
||||
gcc-9 \
|
||||
gcc-9-plugin-dev \
|
||||
gcc-9-multilib \
|
||||
@ -23,10 +26,12 @@ RUN apt-get update && apt-get install -y \
|
||||
ca-certificates \
|
||||
libpixman-1-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG CC=gcc-9
|
||||
ARG CXX=g++-9
|
||||
ARG LLVM_CONFIG=llvm-config-9
|
||||
COPY . /app
|
||||
RUN cd /app && make clean && make distrib && \
|
||||
make install && cd .. && rm -rf /app
|
||||
WORKDIR /work
|
||||
|
||||
RUN git clone https://github.com/vanhauser-thc/AFLplusplus
|
||||
|
||||
RUN cd AFLplusplus && make clean && make distrib && \
|
||||
make install && cd .. && rm -rf AFLplusplus
|
||||
|
58
Makefile
58
Makefile
@ -29,27 +29,51 @@ VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
||||
|
||||
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
||||
SH_PROGS = afl-plot afl-cmin afl-whatsup afl-system-config
|
||||
SH_PROGS = afl-plot afl-cmin afl-cmin.bash afl-whatsup afl-system-config
|
||||
MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8)
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops
|
||||
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ \
|
||||
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_FLTO ?= -flto=full
|
||||
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
|
||||
|
||||
AFL_FUZZ_FILES = $(wildcard src/afl-fuzz*.c)
|
||||
|
||||
ifneq "($filter %3.7m, $(shell python3.7m-config --includes 2>/dev/null)" ""
|
||||
ifneq "$(filter %3.7m, $(shell python3.7m-config --includes 2>/dev/null))" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3.7m-config --includes)
|
||||
PYTHON_LIB ?= $(shell python3.7m-config --ldflags)
|
||||
PYTHON_VERSION = 3.7m
|
||||
else
|
||||
ifneq "($filter %3.7, $(shell python3.7-config --includes) 2> /dev/null" ""
|
||||
ifneq "$(filter %3.7, $(shell python3.7-config --includes 2>/dev/null))" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3.7-config --includes)
|
||||
PYTHON_LIB ?= $(shell python3.7-config --ldflags)
|
||||
PYTHON_VERSION = 3.7
|
||||
else
|
||||
ifneq "($filter %2.7, $(shell python2.7-config --includes) 2> /dev/null" ""
|
||||
ifneq "$(filter %2.7, $(shell python2.7-config --includes 2>/dev/null))" ""
|
||||
PYTHON_INCLUDE ?= $(shell python2.7-config --includes)
|
||||
PYTHON_LIB ?= $(shell python2.7-config --ldflags)
|
||||
PYTHON_VERSION = 2.7
|
||||
@ -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/python2.7 && echo /usr/include/python2.7)
|
||||
|
||||
ifneq "($filter %3.7m, $(PYTHON_INCLUDE))" ""
|
||||
ifneq "$(filter %3.7m, $(PYTHON_INCLUDE))" ""
|
||||
PYTHON_VERSION ?= 3.7m
|
||||
PYTHON_LIB ?= -lpython3.7m
|
||||
else
|
||||
ifneq "($filter %3.7, $(PYTHON_INCLUDE))" ""
|
||||
ifneq "$(filter %3.7, $(PYTHON_INCLUDE))" ""
|
||||
PYTHON_VERSION ?= 3.7
|
||||
else
|
||||
ifneq "($filter %2.7, $(PYTHON_INCLUDE))" ""
|
||||
ifneq "$(filter %2.7, $(PYTHON_INCLUDE))" ""
|
||||
PYTHON_VERSION ?= 2.7
|
||||
PYTHON_LIB ?= -lpython2.7
|
||||
else
|
||||
@ -229,31 +253,31 @@ afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
|
||||
ln -sf afl-as as
|
||||
|
||||
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
|
||||
$(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
|
||||
$(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
|
||||
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
|
||||
$(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
|
||||
$(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
|
||||
$(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
|
||||
$(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
|
||||
$(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
|
||||
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
|
6
TODO
6
TODO
@ -2,6 +2,12 @@
|
||||
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:
|
||||
- laf-intel
|
||||
- better instrumentation
|
||||
|
880
afl-cmin
880
afl-cmin
@ -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.
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
# external tools used by this script:
|
||||
# test
|
||||
# grep
|
||||
# rm
|
||||
# mkdir
|
||||
# ln
|
||||
# cp
|
||||
# pwd
|
||||
# which
|
||||
# cd
|
||||
# find
|
||||
# stat
|
||||
# sort
|
||||
# cut
|
||||
# and afl-showmap from this project :-)
|
||||
|
||||
# getopt.awk --- Do C library getopt(3) function in awk
|
||||
|
||||
# External variables:
|
||||
# Optind -- index in ARGV of first nonoption argument
|
||||
# Optarg -- string value of argument to current option
|
||||
# Opterr -- if nonzero, print our own diagnostic
|
||||
# Optopt -- current option letter
|
||||
|
||||
# Returns:
|
||||
# -1 at end of options
|
||||
# "?" for unrecognized option
|
||||
# <c> a character representing the current option
|
||||
|
||||
# Private Data:
|
||||
# _opti -- index in multiflag option, e.g., -abc
|
||||
|
||||
function getopt(argc, argv, options, thisopt, i)
|
||||
{
|
||||
if (length(options) == 0) # no options given
|
||||
return -1
|
||||
|
||||
if (argv[Optind] == "--") { # all done
|
||||
Optind++
|
||||
_opti = 0
|
||||
return -1
|
||||
} else if (argv[Optind] !~ /^-[^:\t ]/) {
|
||||
_opti = 0
|
||||
return -1
|
||||
}
|
||||
if (_opti == 0)
|
||||
_opti = 2
|
||||
thisopt = substr(argv[Optind], _opti, 1)
|
||||
Optopt = thisopt
|
||||
i = index(options, thisopt)
|
||||
if (i == 0) {
|
||||
if (Opterr)
|
||||
printf("%c -- invalid option\n", thisopt) > "/dev/stderr"
|
||||
if (_opti >= length(argv[Optind])) {
|
||||
Optind++
|
||||
_opti = 0
|
||||
} else
|
||||
_opti++
|
||||
return "?"
|
||||
}
|
||||
if (substr(options, i + 1, 1) == ":") {
|
||||
# get option argument
|
||||
if (length(substr(argv[Optind], _opti + 1)) > 0)
|
||||
Optarg = substr(argv[Optind], _opti + 1)
|
||||
else
|
||||
Optarg = argv[++Optind]
|
||||
_opti = 0
|
||||
} else
|
||||
Optarg = ""
|
||||
if (_opti == 0 || _opti >= length(argv[Optind])) {
|
||||
Optind++
|
||||
_opti = 0
|
||||
} else
|
||||
_opti++
|
||||
return thisopt
|
||||
}
|
||||
|
||||
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"
|
||||
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
|
||||
fi
|
||||
}
|
||||
|
||||
# The sed command converted the sorted list to a shell script that populates
|
||||
# BEST_FILE[tuple]="fname". Let's load that!
|
||||
function exists_and_is_executable(binarypath) {
|
||||
return 0 == system("test -f "binarypath" -a -x "binarypath)
|
||||
}
|
||||
|
||||
. "$TRACE_DIR/.candidate_script"
|
||||
BEGIN {
|
||||
print "corpus minimization tool for afl++ (awk version)\n"
|
||||
|
||||
##########################
|
||||
# STEP 5: WRITING OUTPUT #
|
||||
##########################
|
||||
# defaults
|
||||
extra_par = ""
|
||||
# process options
|
||||
Opterr = 1 # default is to diagnose
|
||||
Optind = 1 # skip ARGV[0]
|
||||
while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eCQU?")) != -1) {
|
||||
if (_go_c == "i") {
|
||||
if (!Optarg) usage()
|
||||
if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
in_dir = Optarg
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "o") {
|
||||
if (!Optarg) usage()
|
||||
if (out_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
out_dir = Optarg
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "f") {
|
||||
if (!Optarg) usage()
|
||||
if (stdin_file) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
stdin_file = Optarg
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "m") {
|
||||
if (!Optarg) usage()
|
||||
if (mem_limit) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
mem_limit = Optarg
|
||||
mem_limit_given = 1
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "t") {
|
||||
if (!Optarg) usage()
|
||||
if (timeout) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
timeout = Optarg
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "C") {
|
||||
ENVIRON["AFL_CMIN_CRASHES_ONLY"] = 1
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "e") {
|
||||
extra_par = extra_par " -e"
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "Q") {
|
||||
if (qemu_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
extra_par = extra_par " -Q"
|
||||
if ( !mem_limit_given ) mem_limit = "250"
|
||||
qemu_mode = 1
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "U") {
|
||||
if (unicorn_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
extra_par = extra_par " -U"
|
||||
if ( !mem_limit_given ) mem_limit = "250"
|
||||
unicorn_mode = 1
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "?") {
|
||||
exit 1
|
||||
} else
|
||||
usage()
|
||||
} # while options
|
||||
|
||||
# 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.
|
||||
if (!mem_limit) mem_limit = 200
|
||||
if (!timeout) timeout = "none"
|
||||
|
||||
echo "[*] Processing candidates and writing output files..."
|
||||
# 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]
|
||||
}
|
||||
|
||||
CUR=0
|
||||
# sanity checks
|
||||
if (!prog_args[0] || !in_dir || !out_dir) usage()
|
||||
|
||||
touch "$TRACE_DIR/.already_have"
|
||||
target_bin = prog_args[0]
|
||||
|
||||
while read -r cnt tuple; do
|
||||
# Do a sanity check to discourage the use of /tmp, since we can't really
|
||||
# handle this safely from an awk script.
|
||||
|
||||
CUR=$((CUR+1))
|
||||
printf "\\r Processing tuple $CUR/$TUPLE_COUNT... "
|
||||
if (!ENVIRON["AFL_ALLOW_TMP"]) {
|
||||
dirlist[0] = in_dir
|
||||
dirlist[1] = target_bin
|
||||
dirlist[2] = out_dir
|
||||
dirlist[3] = stdin_file
|
||||
"pwd" | getline dirlist[4] # current directory
|
||||
for (dirind in dirlist) {
|
||||
dir = dirlist[dirind]
|
||||
|
||||
# If we already have this tuple, skip it.
|
||||
if (dir ~ /^(\/var)?\/tmp/) {
|
||||
print "[-] Error: do not use this script in /tmp or /var/tmp." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
delete dirlist
|
||||
}
|
||||
|
||||
grep -q "^$tuple\$" "$TRACE_DIR/.already_have" && continue
|
||||
# If @@ is specified, but there's no -f, let's come up with a temporary input
|
||||
# file name.
|
||||
|
||||
FN=${BEST_FILE[tuple]}
|
||||
trace_dir = out_dir "/.traces"
|
||||
|
||||
$CP_TOOL "$IN_DIR/$FN" "$OUT_DIR/$FN"
|
||||
if (!stdin_file) {
|
||||
found_atat = 0
|
||||
for (prog_args_ind in prog_args) {
|
||||
if ("@@" == prog_args[prog_args_ind]) {
|
||||
found_atat = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
if (found_atat) {
|
||||
stdin_file = trace_dir "/.cur_input"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
# Check for obvious errors.
|
||||
|
||||
done <"$TRACE_DIR/.all_uniq"
|
||||
if (mem_limit && mem_limit != "none" && mem_limit < 5) {
|
||||
print "[-] Error: dangerously low memory limit." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo
|
||||
if (timeout && timeout != "none" && timeout < 10) {
|
||||
print "[-] Error: dangerously low timeout." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
OUT_COUNT=`ls -- "$OUT_DIR" | wc -l`
|
||||
if (target_bin && !exists_and_is_executable(target_bin)) {
|
||||
|
||||
if [ "$OUT_COUNT" = "1" ]; then
|
||||
echo "[!] WARNING: All test cases had the same traces, check syntax!"
|
||||
fi
|
||||
"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
|
||||
}
|
||||
|
||||
echo "[+] Narrowed down to $OUT_COUNT files, saved in '$OUT_DIR'."
|
||||
echo
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR"
|
||||
if (0 != system( "test -d "in_dir )) {
|
||||
print "[-] Error: directory '"in_dir"' not found." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit 0
|
||||
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
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
first_file = infilesSmallToBig[0]
|
||||
|
||||
# Make sure that we're not dealing with a directory.
|
||||
|
||||
if (0 == system("test -d "in_dir"/"first_file)) {
|
||||
print "[-] Error: The input directory contains subdirectories - please fix." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (0 == system("ln "in_dir"/"first_file" "trace_dir"/.link_test")) {
|
||||
cp_tool = "ln"
|
||||
} else {
|
||||
cp_tool = "cp"
|
||||
}
|
||||
|
||||
# Make sure that we can actually get anything out of afl-showmap before we
|
||||
# waste too much time.
|
||||
|
||||
print "[*] Testing the target binary..."
|
||||
|
||||
if (!stdin_file) {
|
||||
system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -- \""target_bin"\" "prog_args_string" <\""in_dir"/"first_file"\"")
|
||||
} else {
|
||||
system("cp "in_dir"/"first_file" "stdin_file)
|
||||
system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
}
|
||||
|
||||
first_count = 0
|
||||
|
||||
runtest = trace_dir"/.run_test"
|
||||
while ((getline < runtest) > 0) {
|
||||
++first_count
|
||||
}
|
||||
|
||||
if (first_count) {
|
||||
print "[+] OK, "first_count" tuples recorded."
|
||||
} else {
|
||||
print "[-] Error: no instrumentation output detected (perhaps crash or timeout)." > "/dev/stderr"
|
||||
if (!ENVIRON["AFL_KEEP_TRACES"]) {
|
||||
system("rm -rf "trace_dir" 2>/dev/null")
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Let's roll!
|
||||
|
||||
#############################
|
||||
# STEP 1: Collecting traces #
|
||||
#############################
|
||||
|
||||
print "[*] Obtaining traces for "in_count" input files in '"in_dir"'."
|
||||
|
||||
cur = 0;
|
||||
if (!stdin_file) {
|
||||
while (cur < in_count) {
|
||||
fn = infilesSmallToBig[cur]
|
||||
++cur;
|
||||
printf "\r Processing file "cur"/"in_count
|
||||
system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/"fn"\" -Z "extra_par" -- \""target_bin"\" "prog_args_string" <\""in_dir"/"fn"\"")
|
||||
}
|
||||
} else {
|
||||
while (cur < in_count) {
|
||||
fn = infilesSmallToBig[cur]
|
||||
++cur
|
||||
printf "\r Processing file "cur"/"in_count
|
||||
system("cp "in_dir"/"fn" "stdin_file)
|
||||
system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/"fn"\" -Z "extra_par" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
}
|
||||
}
|
||||
|
||||
print ""
|
||||
|
||||
|
||||
#######################################################
|
||||
# STEP 2: register smallest input file for each tuple #
|
||||
# STEP 3: copy that file (at most once) #
|
||||
#######################################################
|
||||
|
||||
print "[*] Processing traces for input files in '"in_dir"'."
|
||||
|
||||
cur = 0
|
||||
out_count = 0
|
||||
tuple_count = 0
|
||||
|
||||
while (cur < in_count) {
|
||||
fn = infilesSmallToBig[cur]
|
||||
++cur
|
||||
printf "\r Processing file "cur"/"in_count
|
||||
# create path for the trace file from afl-showmap
|
||||
tracefile_path = trace_dir"/"fn
|
||||
# gather all keys, and count them
|
||||
while ((getline line < tracefile_path) > 0) {
|
||||
key = line
|
||||
if (!(key in key_count)) {
|
||||
++tuple_count
|
||||
}
|
||||
++key_count[key]
|
||||
if (! (key in best_file)) {
|
||||
# this is the best file for this key
|
||||
best_file[key] = fn
|
||||
# copy file unless already done
|
||||
if (! (fn in file_already_copied)) {
|
||||
system(cp_tool" "in_dir"/"fn" "out_dir"/"fn)
|
||||
file_already_copied[fn] = ""
|
||||
++out_count
|
||||
}
|
||||
}
|
||||
}
|
||||
close(tracefile_path)
|
||||
}
|
||||
|
||||
print ""
|
||||
print "[+] Found "tuple_count" unique tuples across "in_count" files."
|
||||
|
||||
if (out_count == 1) {
|
||||
print "[!] WARNING: All test cases had the same traces, check syntax!"
|
||||
}
|
||||
print "[+] Narrowed down to "out_count" files, saved in '"out_dir"'."
|
||||
|
||||
if (!ENVIRON["AFL_KEEP_TRACES"]) {
|
||||
system("rm -rf "trace_dir" 2>/dev/null")
|
||||
}
|
||||
|
||||
exit 0
|
||||
}
|
||||
EOF
|
||||
|
470
afl-cmin.bash
Executable file
470
afl-cmin.bash
Executable 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
|
@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
test "$1" = "-h" && {
|
||||
echo afl-system-config by Marc Heuse
|
||||
echo 'afl-system-config by Marc Heuse <mh@mh-sec.de>'
|
||||
echo
|
||||
echo $0
|
||||
echo
|
||||
@ -12,55 +12,72 @@ test "$1" = "-h" && {
|
||||
exit 1
|
||||
}
|
||||
|
||||
DONE=
|
||||
PLATFORM=`uname -s`
|
||||
echo This reconfigures the system to have a better fuzzing performance
|
||||
echo This reconfigures the system to have a better fuzzing performance.
|
||||
if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
|
||||
echo Error you need to be root to run this
|
||||
exit 1
|
||||
echo "Warning: you need to be root to run this!"
|
||||
# we do not exit as other mechanisms exist that allows to do this than
|
||||
# being root. let the errors speak for themselves.
|
||||
fi
|
||||
if [ "$PLATFORM" = "Linux" ] ; then
|
||||
sysctl -w kernel.core_pattern=core
|
||||
sysctl -w kernel.randomize_va_space=0
|
||||
sysctl -w kernel.sched_child_runs_first=1
|
||||
sysctl -w kernel.sched_autogroup_enabled=1
|
||||
sysctl -w kernel.sched_migration_cost_ns=50000000
|
||||
sysctl -w kernel.sched_latency_ns=250000000
|
||||
echo never > /sys/kernel/mm/transparent_hugepage/enabled
|
||||
test -e /sys/devices/system/cpu/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/scaling_governor
|
||||
test -e /sys/devices/system/cpu/cpufreq/policy0/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/policy*/scaling_governor
|
||||
test -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||
test -e /sys/devices/system/cpu/intel_pstate/no_turbo && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
|
||||
test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost
|
||||
echo
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo '/etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"'
|
||||
{
|
||||
sysctl -w kernel.core_pattern=core
|
||||
sysctl -w kernel.randomize_va_space=0
|
||||
sysctl -w kernel.sched_child_runs_first=1
|
||||
sysctl -w kernel.sched_autogroup_enabled=1
|
||||
sysctl -w kernel.sched_migration_cost_ns=50000000
|
||||
sysctl -w kernel.sched_latency_ns=250000000
|
||||
echo never > /sys/kernel/mm/transparent_hugepage/enabled
|
||||
test -e /sys/devices/system/cpu/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/scaling_governor
|
||||
test -e /sys/devices/system/cpu/cpufreq/policy0/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/policy*/scaling_governor
|
||||
test -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||
test -e /sys/devices/system/cpu/intel_pstate/no_turbo && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
|
||||
test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost
|
||||
} > /dev/null
|
||||
echo Settings applied.
|
||||
dmesg | egrep -q 'nospectre_v2|spectre_v2=off' || {
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo ' /etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"'
|
||||
}
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "FreeBSD" ] ; then
|
||||
sysctl kern.elf32.aslr.enable=0
|
||||
sysctl kern.elf64.aslr.enable=0
|
||||
echo
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo 'sysctl hw.ibrs_disable=1'
|
||||
echo
|
||||
echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
|
||||
{
|
||||
sysctl kern.elf32.aslr.enable=0
|
||||
sysctl kern.elf64.aslr.enable=0
|
||||
} > /dev/null
|
||||
echo Settings applied.
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo ' sysctl hw.ibrs_disable=1'
|
||||
echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "OpenBSD" ] ; then
|
||||
echo
|
||||
echo 'System security features cannot be disabled on OpenBSD.'
|
||||
echo
|
||||
echo 'System security features cannot be disabled on OpenBSD.'
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "NetBSD" ] ; then
|
||||
echo
|
||||
echo It is recommended to enable unprivileged users to set cpu affinity
|
||||
echo to be able to use afl-gotcpu meaningfully.
|
||||
/sbin/sysctl -w security.models.extensions.user_set_cpu_affinity=1
|
||||
{
|
||||
#echo It is recommended to enable unprivileged users to set cpu affinity
|
||||
#echo to be able to use afl-gotcpu meaningfully.
|
||||
/sbin/sysctl -w security.models.extensions.user_set_cpu_affinity=1
|
||||
} > /dev/null
|
||||
echo Settings applied.
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "Darwin" ] ; then
|
||||
if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then
|
||||
echo We unload the default crash reporter here
|
||||
SL=/System/Library; PL=com.apple.ReportCrash
|
||||
launchctl unload -w ${SL}/LaunchAgents/${PL}.plist
|
||||
sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist
|
||||
echo We unload the default crash reporter here
|
||||
SL=/System/Library; PL=com.apple.ReportCrash
|
||||
launchctl unload -w ${SL}/LaunchAgents/${PL}.plist
|
||||
sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist
|
||||
echo Settings applied.
|
||||
else
|
||||
echo Nothing to do.
|
||||
fi
|
||||
DONE=1
|
||||
fi
|
||||
echo
|
||||
echo Also use AFL_TMPDIR to use a tmpfs for the input file
|
||||
test -z "$DONE" && echo Error: Unknown platform: $PLATFORM
|
||||
test -z "$AFL_TMPDIR" && echo Also use AFL_TMPDIR and point it to a tmpfs for the input file caching
|
||||
|
@ -17,10 +17,17 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
Version ++2.60d (develop):
|
||||
--------------------------
|
||||
|
||||
- use -march=native if available
|
||||
- afl-fuzz:
|
||||
- now prints the real python version support compiled in
|
||||
- afl-clang-fast now shows in the help output for which llvm version it
|
||||
was compiled for.
|
||||
- set stronger performance compile options and little tweaks
|
||||
- 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
|
||||
|
||||
|
||||
|
@ -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
|
||||
it allows for very fast and coverage guided fuzzing.
|
||||
|
||||
However, if there is only the binary program and not source code available,
|
||||
then standard afl++ (dumb mode) is not effective.
|
||||
However, if there is only the binary program and no source code available,
|
||||
then standard `afl-fuzz -n` (dumb mode) is not effective.
|
||||
|
||||
The following is a description of how these 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
|
||||
@ -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.
|
||||
It is available in the ./qemu_mode/ directory and once compiled it can
|
||||
be accessed by the afl-fuzz -Q command line option.
|
||||
The speed decrease is at about 50%
|
||||
It is the easiest to use alternative and even works for cross-platform binaries.
|
||||
|
||||
Note that there is also honggfuzz: [https://github.com/google/honggfuzz](https://github.com/google/honggfuzz)
|
||||
which now has a qemu_mode, but its performance is just 1.5%!
|
||||
|
||||
As it is included in afl++ this needs no URL.
|
||||
|
||||
WINE+QEMU
|
||||
---------
|
||||
Wine mode can run Win32 PE with the QEMU instrumentation.
|
||||
|
||||
##WINE+QEMU
|
||||
Wine mode can run Win32 PE binaries with the QEMU instrumentation.
|
||||
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.
|
||||
In contrast to QEMU, Unicorn does not offer a full system or even userland emulation.
|
||||
Runtime environment and/or loaders have to be written from scratch, if needed.
|
||||
On top, block chaining has been removed. This means the speed boost introduced in
|
||||
to the patched QEMU Mode of afl++ cannot simply be ported over to Unicorn.
|
||||
For further information, check out ./unicorn_mode.txt.
|
||||
In contrast to QEMU, Unicorn does not offer a full system or even userland
|
||||
emulation. Runtime environment and/or loaders have to be written from scratch,
|
||||
if needed. On top, block chaining has been removed. This means the speed boost
|
||||
introduced in the patched QEMU Mode of afl++ cannot simply be ported over to
|
||||
Unicorn. For further information, check out ./unicorn_mode.txt.
|
||||
|
||||
As it is included in afl++ this needs no URL.
|
||||
|
||||
|
||||
DYNINST
|
||||
-------
|
||||
##DYNINST
|
||||
Dyninst is a binary instrumentation framework similar to Pintool and Dynamorio
|
||||
(see far below). However whereas Pintool and Dynamorio work at runtime, dyninst
|
||||
instruments the target at load time, and then let it run.
|
||||
instruments the target at load time, and then let it run - or save the
|
||||
binary with the changes.
|
||||
This is great for some things, e.g. fuzzing, and not so effective for others,
|
||||
e.g. malware analysis.
|
||||
|
||||
@ -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.
|
||||
Afterwards we can just fuzz the newly saved target binary with afl-fuzz.
|
||||
Sounds great? It is. The issue though - it is a non-trivial problem to
|
||||
insert instructions, which change addresses in the process space, so
|
||||
insert instructions, which change addresses in the process space, so that
|
||||
everything is still working afterwards. Hence more often than not binaries
|
||||
crash when they are run (because of instrumentation).
|
||||
crash when they are run.
|
||||
|
||||
The speed decrease is about 15-35%, depending on the optimization options
|
||||
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
|
||||
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.
|
||||
The big issue with Intel's PT is the small buffer size and the complex
|
||||
encoding of the debug information collected through PT.
|
||||
@ -77,30 +80,39 @@ the implementation and other factors).
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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.
|
||||
There is no implementation so far which handle coresight and getting
|
||||
it working on an ARM Linux is very difficult due to custom kernel building
|
||||
on embedded systems is difficult. And finding one that has coresight in
|
||||
the ARM chip is difficult too.
|
||||
My guess is that it is slower than Qemu, but faster than Intel PT.
|
||||
|
||||
If anyone finds any coresight implementation for afl please ping me:
|
||||
vh@thc.org
|
||||
|
||||
|
||||
PIN & DYNAMORIO
|
||||
---------------
|
||||
##FRIDA
|
||||
Frida is a dynamic instrumentation engine like Pintool, Dyninst and Dynamorio.
|
||||
What is special is that it is written Python, and scripted with Javascript.
|
||||
It is mostly used to reverse binaries on mobile phones however can be used
|
||||
everywhere.
|
||||
|
||||
There is a WIP fuzzer available at [https://github.com/andreafioraldi/frida-fuzzer](https://github.com/andreafioraldi/frida-fuzzer)
|
||||
|
||||
|
||||
##PIN & DYNAMORIO
|
||||
Pintool and Dynamorio are dynamic instrumentation engines, and they can be
|
||||
used for getting basic block information at runtime.
|
||||
Pintool is only available for Intel x32/x64 on Linux, Mac OS and Windows
|
||||
@ -115,30 +127,27 @@ Hence Dynamorio is the option to go for if everything fails, and Pintool
|
||||
only if Dynamorio fails too.
|
||||
|
||||
Dynamorio solutions:
|
||||
https://github.com/vanhauser-thc/afl-dynamorio
|
||||
https://github.com/mxmssh/drAFL
|
||||
https://github.com/googleprojectzero/winafl/ <= very good but windows only
|
||||
* [https://github.com/vanhauser-thc/afl-dynamorio](https://github.com/vanhauser-thc/afl-dynamorio)
|
||||
* [https://github.com/mxmssh/drAFL](https://github.com/mxmssh/drAFL)
|
||||
* [https://github.com/googleprojectzero/winafl/](https://github.com/googleprojectzero/winafl/) <= very good but windows only
|
||||
|
||||
Pintool solutions:
|
||||
https://github.com/vanhauser-thc/afl-pin
|
||||
https://github.com/mothran/aflpin
|
||||
https://github.com/spinpx/afl_pin_mode <= only old Pintool version supported
|
||||
* [https://github.com/vanhauser-thc/afl-pin](https://github.com/vanhauser-thc/afl-pin)
|
||||
* [https://github.com/mothran/aflpin](https://github.com/mothran/aflpin)
|
||||
* [https://github.com/spinpx/afl_pin_mode](https://github.com/spinpx/afl_pin_mode) <= only old Pintool version supported
|
||||
|
||||
|
||||
Non-AFL solutions
|
||||
-----------------
|
||||
##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
|
||||
work with large binaries, others are very slow but have good path discovery,
|
||||
some are very hard to set-up ...
|
||||
|
||||
QSYM: https://github.com/sslab-gatech/qsym
|
||||
Manticore: https://github.com/trailofbits/manticore
|
||||
S2E: https://github.com/S2E
|
||||
<please send me any missing that are good>
|
||||
* QSYM: [https://github.com/sslab-gatech/qsym](https://github.com/sslab-gatech/qsym)
|
||||
* Manticore: [https://github.com/trailofbits/manticore](https://github.com/trailofbits/manticore)
|
||||
* S2E: [https://github.com/S2E](https://github.com/S2E)
|
||||
* <please send me any missing that are good>
|
||||
|
||||
|
||||
## Closing words
|
||||
|
||||
That's it!
|
||||
News, corrections, updates?
|
||||
Email vh@thc.org
|
||||
That's it! News, corrections, updates? Send an email to vh@thc.org
|
@ -29,6 +29,9 @@ Here's a quick overview of the stuff you can find in this directory:
|
||||
|
||||
- post_library - an example of how to build postprocessors for AFL.
|
||||
|
||||
- socket_fuzzing - a LD_PRELOAD library 'redirects' a socket to stdin
|
||||
for fuzzing access with afl++
|
||||
|
||||
Note that the minimize_corpus.sh tool has graduated from the experimental/
|
||||
directory and is now available as ../afl-cmin. The LLVM mode has likewise
|
||||
graduated to ../llvm_mode/*.
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
@ -61,12 +61,16 @@
|
||||
|
||||
/* Default memory limit for child process (MB): */
|
||||
|
||||
#ifndef __NetBSD__
|
||||
#ifndef WORD_SIZE_64
|
||||
#define MEM_LIMIT 25
|
||||
#else
|
||||
#define MEM_LIMIT 50
|
||||
#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): */
|
||||
|
||||
#define MEM_LIMIT_QEMU 200
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -397,6 +397,29 @@ void* aligned_alloc(size_t align, size_t len) {
|
||||
|
||||
}
|
||||
|
||||
/* specific BSD api mainly checking possible overflow for the size */
|
||||
|
||||
void* reallocarray(void* ptr, size_t elem_len, size_t elem_cnt) {
|
||||
|
||||
const size_t elem_lim = 1UL << (sizeof(size_t) * 4);
|
||||
const size_t elem_tot = elem_len * elem_cnt;
|
||||
void* ret = NULL;
|
||||
|
||||
if ((elem_len >= elem_lim || elem_cnt >= elem_lim) && elem_len > 0 &&
|
||||
elem_cnt > (SIZE_MAX / elem_len)) {
|
||||
|
||||
DEBUGF("reallocarray size overflow (%zu)", elem_tot);
|
||||
|
||||
} else {
|
||||
|
||||
ret = realloc(ptr, elem_tot);
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void __dislocator_init(void) {
|
||||
|
||||
u8* tmp = (u8*)getenv("AFL_LD_LIMIT_MB");
|
||||
|
@ -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 {
|
||||
|
||||
char be_quiet = 0;
|
||||
@ -122,19 +144,6 @@ struct InsTrim : public ModulePass {
|
||||
// this is our default
|
||||
MarkSetOpt = true;
|
||||
|
||||
/* // I dont think this makes sense to port into LLVMInsTrim
|
||||
char* inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||
unsigned int inst_ratio = 100;
|
||||
if (inst_ratio_str) {
|
||||
|
||||
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio ||
|
||||
inst_ratio > 100) FATAL("Bad value of AFL_INST_RATIO (must be between 1
|
||||
and 100)");
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
LLVMContext &C = M.getContext();
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
@ -181,8 +190,7 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
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();
|
||||
if (oDILoc) {
|
||||
|
||||
@ -240,6 +248,8 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
if (isBlacklisted(&F)) continue;
|
||||
|
||||
std::unordered_set<BasicBlock *> MS;
|
||||
if (!MarkSetOpt) {
|
||||
|
||||
@ -408,28 +418,19 @@ struct InsTrim : public ModulePass {
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
/* Set prev_loc to cur_loc >> 1 */
|
||||
/*
|
||||
StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, L >> 1),
|
||||
OldPrev); Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C,
|
||||
None));
|
||||
*/
|
||||
|
||||
total_instr++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n" /*", ratio
|
||||
%u%%)."*/
|
||||
,
|
||||
OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n",
|
||||
total_instr, total_rs, total_hs,
|
||||
getenv("AFL_HARDEN")
|
||||
? "hardened"
|
||||
: ((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN"))
|
||||
? "ASAN/MSAN"
|
||||
: "non-hardened") /*, inst_ratio*/);
|
||||
: "non-hardened"));
|
||||
return false;
|
||||
|
||||
}
|
||||
|
@ -65,16 +65,11 @@ void buildCFG(Function *F) {
|
||||
|
||||
}
|
||||
|
||||
// uint32_t FakeID = 0;
|
||||
for (auto S = F->begin(), E = F->end(); S != E; ++S) {
|
||||
|
||||
BasicBlock *BB = &*S;
|
||||
uint32_t MyID = LMap[BB];
|
||||
// if (succ_begin(BB) == succ_end(BB)) {
|
||||
|
||||
// Succs[MyID].push_back(FakeID);
|
||||
// Marked.insert(MyID);
|
||||
//}
|
||||
for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) {
|
||||
|
||||
Succs[MyID].push_back(LMap[*I]);
|
||||
@ -113,7 +108,7 @@ void DFStree(size_t now_id) {
|
||||
|
||||
}
|
||||
|
||||
void turnCFGintoDAG(Function *F) {
|
||||
void turnCFGintoDAG() {
|
||||
|
||||
tSuccs = Succs;
|
||||
tag.resize(Blocks.size());
|
||||
@ -176,7 +171,7 @@ void DFS(uint32_t now) {
|
||||
|
||||
}
|
||||
|
||||
void DominatorTree(Function *F) {
|
||||
void DominatorTree() {
|
||||
|
||||
if (Blocks.empty()) return;
|
||||
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;
|
||||
|
||||
@ -411,8 +406,6 @@ void MarkVertice(Function *F) {
|
||||
|
||||
timeStamp = 0;
|
||||
uint32_t t = 0;
|
||||
// MarkSubGraph(s, t);
|
||||
// return;
|
||||
|
||||
while (s != t) {
|
||||
|
||||
@ -432,9 +425,9 @@ std::pair<std::vector<BasicBlock *>, std::vector<BasicBlock *> > markNodes(
|
||||
reset();
|
||||
labelEachBlock(F);
|
||||
buildCFG(F);
|
||||
turnCFGintoDAG(F);
|
||||
DominatorTree::DominatorTree(F);
|
||||
MarkVertice(F);
|
||||
turnCFGintoDAG();
|
||||
DominatorTree::DominatorTree();
|
||||
MarkVertice();
|
||||
|
||||
std::vector<BasicBlock *> Result, ResultAbove;
|
||||
for (uint32_t x : Markabove) {
|
||||
|
@ -198,24 +198,23 @@ PS. Because there are task switches still involved, the mode isn't as fast as
|
||||
faster than the normal fork() model, and compared to in-process fuzzing,
|
||||
should be a lot more robust.
|
||||
|
||||
## 8) Bonus feature #3: new 'trace-pc-guard' mode
|
||||
## 8) Bonus feature #3: 'trace-pc-guard' mode
|
||||
|
||||
Recent versions of LLVM are shipping with a built-in execution tracing feature
|
||||
LLVM is shipping with a built-in execution tracing feature
|
||||
that provides AFL with the necessary tracing data without the need to
|
||||
post-process the assembly or install any compiler plugins. See:
|
||||
|
||||
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
|
||||
|
||||
If you have a sufficiently recent compiler and want to give it a try, build
|
||||
afl-clang-fast this way:
|
||||
If you have not an outdated compiler and want to give it a try, build
|
||||
targets this way:
|
||||
|
||||
```
|
||||
AFL_TRACE_PC=1 make clean all
|
||||
libtarget-1.0 $ AFL_LLVM_USE_TRACE_PC=1 make
|
||||
```
|
||||
|
||||
Note that this mode is currently about 20% slower than "vanilla" afl-clang-fast,
|
||||
Note that this mode is about 20% slower than "vanilla" afl-clang-fast,
|
||||
and about 5-10% slower than afl-clang. This is likely because the
|
||||
instrumentation is not inlined, and instead involves a function call. On systems
|
||||
that support it, compiling your target with -flto should help.
|
||||
|
||||
|
||||
instrumentation is not inlined, and instead involves a function call.
|
||||
On systems that support it, compiling your target with -flto can help
|
||||
a bit.
|
||||
|
@ -204,6 +204,14 @@ static void edit_params(u32 argc, char** argv) {
|
||||
// "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
|
||||
// cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
|
||||
#else
|
||||
if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
|
||||
getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
|
||||
|
||||
} else {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
@ -211,6 +219,9 @@ static void edit_params(u32 argc, char** argv) {
|
||||
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 */
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Qunused-arguments";
|
||||
@ -282,6 +293,8 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
#ifdef USE_TRACE_PC
|
||||
|
||||
if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
|
||||
getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC"))
|
||||
if (getenv("AFL_INST_RATIO"))
|
||||
FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'.");
|
||||
|
||||
@ -444,7 +457,8 @@ int main(int argc, char** argv) {
|
||||
"You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. "
|
||||
"Setting\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);
|
||||
|
||||
exit(1);
|
||||
@ -454,6 +468,8 @@ int main(int argc, char** argv) {
|
||||
#ifdef USE_TRACE_PC
|
||||
SAYF(cCYA "afl-clang-fast" VERSION cRST
|
||||
" [tpcg] by <lszekeres@google.com>\n");
|
||||
#warning \
|
||||
"You do not need to specifically compile with USE_TRACE_PC anymore, setting the environment variable AFL_LLVM_USE_TRACE_PC is enough."
|
||||
#else
|
||||
SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n");
|
||||
#endif /* ^USE_TRACE_PC */
|
||||
|
@ -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;
|
||||
|
||||
// StringRef getPassName() const override {
|
||||
@ -156,13 +178,11 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
/* Instrument all the things! */
|
||||
|
||||
const char *IntrinsicPrefix = "llvm.";
|
||||
int inst_blocks = 0;
|
||||
|
||||
for (auto &F : M) {
|
||||
|
||||
auto Fname = F.getName();
|
||||
if (Fname.startswith(IntrinsicPrefix)) continue;
|
||||
if (isBlacklisted(&F)) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
@ -377,6 +397,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
inst_blocks++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Say something nice. */
|
||||
|
@ -18,7 +18,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
@ -42,6 +48,23 @@ class CompareTransform : public ModulePass {
|
||||
static char ID;
|
||||
CompareTransform() : ModulePass(ID) {
|
||||
|
||||
char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
@ -57,6 +80,9 @@ class CompareTransform : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
|
||||
private:
|
||||
bool transformCmps(Module &M, const bool processStrcmp,
|
||||
const bool processMemcmp, const bool processStrncmp,
|
||||
@ -104,6 +130,74 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
|
||||
bool instrumentBlock = false;
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
if (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) {
|
||||
|
||||
CallInst *callInst = nullptr;
|
||||
|
@ -15,7 +15,17 @@
|
||||
* 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/IR/DebugInfo.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||
@ -35,6 +45,41 @@ class SplitComparesTransform : public ModulePass {
|
||||
static char ID;
|
||||
SplitComparesTransform() : ModulePass(ID) {
|
||||
|
||||
char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool isBlacklisted(const Function *F) {
|
||||
|
||||
static const 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;
|
||||
@ -49,6 +94,9 @@ class SplitComparesTransform : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
|
||||
private:
|
||||
int enableFPSplit;
|
||||
|
||||
@ -77,8 +125,78 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||
* all integer comparisons with >= and <= predicates to the icomps vector */
|
||||
for (auto &F : M) {
|
||||
|
||||
if (isBlacklisted(&F)) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
bool instrumentBlock = false;
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
if (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) {
|
||||
|
||||
CmpInst *selectcmpInst = nullptr;
|
||||
|
@ -18,7 +18,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
@ -42,6 +48,41 @@ class SplitSwitchesTransform : public ModulePass {
|
||||
static char ID;
|
||||
SplitSwitchesTransform() : ModulePass(ID) {
|
||||
|
||||
char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool isBlacklisted(const Function *F) {
|
||||
|
||||
static const 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;
|
||||
@ -71,6 +112,9 @@ class SplitSwitchesTransform : public ModulePass {
|
||||
|
||||
typedef std::vector<CaseExpr> CaseVector;
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
|
||||
private:
|
||||
bool splitSwitches(Module &M);
|
||||
bool transformCmps(Module &M, const bool processStrcmp,
|
||||
@ -268,10 +312,79 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
||||
* all switches to switches vector for later processing */
|
||||
for (auto &F : M) {
|
||||
|
||||
if (isBlacklisted(&F)) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
SwitchInst *switchInst = nullptr;
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
bool instrumentBlock = false;
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
if (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->getNumCases() < 1) continue;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
@ -524,7 +524,12 @@ u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {
|
||||
struct queue_entry* q = queue;
|
||||
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;
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
|
12
src/third_party/libradamsa/Makefile
vendored
12
src/third_party/libradamsa/Makefile
vendored
@ -2,17 +2,23 @@ CUR_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
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
|
||||
$(CC) -O3 -shared libradamsa.a -o libradamsa.so
|
||||
$(CC) $(CFLAGS) -shared libradamsa.a -o libradamsa.so
|
||||
|
||||
libradamsa.a: libradamsa.c radamsa.h
|
||||
@echo " ***************************************************************"
|
||||
@echo " * Compiling libradamsa, wait some minutes (~3 on modern CPUs) *"
|
||||
@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
|
||||
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"
|
||||
rm /tmp/libradamsa-*.fuzz
|
||||
|
||||
|
25
test/test-unsigaction.c
Normal file
25
test/test-unsigaction.c
Normal 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;
|
||||
}
|
101
test/test.sh
101
test/test.sh
@ -150,15 +150,15 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" && {
|
||||
}
|
||||
echo 000000000000000000000000 > in/in2
|
||||
mkdir -p in2
|
||||
../afl-cmin -i in -o in2 -- ./test-instr.plain > /dev/null 2>&1
|
||||
../afl-cmin -i in -o in2 -- ./test-instr.plain > /dev/null
|
||||
CNT=`ls in2/ | wc -l`
|
||||
case "$CNT" in
|
||||
1| *1) $ECHO "$GREEN[+] afl-cmin correctly minimized testcase numbers" ;;
|
||||
*) $ECHO "$RED[!] afl-cmin did not correctly minimize testcase numbers"
|
||||
*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 -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}'`
|
||||
test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase"
|
||||
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 "$BLUE[*] Testing: llvm_mode"
|
||||
$ECHO "$BLUE[*] Testing: llvm_mode, afl-showmap, afl-fuzz, afl-cmin and afl-tmin"
|
||||
test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
|
||||
# on FreeBSD need to set AFL_CC
|
||||
test `uname -s` = 'FreeBSD' && {
|
||||
if which clang >/dev/null; then
|
||||
export AFL_CC=`which clang`
|
||||
else
|
||||
export AFL_CC=`$LLVM_CONFIG --bindir`/clang
|
||||
fi
|
||||
}
|
||||
../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1
|
||||
AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1
|
||||
test -e test-instr.plain && {
|
||||
@ -251,6 +253,26 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
|
||||
$ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode"
|
||||
CODE=1
|
||||
}
|
||||
test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" || {
|
||||
echo 000000000000000000000000 > in/in2
|
||||
mkdir -p in2
|
||||
../afl-cmin -i in -o in2 -- ./test-instr.plain > /dev/null
|
||||
CNT=`ls in2/ | wc -l`
|
||||
case "$CNT" in
|
||||
*1) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;;
|
||||
*) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases"
|
||||
CODE=1
|
||||
;;
|
||||
esac
|
||||
../afl-tmin -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1
|
||||
SIZE=`ls -l in2/in2 2> /dev/null | awk '{print$5}'`
|
||||
test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase"
|
||||
test "$SIZE" = 1 || {
|
||||
$ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE"
|
||||
CODE=1
|
||||
}
|
||||
rm -rf in2
|
||||
}
|
||||
rm -rf in out errors
|
||||
}
|
||||
rm -f test-instr.plain
|
||||
@ -334,7 +356,7 @@ test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && {
|
||||
$ECHO "$GREEN[+] gcc_plugin run reported $TUPLES instrumented locations which is fine"
|
||||
} || {
|
||||
$ECHO "$RED[!] gcc_plugin instrumentation produces a weird number of instrumented locations: $TUPLES"
|
||||
$ECHO "$YELLOW[-] the gcc_plugin instrumentation issue is not flagged as an error because travis builds would all fail otherwise :-("
|
||||
$ECHO "$YELLOW[-] this is a known issue in gcc, not afl++. It is not flagged as an error because travis builds would all fail otherwise :-("
|
||||
#CODE=1
|
||||
}
|
||||
}
|
||||
@ -457,6 +479,15 @@ test -e ../libdislocator.so && {
|
||||
}
|
||||
rm -f test-compcov
|
||||
test -e ../libradamsa.so && {
|
||||
# on FreeBSD need to set AFL_CC
|
||||
|
||||
test `uname -s` = 'FreeBSD' && {
|
||||
if which clang >/dev/null; then
|
||||
export AFL_CC=`which clang`
|
||||
else
|
||||
export AFL_CC=`$LLVM_CONFIG --bindir`/clang
|
||||
fi
|
||||
}
|
||||
test -e test-instr.plain || ../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1
|
||||
test -e test-instr.plain || ../afl-gcc-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1
|
||||
test -e test-instr.plain || ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1
|
||||
@ -560,8 +591,64 @@ test -e ../afl-qemu-trace && {
|
||||
CODE=1
|
||||
exit 1
|
||||
}
|
||||
$ECHO "$YELLOW[-] we need a test case for qemu_mode unsigaction library"
|
||||
rm -rf in out errors
|
||||
test -e ../qemu_mode/unsigaction/unsigaction32.so && {
|
||||
${AFL_CC} -o test-unsigaction32 -m32 test-unsigaction.c >> errors 2>&1 && {
|
||||
./test-unsigaction32
|
||||
RETVAL_NORMAL32=$?
|
||||
LD_PRELOAD=../qemu_mode/unsigaction/unsigaction32.so ./test-unsigaction32
|
||||
RETVAL_LIBUNSIGACTION32=$?
|
||||
test $RETVAL_NORMAL32 = "2" -a $RETVAL_LIBUNSIGACTION32 = "0" && {
|
||||
$ECHO "$GREEN[+] qemu_mode unsigaction library (32 bit) ignores signals"
|
||||
} || {
|
||||
test $RETVAL_NORMAL32 != "2" && {
|
||||
$ECHO "$RED[!] cannot trigger signal in test program (32 bit)"
|
||||
}
|
||||
test $RETVAL_LIBUNSIGACTION32 != "0" && {
|
||||
$ECHO "$RED[!] signal in test program (32 bit) is not ignored with unsigaction"
|
||||
}
|
||||
CODE=1
|
||||
}
|
||||
} || {
|
||||
echo CUT------------------------------------------------------------------CUT
|
||||
cat errors
|
||||
echo CUT------------------------------------------------------------------CUT
|
||||
$ECHO "$RED[!] cannot compile test program (32 bit) for unsigaction library"
|
||||
CODE=1
|
||||
}
|
||||
} || {
|
||||
$ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (32 bit) because it is not present"
|
||||
INCOMPLETE=1
|
||||
}
|
||||
test -e ../qemu_mode/unsigaction/unsigaction64.so && {
|
||||
${AFL_CC} -o test-unsigaction64 -m64 test-unsigaction.c >> errors 2>&1 && {
|
||||
./test-unsigaction64
|
||||
RETVAL_NORMAL64=$?
|
||||
LD_PRELOAD=../qemu_mode/unsigaction/unsigaction64.so ./test-unsigaction64
|
||||
RETVAL_LIBUNSIGACTION64=$?
|
||||
test $RETVAL_NORMAL64 = "2" -a $RETVAL_LIBUNSIGACTION64 = "0" && {
|
||||
$ECHO "$GREEN[+] qemu_mode unsigaction library (64 bit) ignores signals"
|
||||
} || {
|
||||
test $RETVAL_NORMAL64 != "2" && {
|
||||
$ECHO "$RED[!] cannot trigger signal in test program (64 bit)"
|
||||
}
|
||||
test $RETVAL_LIBUNSIGACTION64 != "0" && {
|
||||
$ECHO "$RED[!] signal in test program (64 bit) is not ignored with unsigaction"
|
||||
}
|
||||
CODE=1
|
||||
}
|
||||
} || {
|
||||
echo CUT------------------------------------------------------------------CUT
|
||||
cat errors
|
||||
echo CUT------------------------------------------------------------------CUT
|
||||
$ECHO "$RED[!] cannot compile test program (64 bit) for unsigaction library"
|
||||
CODE=1
|
||||
}
|
||||
} || {
|
||||
$ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (64 bit) because it is not present"
|
||||
INCOMPLETE=1
|
||||
}
|
||||
rm -rf errors test-unsigaction32 test-unsigaction64
|
||||
}
|
||||
} || {
|
||||
$ECHO "$RED[!] gcc compilation of test targets failed - what is going on??"
|
||||
|
@ -1,11 +1,10 @@
|
||||
/*
|
||||
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
|
||||
simple_target's buffer (hardcoded to be at 0x300000), and executes 'main()'.
|
||||
If any crashes occur during emulation, this script throws a matching signal
|
||||
to tell AFL that a crash occurred.
|
||||
argv[1], sets up argv, and argc and executes 'main()'.
|
||||
If run inside AFL, afl_fuzz automatically does the "right thing"
|
||||
|
||||
Run under AFL as follows:
|
||||
|
||||
|
Reference in New Issue
Block a user