mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-24 14:43:22 +00:00
Compare commits
289 Commits
Author | SHA1 | Date | |
---|---|---|---|
87a693d1a9 | |||
49bd24144a | |||
182b8a4582 | |||
97bddc8cfa | |||
a55e26959b | |||
63e2222af1 | |||
9637fe8a74 | |||
c084458294 | |||
498e9f4298 | |||
13033034db | |||
749c63d3b3 | |||
73c2619c33 | |||
65f9553365 | |||
3f621c8ed4 | |||
11f3b487ee | |||
4c253aedae | |||
efdad526ee | |||
4081a8f7b6 | |||
867f948bb2 | |||
767ed8c5da | |||
f0aadc3d0b | |||
d95c4483fd | |||
00a147b244 | |||
74eae83b54 | |||
324b44872c | |||
94a1d4d3ac | |||
a3392007cd | |||
4ac06a4eef | |||
0495ded87d | |||
64b80b3201 | |||
fa20eb1de7 | |||
ae15803bf1 | |||
9e375179d8 | |||
460760d7b6 | |||
682b620922 | |||
6b69cd2e57 | |||
56a86bb9e2 | |||
00ad2ffc61 | |||
a16e92800d | |||
7c9ff4bfe7 | |||
d334093606 | |||
044bd3cb41 | |||
4e192db13c | |||
8cc5442401 | |||
bd94d5fce7 | |||
b6be906082 | |||
60a5df5262 | |||
c384a17b41 | |||
6fdd6004f2 | |||
c4fe6f5277 | |||
645e331559 | |||
45bddcd808 | |||
9627458ecc | |||
f8b3d34225 | |||
5273c61cd8 | |||
0c1c947aaf | |||
6224ae1c60 | |||
72f4a9f678 | |||
060f4ea320 | |||
6177954773 | |||
7b40d7b942 | |||
1317433a51 | |||
a578d719e1 | |||
8bb10c3bf1 | |||
b920cd2f23 | |||
38dac93f63 | |||
6f66be12f6 | |||
4ee93331dc | |||
30a675ab87 | |||
845522f59b | |||
f37be09a92 | |||
ed877f5e3e | |||
d916403927 | |||
41f6aa7940 | |||
50a63777ec | |||
26fe7a9d66 | |||
07a0e2caf7 | |||
30bfd44dfd | |||
26f8708fed | |||
3beec8d4fa | |||
2e553bcd69 | |||
0e5a5f1805 | |||
d02cfc54b6 | |||
7b9ac9d414 | |||
515de0d68d | |||
1eeb6785ad | |||
20392878f1 | |||
102067d43d | |||
6c88e21459 | |||
041f19494e | |||
8e9f507bbc | |||
c380819e02 | |||
fa84e52af0 | |||
cf9238e09d | |||
190f3024da | |||
768053b6f2 | |||
bdd2a412c4 | |||
d217c7df05 | |||
9484da57ed | |||
ef2ccc8117 | |||
02887dc164 | |||
d048af11cd | |||
0559d1d171 | |||
37b681ac11 | |||
f065ddbdb2 | |||
140053502b | |||
58fad91b0b | |||
01b5aa123d | |||
8cdf767bf5 | |||
e910882e32 | |||
a1c9c497d5 | |||
a63c838b10 | |||
b7e574607c | |||
10e6b4e454 | |||
ec5b1924c4 | |||
80ddb484de | |||
cafb2e540e | |||
b4e3f22259 | |||
df52157834 | |||
a13958b32b | |||
a31b58eeea | |||
e31b816aa0 | |||
0e5027d8d8 | |||
dc79533191 | |||
128e4d5565 | |||
ad3960580d | |||
00683d06c2 | |||
a38980c80b | |||
664a180d72 | |||
d6346561db | |||
d82ada89fe | |||
9d384b4e38 | |||
6e45e55d82 | |||
ecaccd9739 | |||
95a2d49232 | |||
16c16b3e6e | |||
945e00b73f | |||
e592b4bcf0 | |||
96ef2d3821 | |||
2d126dc750 | |||
13a32e9595 | |||
73f7164048 | |||
5b1b986c89 | |||
1c53bbea52 | |||
0c5c172a30 | |||
ff1643d81f | |||
378573ab8b | |||
33ddf6ea0e | |||
a2bc3538f7 | |||
636e98d151 | |||
9f01737fa7 | |||
be4e5d2617 | |||
cc78fb721b | |||
bb7d2a7347 | |||
9d03763d94 | |||
3cf4529f3c | |||
477fb58311 | |||
59043b24cc | |||
15547eb654 | |||
16f9cc7369 | |||
efa9df24c2 | |||
a37eca9df5 | |||
e68d2345d5 | |||
fced3e00ce | |||
c53663c7ac | |||
bc2e65e482 | |||
ce2814967d | |||
781725aeaf | |||
9276dc9e6c | |||
c7de368dc2 | |||
fbd9994f6f | |||
087c368242 | |||
a56354a893 | |||
a5d4c8d532 | |||
a46fe3ad43 | |||
4ffa5b0636 | |||
62ec52dd95 | |||
ea876e59a8 | |||
66eee34709 | |||
85627516a4 | |||
b26ee09f71 | |||
fb89b042f8 | |||
646aeb2b18 | |||
2a60ceb694 | |||
80916a3613 | |||
0c3d06c41e | |||
07db922024 | |||
fa610270ab | |||
cefefba244 | |||
42017bbeda | |||
a3ee281e2b | |||
232290108e | |||
5c017d7071 | |||
62aacf88ab | |||
b3e77d3d50 | |||
766085293d | |||
4a593d0405 | |||
82b6b8c87e | |||
b6f9f4c436 | |||
b120ca27f8 | |||
5eb1f3a4c6 | |||
8ada9d06e8 | |||
cce8c4dbae | |||
3502db1ac5 | |||
df8a0e8418 | |||
b8a25063f6 | |||
6df21f3489 | |||
dcba2c3642 | |||
059c963467 | |||
ce9c6df456 | |||
0aef3b4040 | |||
4cc0589440 | |||
96722083d8 | |||
ee238eb00d | |||
45ccc7d475 | |||
a32d2ad193 | |||
f25919ad56 | |||
ae524d856d | |||
9be4f9c055 | |||
40e5b285f2 | |||
3ca787ba76 | |||
137b9ecf5e | |||
2509624add | |||
8c6fcd98be | |||
7dc825dbe9 | |||
6b3336d107 | |||
ceeb266273 | |||
9bb0733eb5 | |||
22cdad2d20 | |||
8b3befea6d | |||
5b9928f1a9 | |||
856a59901e | |||
19d0961020 | |||
3bd5e65edc | |||
b520046ab6 | |||
d22550a520 | |||
6f994ec56b | |||
09c8e40363 | |||
8e44c06a13 | |||
358f17f615 | |||
b109e31722 | |||
b77458ae81 | |||
cfeb8e83f7 | |||
0fa9ad46ae | |||
cfb11177cd | |||
0a1979fd20 | |||
0dc64b93d8 | |||
f6c9acd518 | |||
68218dd31c | |||
441b64b467 | |||
8b319969f3 | |||
41b1787565 | |||
e6fccdd9c1 | |||
0ffef8c79d | |||
3d52079a7c | |||
e47c29e728 | |||
280374f739 | |||
ce15937717 | |||
c8f2ba5b49 | |||
3dbfd18f36 | |||
4be0ae2008 | |||
67b39050df | |||
4ccd8c1400 | |||
4f997665f1 | |||
b6a15d9719 | |||
e90194093e | |||
4c90293e44 | |||
8197e9b2e4 | |||
baec99079f | |||
16a5e6bf16 | |||
39f715982d | |||
724d4ec3de | |||
e4670d3abc | |||
8aa86d063a | |||
c1d9e00044 | |||
0827a447d3 | |||
2874565b36 | |||
8ed3126f28 | |||
b408fdffcc | |||
d9cd600c1b | |||
68f18923ab | |||
9e74a7dfe1 | |||
0b5b888f82 | |||
46854b439a | |||
8ffed4b859 | |||
3b8cd9652a | |||
269050aee3 | |||
6e753f8f0f | |||
9adcc73d61 |
@ -10,7 +10,7 @@ AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
@ -72,7 +72,7 @@ IncludeCategories:
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentPPDirectives: BeforeHash
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
|
@ -29,14 +29,14 @@ CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN")
|
||||
if CLANG_FORMAT_BIN is None:
|
||||
o = 0
|
||||
try:
|
||||
p = subprocess.Popen(["clang-format-8", "--version"], stdout=subprocess.PIPE)
|
||||
p = subprocess.Popen(["clang-format-10", "--version"], stdout=subprocess.PIPE)
|
||||
o, _ = p.communicate()
|
||||
o = str(o, "utf-8")
|
||||
o = o[len("clang-format version "):].strip()
|
||||
o = o[:o.find(".")]
|
||||
o = int(o)
|
||||
except:
|
||||
print ("clang-format-8 is needed. Aborted.")
|
||||
print ("clang-format-10 is needed. Aborted.")
|
||||
exit(1)
|
||||
#if o < 7:
|
||||
# if subprocess.call(['which', 'clang-format-7'], stdout=subprocess.PIPE) == 0:
|
||||
@ -51,7 +51,7 @@ if CLANG_FORMAT_BIN is None:
|
||||
# print ("clang-format 7 or above is needed. Aborted.")
|
||||
# exit(1)
|
||||
else:
|
||||
CLANG_FORMAT_BIN = 'clang-format-8'
|
||||
CLANG_FORMAT_BIN = 'clang-format-10'
|
||||
|
||||
COLUMN_LIMIT = 80
|
||||
for line in fmt.split("\n"):
|
||||
@ -70,8 +70,8 @@ def custom_format(filename):
|
||||
out = ""
|
||||
|
||||
for line in src.split("\n"):
|
||||
if line.startswith("#"):
|
||||
if line.startswith("#define"):
|
||||
if line.lstrip().startswith("#"):
|
||||
if line[line.find("#")+1:].lstrip().startswith("define"):
|
||||
in_define = True
|
||||
|
||||
if "/*" in line and not line.strip().startswith("/*") and line.endswith("*/") and len(line) < (COLUMN_LIMIT-2):
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -42,10 +42,11 @@ qemu_mode/libcompcov/compcovtest
|
||||
as
|
||||
ld
|
||||
qemu_mode/qemu-*
|
||||
unicorn_mode/unicornafl/
|
||||
unicorn_mode/samples/*/\.test-*
|
||||
unicorn_mode/samples/*/output/
|
||||
core\.*
|
||||
test/unittests/unit_maybe_alloc
|
||||
test/unittests/unit_preallocable
|
||||
test/unittests/unit_list
|
||||
examples/afl_network_proxy/afl-network-server
|
||||
examples/afl_network_proxy/afl-network-client
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "unicorn_mode/unicornafl"]
|
||||
path = unicorn_mode/unicornafl
|
||||
url = https://github.com/AFLplusplus/unicornafl.git
|
@ -9,6 +9,9 @@ branches:
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: focal
|
||||
env: NAME="focal-amd64" MODERN="yes" GCC="9"
|
||||
- os: linux
|
||||
dist: bionic
|
||||
env: NAME="bionic-amd64" MODERN="yes" GCC="7"
|
||||
@ -32,7 +35,7 @@ jobs:
|
||||
- arch: arm64
|
||||
|
||||
env:
|
||||
- AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_NO_UI=1 AFL_STOP_MANUALLY=1
|
||||
- AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_NO_UI=1
|
||||
# - AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_NO_UI=1 AFL_EXIT_WHEN_DONE=1
|
||||
# TODO: test AFL_BENCH_UNTIL_CRASH once we have a target that crashes
|
||||
# - AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_NO_UI=1 AFL_BENCH_JUST_ONE=1
|
||||
@ -41,8 +44,8 @@ before_install:
|
||||
# export LLVM_DIR=${TRAVIS_BUILD_DIR}/${LLVM_PACKAGE}
|
||||
- echo Testing on $NAME
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then wget "$LINK""$NAME".tar.xz ; export LLVM_CONFIG=`pwd`/"$NAME" ; tar xJf "$NAME".tar.xz ; fi
|
||||
- if [ "$MODERN" = "yes" ]; then sudo apt update ; sudo apt upgrade ; sudo apt install -y libtool libtool-bin automake bison libglib2.0 build-essential clang gcc-"$GCC" gcc-"$GCC"-plugin-dev libc++-"$GCC"-dev findutils libcmocka-dev ; fi
|
||||
- if [ "$MODERN" = "no" ]; then sudo apt update ; sudo apt install -y libtool $EXTRA libpixman-1-dev automake bison libglib2.0 build-essential gcc-"$GCC" gcc-"$GCC"-plugin-dev libc++-dev findutils libcmocka-dev ; fi
|
||||
- if [ "$MODERN" = "yes" ]; then sudo apt update ; sudo apt upgrade ; sudo apt install -y git libtool libtool-bin automake bison libglib2.0-0 build-essential clang gcc-"$GCC" gcc-"$GCC"-plugin-dev libc++-"$GCC"-dev findutils libcmocka-dev python3-setuptools ; fi
|
||||
- if [ "$MODERN" = "no" ]; then sudo apt update ; sudo apt install -y git libtool $EXTRA libpixman-1-dev automake bison libglib2.0 build-essential gcc-"$GCC" gcc-"$GCC"-plugin-dev libc++-dev findutils libcmocka-dev python3-setuptools ; fi
|
||||
|
||||
script:
|
||||
- gcc -v
|
||||
|
10
Dockerfile
10
Dockerfile
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:eoan
|
||||
FROM ubuntu
|
||||
MAINTAINER David Carlier <devnexen@gmail.com>
|
||||
LABEL "about"="AFLplusplus docker image"
|
||||
RUN apt-get update && apt-get -y install \
|
||||
@ -10,8 +10,10 @@ RUN apt-get update && apt-get -y install \
|
||||
clang-9 \
|
||||
flex \
|
||||
git \
|
||||
python3.7 \
|
||||
python3.7-dev \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-setuptools \
|
||||
python-is-python3 \
|
||||
gcc-9 \
|
||||
gcc-9-plugin-dev \
|
||||
gcc-9-multilib \
|
||||
@ -20,8 +22,6 @@ RUN apt-get update && apt-get -y install \
|
||||
libtool-bin \
|
||||
libglib2.0-dev \
|
||||
llvm-9-dev \
|
||||
python-setuptools \
|
||||
python2.7-dev \
|
||||
wget \
|
||||
ca-certificates \
|
||||
libpixman-1-dev \
|
||||
|
192
GNUmakefile
192
GNUmakefile
@ -52,14 +52,30 @@ endif
|
||||
|
||||
ifneq "$(shell uname)" "Darwin"
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
CFLAGS_OPT = -march=native
|
||||
CFLAGS_OPT += -march=native
|
||||
endif
|
||||
# OS X does not like _FORTIFY_SOURCE=2
|
||||
CFLAGS_OPT += -D_FORTIFY_SOURCE=2
|
||||
endif
|
||||
|
||||
ifdef STATIC
|
||||
$(info Compiling static version of binaries)
|
||||
# Disable python for static compilation to simplify things
|
||||
PYTHON_OK=0
|
||||
PYFLAGS=
|
||||
|
||||
CFLAGS_OPT += -static
|
||||
LDFLAGS += -lm -lpthread -lz -lutil
|
||||
endif
|
||||
|
||||
ifdef PROFILING
|
||||
$(info Compiling with profiling information, for analysis: gprof ./afl-fuzz gmon.out > prof.txt)
|
||||
CFLAGS_OPT += -pg -DPROFILING=1
|
||||
LDFLAGS += -pg
|
||||
endif
|
||||
|
||||
ifneq "$(shell uname -m)" "x86_64"
|
||||
ifneq "$(shell uname -m)" "i386"
|
||||
ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386"
|
||||
ifneq "$(shell uname -m)" "amd64"
|
||||
ifneq "$(shell uname -m)" "i86pc"
|
||||
AFL_NO_X86=1
|
||||
@ -73,13 +89,23 @@ override CFLAGS += -Wall -g -Wno-pointer-sign -Wmissing-declarations\
|
||||
-I include/ -Werror -DAFL_PATH=\"$(HELPER_PATH)\" \
|
||||
-DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\"
|
||||
|
||||
ifeq "$(shell uname -s)" "OpenBSD"
|
||||
override CFLAGS += -I /usr/local/include/
|
||||
LDFLAGS += -L /usr/local/lib/
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname -s)" "NetBSD"
|
||||
override CFLAGS += -I /usr/pkg/include/
|
||||
LDFLAGS += -L /usr/pkg/lib/
|
||||
endif
|
||||
|
||||
AFL_FUZZ_FILES = $(wildcard src/afl-fuzz*.c)
|
||||
|
||||
ifneq "$(shell command -v python3m 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3m-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3m-config --includes)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3m --version 2>&1))
|
||||
# Starting with python3.8, we need to pass the `embed` flag. Earier versions didn't know this flag.
|
||||
# Starting with python3.8, we need to pass the `embed` flag. Earlier versions didn't know this flag.
|
||||
ifeq "$(shell python3m-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1"
|
||||
PYTHON_LIB ?= $(shell python3m-config --libs --embed --ldflags)
|
||||
else
|
||||
@ -88,24 +114,50 @@ ifneq "$(shell command -v python3m 2>/dev/null)" ""
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq "$(shell command -v python3 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3-config --includes)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3 --version 2>&1))
|
||||
# Starting with python3.8, we need to pass the `embed` flag. Earier versions didn't know this flag.
|
||||
ifeq "$(shell python3-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1"
|
||||
PYTHON_LIB ?= $(shell python3-config --libs --embed --ldflags)
|
||||
else
|
||||
PYTHON_LIB ?= $(shell python3-config --ldflags)
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python3 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3-config --includes)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3 --version 2>&1))
|
||||
# Starting with python3.8, we need to pass the `embed` flag. Earier versions didn't know this flag.
|
||||
ifeq "$(shell python3-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1"
|
||||
PYTHON_LIB ?= $(shell python3-config --libs --embed --ldflags)
|
||||
else
|
||||
PYTHON_LIB ?= $(shell python3-config --ldflags)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq "$(shell command -v python 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python-config --includes)
|
||||
PYTHON_LIB ?= $(shell python-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python --version 2>&1))
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python-config --includes)
|
||||
PYTHON_LIB ?= $(shell python-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python --version 2>&1))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# Old Ubuntu and others dont have python/python3-config so we hardcode 3.7
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python3.7 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3.7-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3.7-config --includes)
|
||||
PYTHON_LIB ?= $(shell python3.7-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3.7 --version 2>&1))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# Old Ubuntu and others dont have python/python2-config so we hardcode 2.7
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python2.7 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python2.7-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python2.7-config --includes)
|
||||
PYTHON_LIB ?= $(shell python2.7-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python2.7 --version 2>&1))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -116,23 +168,23 @@ else
|
||||
endif
|
||||
|
||||
ifneq "$(filter Linux GNU%,$(shell uname))" ""
|
||||
LDFLAGS += -ldl
|
||||
LDFLAGS += -ldl
|
||||
endif
|
||||
|
||||
ifneq "$(findstring FreeBSD, $(shell uname))" ""
|
||||
CFLAGS += -pthread
|
||||
LDFLAGS += -lpthread
|
||||
CFLAGS += -pthread
|
||||
LDFLAGS += -lpthread
|
||||
endif
|
||||
|
||||
ifneq "$(findstring NetBSD, $(shell uname))" ""
|
||||
CFLAGS += -pthread
|
||||
LDFLAGS += -lpthread
|
||||
CFLAGS += -pthread
|
||||
LDFLAGS += -lpthread
|
||||
endif
|
||||
|
||||
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
|
||||
TEST_CC = afl-gcc
|
||||
TEST_CC = afl-gcc
|
||||
else
|
||||
TEST_CC = afl-clang
|
||||
TEST_CC = afl-clang
|
||||
endif
|
||||
|
||||
COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h
|
||||
@ -150,18 +202,18 @@ ifdef NO_PYTHON
|
||||
PYFLAGS=
|
||||
endif
|
||||
|
||||
ifdef STATIC
|
||||
$(info Compiling static version of binaries)
|
||||
# Disable python for static compilation to simplify things
|
||||
PYTHON_OK=0
|
||||
PYFLAGS=
|
||||
|
||||
CFLAGS += -static
|
||||
LDFLAGS += -lm -lpthread -lz -lutil
|
||||
IN_REPO=0
|
||||
ifeq "$(shell command -v git >/dev/null && git status >/dev/null 2>&1 && echo 1 || echo 0)" "1"
|
||||
IN_REPO=1
|
||||
endif
|
||||
ifeq "$(shell command -v svn >/dev/null && svn proplist . 2>/dev/null && echo 1 || echo 0)" "1"
|
||||
IN_REPO=1
|
||||
endif
|
||||
|
||||
ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
|
||||
ASAN_LDFLAGS+=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
|
||||
ifeq "$(shell echo 'int main() { return 0;}' | $(CC) $(CFLAGS) -fsanitize=address -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1"
|
||||
ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
|
||||
ASAN_LDFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
ifdef ASAN_BUILD
|
||||
$(info Compiling ASAN version of binaries)
|
||||
@ -169,12 +221,6 @@ ifdef ASAN_BUILD
|
||||
LDFLAGS+=$(ASAN_LDFLAGS)
|
||||
endif
|
||||
|
||||
ifdef PROFILING
|
||||
$(info Compiling profiling version of binaries)
|
||||
CFLAGS+=-pg
|
||||
LDFLAGS+=-pg
|
||||
endif
|
||||
|
||||
ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1"
|
||||
SHMAT_OK=1
|
||||
else
|
||||
@ -214,10 +260,11 @@ help:
|
||||
@echo "distrib: everything (for both binary-only and source code fuzzing)"
|
||||
@echo "man: creates simple man pages from the help option of the programs"
|
||||
@echo "install: installs everything you have compiled with the build option above"
|
||||
@echo "clean: cleans everything. for qemu_mode it means it deletes all downloads as well"
|
||||
@echo "clean: cleans everything compiled (not downloads when on a checkout)"
|
||||
@echo "deepclean: cleans everything including downloads"
|
||||
@echo "code-format: format the code, do this before you commit and send a PR please!"
|
||||
@echo "tests: this runs the test framework. It is more catered for the developers, but if you run into problems this helps pinpointing the problem"
|
||||
@echo "unit: perform unit tests (based on cmocka)"
|
||||
@echo "unit: perform unit tests (based on cmocka and GNU linker)"
|
||||
@echo "document: creates afl-fuzz-document which will only do one run and save all manipulated inputs into out/queue/mutations"
|
||||
@echo "help: shows these build options :-)"
|
||||
@echo "=========================================="
|
||||
@ -272,7 +319,7 @@ test_python:
|
||||
else
|
||||
|
||||
test_python:
|
||||
@echo "[-] You seem to need to install the package python3-dev or python2-dev (and perhaps python[23]-apt), but it is optional so we continue"
|
||||
@echo "[-] You seem to need to install the package python3-dev, python2-dev or python-dev (and perhaps python[23]-apt), but it is optional so we continue"
|
||||
|
||||
endif
|
||||
|
||||
@ -304,19 +351,19 @@ src/third_party/libradamsa/libradamsa.so: src/third_party/libradamsa/libradamsa.
|
||||
$(MAKE) -C src/third_party/libradamsa/ CFLAGS="$(CFLAGS)"
|
||||
|
||||
afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o | test_x86
|
||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(PYFLAGS) $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(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) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS)
|
||||
afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.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) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(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) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS)
|
||||
|
||||
afl-gotcpu: src/afl-gotcpu.c src/afl-common.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c src/afl-common.o -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o -o $@ $(LDFLAGS)
|
||||
|
||||
|
||||
# document all mutations and only do one run (use with only one input file!)
|
||||
@ -324,34 +371,43 @@ document: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/
|
||||
$(CC) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o afl-fuzz-document $(PYFLAGS) $(LDFLAGS)
|
||||
|
||||
test/unittests/unit_maybe_alloc.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_maybe_alloc.c $(AFL_FUZZ_FILES)
|
||||
$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_maybe_alloc.c -o test/unittests/unit_maybe_alloc.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_maybe_alloc.c -o test/unittests/unit_maybe_alloc.o
|
||||
|
||||
test/unittests/unit_preallocable.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_preallocable.c $(AFL_FUZZ_FILES)
|
||||
$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_preallocable.c -o test/unittests/unit_preallocable.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_preallocable.c -o test/unittests/unit_preallocable.o
|
||||
|
||||
unit_maybe_alloc: test/unittests/unit_maybe_alloc.o
|
||||
$(CC) $(CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_maybe_alloc.o -o test/unittests/unit_maybe_alloc $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
@$(CC) $(CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_maybe_alloc.o -o test/unittests/unit_maybe_alloc $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
./test/unittests/unit_maybe_alloc
|
||||
|
||||
test/unittests/unit_list.o : $(COMM_HDR) include/list.h test/unittests/unit_list.c $(AFL_FUZZ_FILES)
|
||||
$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_list.c -o test/unittests/unit_list.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_list.c -o test/unittests/unit_list.o
|
||||
|
||||
unit_list: test/unittests/unit_list.o
|
||||
$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_list.o -o test/unittests/unit_list $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_list.o -o test/unittests/unit_list $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
./test/unittests/unit_list
|
||||
|
||||
test/unittests/preallocable.o : $(COMM_HDR) include/afl-prealloc.h test/unittests/preallocable.c $(AFL_FUZZ_FILES)
|
||||
$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CFLAGS_FLTO) -c test/unittests/preallocable.c -o test/unittests/preallocable.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CFLAGS_FLTO) -c test/unittests/preallocable.c -o test/unittests/preallocable.o
|
||||
|
||||
unit_preallocable: test/unittests/unit_preallocable.o
|
||||
$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_preallocable.o -o test/unittests/unit_preallocable $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_preallocable.o -o test/unittests/unit_preallocable $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
./test/unittests/unit_preallocable
|
||||
|
||||
unit_clean:
|
||||
@rm -f ./test/unittests/unit_preallocable ./test/unittests/unit_list ./test/unittests/unit_maybe_alloc test/unittests/*.o
|
||||
|
||||
ifneq "$(shell uname)" "Darwin"
|
||||
|
||||
unit: unit_maybe_alloc unit_preallocable unit_list unit_clean
|
||||
|
||||
else
|
||||
|
||||
unit:
|
||||
@echo [-] unit tests are skipped on Darwin \(lacks GNU linker feature --wrap\)
|
||||
|
||||
endif
|
||||
|
||||
code-format:
|
||||
./.custom-format.py -i src/*.c
|
||||
./.custom-format.py -i include/*.h
|
||||
@ -401,27 +457,40 @@ all_done: test_build
|
||||
@if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi
|
||||
@! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.md for advice.\033[0m\n" 2>/dev/null
|
||||
|
||||
.NOTPARALLEL: clean
|
||||
.NOTPARALLEL: clean all
|
||||
|
||||
clean:
|
||||
rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.1.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable
|
||||
rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-*
|
||||
rm -rf out_dir qemu_mode/qemu-3.1.1 *.dSYM */*.dSYM
|
||||
-$(MAKE) -C llvm_mode clean
|
||||
-$(MAKE) -C gcc_plugin clean
|
||||
$(MAKE) -C libdislocator clean
|
||||
$(MAKE) -C libtokencap clean
|
||||
$(MAKE) -C examples/afl_network_proxy clean
|
||||
$(MAKE) -C examples/socket_fuzzing clean
|
||||
$(MAKE) -C examples/argv_fuzzing clean
|
||||
$(MAKE) -C qemu_mode/unsigaction clean
|
||||
$(MAKE) -C qemu_mode/libcompcov clean
|
||||
$(MAKE) -C src/third_party/libradamsa/ clean
|
||||
-rm -rf unicorn_mode/unicornafl
|
||||
rm -rf qemu_mode/qemu-3.1.1
|
||||
ifeq "$(IN_REPO)" "1"
|
||||
test -d unicorn_mode/unicornafl && $(MAKE) -C unicorn_mode/unicornafl clean || true
|
||||
else
|
||||
rm -rf qemu_mode/qemu-3.1.1.tar.xz
|
||||
rm -rf unicorn_mode/unicornafl
|
||||
endif
|
||||
|
||||
deepclean: clean
|
||||
rm -rf qemu_mode/qemu-3.1.1.tar.xz
|
||||
rm -rf unicorn_mode/unicornafl
|
||||
git reset --hard >/dev/null 2>&1 || true
|
||||
|
||||
distrib: all radamsa
|
||||
-$(MAKE) -C llvm_mode
|
||||
-$(MAKE) -C gcc_plugin
|
||||
$(MAKE) -C libdislocator
|
||||
$(MAKE) -C libtokencap
|
||||
$(MAKE) -C examples/afl_network_proxy
|
||||
$(MAKE) -C examples/socket_fuzzing
|
||||
$(MAKE) -C examples/argv_fuzzing
|
||||
cd qemu_mode && sh ./build_qemu_support.sh
|
||||
@ -430,6 +499,7 @@ distrib: all radamsa
|
||||
binary-only: all radamsa
|
||||
$(MAKE) -C libdislocator
|
||||
$(MAKE) -C libtokencap
|
||||
$(MAKE) -C examples/afl_network_proxy
|
||||
$(MAKE) -C examples/socket_fuzzing
|
||||
$(MAKE) -C examples/argv_fuzzing
|
||||
cd qemu_mode && sh ./build_qemu_support.sh
|
||||
@ -440,6 +510,9 @@ source-only: all radamsa
|
||||
-$(MAKE) -C gcc_plugin
|
||||
$(MAKE) -C libdislocator
|
||||
$(MAKE) -C libtokencap
|
||||
#$(MAKE) -C examples/afl_network_proxy
|
||||
#$(MAKE) -C examples/socket_fuzzing
|
||||
#$(MAKE) -C examples/argv_fuzzing
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@
|
||||
@ -475,6 +548,7 @@ install: all $(MANPAGES)
|
||||
if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi
|
||||
if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi
|
||||
if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi
|
||||
if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi
|
||||
|
||||
set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++
|
||||
set -e; if [ -f afl-clang-fast ] ; then ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi
|
||||
|
42
Makefile
42
Makefile
@ -1,2 +1,42 @@
|
||||
all:
|
||||
@echo please use GNU make, thanks!
|
||||
@echo trying to use GNU make...
|
||||
@gmake all
|
||||
|
||||
source-only:
|
||||
@gmake source-only
|
||||
|
||||
binary-only:
|
||||
@gmake binary-only
|
||||
|
||||
distrib:
|
||||
@gmake distrib
|
||||
|
||||
man:
|
||||
@gmake man
|
||||
|
||||
install:
|
||||
@gmake install
|
||||
|
||||
document:
|
||||
@gmake document
|
||||
|
||||
deepclean:
|
||||
@gmake deepclean
|
||||
|
||||
code-format:
|
||||
@gmake code-format
|
||||
|
||||
help:
|
||||
@gmake help
|
||||
|
||||
tests:
|
||||
@gmake tests
|
||||
|
||||
unit:
|
||||
@gmake unit
|
||||
|
||||
unit_clean:
|
||||
@gmake unit_clean
|
||||
|
||||
clean:
|
||||
@gmake clean
|
||||
|
16
README.md
16
README.md
@ -4,9 +4,9 @@
|
||||
|
||||

|
||||
|
||||
Release Version: [2.64c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
Release Version: [2.65c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
|
||||
Github Version: 2.64d
|
||||
Github Version: 2.65d
|
||||
|
||||
includes all necessary/interesting changes from Google's afl 2.56b
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
|
||||
(3) partially via AFL_CODE_START/AFL_CODE_END
|
||||
|
||||
(4) Only for LLVM >= 9 and not all targets compile
|
||||
(4) Only for LLVM >= 11 and not all targets compile
|
||||
|
||||
(5) upcoming, development in the branch
|
||||
|
||||
@ -137,7 +137,7 @@ afl++ has many build options.
|
||||
The easiest is to build and install everything:
|
||||
|
||||
```shell
|
||||
$ sudo apt install build-essential libtool-bin python3 automake bison libglib2.0-dev libpixman-1-dev clang python-setuptools
|
||||
$ sudo apt install build-essential libtool-bin python3-dev automake flex bison libglib2.0-dev libpixman-1-dev clang python3-setuptools llvm
|
||||
$ make distrib
|
||||
$ sudo make install
|
||||
```
|
||||
@ -160,7 +160,8 @@ These build targets exist:
|
||||
* distrib: everything (for both binary-only and source code fuzzing)
|
||||
* man: creates simple man pages from the help option of the programs
|
||||
* install: installs everything you have compiled with the build options above
|
||||
* clean: cleans everything. for qemu_mode and unicorn_mode it means it deletes all downloads as well
|
||||
* clean: cleans everything compiled, not downloads (unless not on a checkout)
|
||||
* deepclean: cleans everything including downloads
|
||||
* code-format: format the code, do this before you commit and send a PR please!
|
||||
* tests: runs test cases to ensure that all features are still working as they should
|
||||
* unit: perform unit tests (based on cmocka)
|
||||
@ -671,8 +672,9 @@ Here are some of the most important caveats for AFL:
|
||||
|
||||
To work around this, you can comment out the relevant checks (see
|
||||
examples/libpng_no_checksum/ for inspiration); if this is not possible,
|
||||
you can also write a postprocessor, as explained in
|
||||
examples/post_library/ (with AFL_POST_LIBRARY)
|
||||
you can also write a postprocessor, one of the hooks of custom mutators.
|
||||
See [docs/custom_mutators.md](docs/custom_mutators.md) on how to use
|
||||
`AFL_CUSTOM_MUTATOR_LIBRARY`
|
||||
|
||||
- There are some unfortunate trade-offs with ASAN and 64-bit binaries. This
|
||||
isn't due to any specific fault of afl-fuzz; see [docs/notes_for_asan.md](docs/notes_for_asan.md)
|
||||
|
23
TODO.md
23
TODO.md
@ -1,24 +1,22 @@
|
||||
# TODO list for AFL++
|
||||
|
||||
## Roadmap 2.65
|
||||
## Roadmap 2.65+
|
||||
|
||||
- AFL_MAP_SIZE for afl-llvm-pass, qemu_mode and unicorn_mode
|
||||
- fix stability calculation bug
|
||||
- sync_fuzzers(): only masters sync from all, slaves only sync from master
|
||||
(@andrea: be careful, often people run all slaves)
|
||||
- AFL_MAP_SIZE for qemu_mode and unicorn_mode
|
||||
- random crc32 HASH_CONST per run? because with 65536 paths we have collisions
|
||||
- namespace for targets? e.g. network
|
||||
- libradamsa as a custom module?
|
||||
- focal for travis
|
||||
|
||||
## Further down the road
|
||||
|
||||
afl-fuzz:
|
||||
- sync_fuzzers(): only masters sync from all, slaves only sync from master
|
||||
(@andrea: be careful, often people run all slaves)
|
||||
- ascii_only mode for mutation output
|
||||
- ascii_only mode for mutation output - or use a custom mutator for this?
|
||||
- setting min_len/max_len/start_offset/end_offset limits for mutation output
|
||||
|
||||
llvm_mode:
|
||||
- added context sensitive branch coverage
|
||||
- add CT cov and ngram cov to LTO and InsTrim
|
||||
- better whitelist solution for LTO
|
||||
|
||||
gcc_plugin:
|
||||
@ -26,18 +24,11 @@ gcc_plugin:
|
||||
- better instrumentation (seems to be better with gcc-9+)
|
||||
|
||||
qemu_mode:
|
||||
- update to 4.x (probably this will be skipped :( )
|
||||
- update to 5.x (if the performance bug if gone)
|
||||
- non colliding instrumentation
|
||||
- instrim for QEMU mode via static analysis (with r2pipe? or angr?)
|
||||
Idea: The static analyzer outputs a map in which each edge that must be
|
||||
skipped is marked with 1. QEMU loads it at startup in the parent process.
|
||||
- rename qemu specific envs to AFL_QEMU (AFL_ENTRYPOINT, AFL_CODE_START/END,
|
||||
AFL_COMPCOV_LEVEL?)
|
||||
- add AFL_QEMU_EXITPOINT (maybe multiple?), maybe pointless as we have
|
||||
persistent mode
|
||||
- add/implement AFL_QEMU_INST_LIBLIST and AFL_QEMU_NOINST_PROGRAM
|
||||
- add/implement AFL_QEMU_INST_REGIONS as a list of _START/_END addresses
|
||||
|
||||
custom_mutators:
|
||||
- rip what Superion is doing into custom mutators for js, php, etc.
|
||||
|
||||
|
@ -9,6 +9,51 @@ Want to stay in the loop on major new features? Join our mailing list by
|
||||
sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
|
||||
|
||||
### Version ++2.65c (release):
|
||||
- afl-fuzz:
|
||||
- AFL_MAP_SIZE was not working correctly
|
||||
- better python detection
|
||||
- an old, old bug in afl that would show negative stability in rare
|
||||
circumstances is now hopefully fixed
|
||||
- AFL_POST_LIBRARY was deprecated, use AFL_CUSTOM_MUTATOR_LIBRARY
|
||||
instead (see docs/custom_mutators.md)
|
||||
- llvm_mode:
|
||||
- afl-clang-fast/lto now do not skip single block functions. This
|
||||
behaviour can be reactivated with AFL_LLVM_SKIPSINGLEBLOCK
|
||||
- if LLVM 11 is installed the posix shm_open+mmap is used and a fixed
|
||||
address for the shared memory map is used as this increases the
|
||||
fuzzing speed
|
||||
- InsTrim now has an LTO version! :-) That is the best and fastest mode!
|
||||
- fixes to LTO mode if instrumented edges > MAP_SIZE
|
||||
- CTX and NGRAM can now be used together
|
||||
- CTX and NGRAM are now also supported in CFG/INSTRIM mode
|
||||
- AFL_LLVM_LAF_TRANSFORM_COMPARES could crash, fixed
|
||||
- added AFL_LLVM_SKIP_NEVERZERO to skip the never zero coverage counter
|
||||
implementation. For targets with few or no loops or heavily called
|
||||
functions. Gives a small performance boost.
|
||||
- qemu_mode:
|
||||
- add information on PIE/PIC load addresses for 32 bit
|
||||
- better dependency checks
|
||||
- gcc_plugin:
|
||||
- better dependency checks
|
||||
- unicorn_mode:
|
||||
- validate_crash_callback can now count non-crashing inputs as crash as well
|
||||
- better submodule handling
|
||||
- afl-showmap: fix for -Q mode
|
||||
- added examples/afl_network_proxy which allows to fuzz a target over the
|
||||
network (not fuzzing tcp/ip services but running afl-fuzz on one system
|
||||
and the target being on an embedded device)
|
||||
- added examples/afl_untracer which does a binary-only fuzzing with the
|
||||
modifications done in memory (intel32/64 and aarch64 support)
|
||||
- added examples/afl_proxy which can be easily used to fuzz and instrument
|
||||
non-standard things
|
||||
- all:
|
||||
- forkserver communication now also used for error reporting
|
||||
- fix 32 bit build options
|
||||
- make clean now leaves qemu-3.1.1.tar.xz and the unicornafl directory
|
||||
intact if in a git/svn checkout - unless "deepclean" is used
|
||||
|
||||
|
||||
### Version ++2.64c (release):
|
||||
- llvm_mode LTO mode:
|
||||
- now requires llvm11 - but compiles all targets! :)
|
||||
@ -141,7 +186,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
- AFL_PERSISTENT_HOOK callback module for persistent QEMU
|
||||
(see examples/qemu_persistent_hook)
|
||||
- added qemu_mode/README.persistent.md documentation
|
||||
- AFL_ENTRYPOINT noew has instruction granularity
|
||||
- AFL_ENTRYPOINT now has instruction granularity
|
||||
- afl-cmin is now a sh script (invoking awk) instead of bash for portability
|
||||
the original script is still present as afl-cmin.bash
|
||||
- afl-showmap: -i dir option now allows processing multiple inputs using the
|
||||
@ -2532,3 +2577,6 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
### Version 0.21b (2013-11-12):
|
||||
|
||||
- Initial public release.
|
||||
|
||||
- Added support for use of multiple custom mutators which can be specified using
|
||||
the environment variable AFL_CUSTOM_MUTATOR_LIBRARY.
|
||||
|
682
docs/README.md
682
docs/README.md
@ -1,682 +0,0 @@
|
||||
# american fuzzy lop plus plus (afl++)
|
||||
|
||||

|
||||
|
||||
Release Version: 2.60c
|
||||
|
||||
Github Version: 2.60d
|
||||
|
||||
includes all necessary/interesting changes from Google's afl 2.56b
|
||||
|
||||
|
||||
Originally developed by Michal "lcamtuf" Zalewski.
|
||||
|
||||
Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
|
||||
|
||||
afl++ is maintained by Marc "van Hauser" Heuse <mh@mh-sec.de>,
|
||||
Heiko "hexcoder-" Eißfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <mail@dmnk.co>.
|
||||
|
||||
Note that although afl now has a Google afl repository [https://github.com/Google/afl](https://github.com/Google/afl),
|
||||
it is unlikely to receive any noteable enhancements: [https://twitter.com/Dor3s/status/1154737061787660288](https://twitter.com/Dor3s/status/1154737061787660288)
|
||||
|
||||
|
||||
## The enhancements compared to the original stock afl
|
||||
|
||||
Many improvements were made over the official afl release - which did not
|
||||
get any feature improvements since November 2017.
|
||||
|
||||
Among other changes afl++ has a more performant llvm_mode, supports
|
||||
llvm up to version 11, QEMU 3.1, more speed and crashfixes for QEMU,
|
||||
better *BSD and Android support and much, much more.
|
||||
|
||||
Additionally the following features and patches have been integrated:
|
||||
|
||||
* AFLfast's power schedules by Marcel Böhme: [https://github.com/mboehme/aflfast](https://github.com/mboehme/aflfast)
|
||||
|
||||
* The new excellent MOpt mutator: [https://github.com/puppet-meteor/MOpt-AFL](https://github.com/puppet-meteor/MOpt-AFL)
|
||||
|
||||
* InsTrim, a very effective CFG llvm_mode instrumentation implementation for large targets: [https://github.com/csienslab/instrim](https://github.com/csienslab/instrim)
|
||||
|
||||
* C. Holler's afl-fuzz Python mutator module and llvm_mode whitelist support: [https://github.com/choller/afl](https://github.com/choller/afl)
|
||||
|
||||
* Custom mutator by a library (instead of Python) by kyakdan
|
||||
|
||||
* unicorn_mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk)
|
||||
|
||||
* laf-intel or CompCov support for llvm_mode, qemu_mode and unicorn_mode
|
||||
|
||||
* NeverZero patch for afl-gcc, llvm_mode, qemu_mode and unicorn_mode which prevents a wrapping map value to zero, increases coverage
|
||||
|
||||
* Persistent mode and deferred forkserver for qemu_mode
|
||||
|
||||
* Win32 PE binary-only fuzzing with QEMU and Wine
|
||||
|
||||
* Radamsa mutator (enable with `-R` to add or `-RR` to run it exclusivly).
|
||||
|
||||
* qbdi_mode: fuzz android native libraries via QBDI framework
|
||||
|
||||
|
||||
A more thorough list is available in the PATCHES file.
|
||||
|
||||
| Feature/Instrumentation | afl-gcc | llvm_mode | gcc_plugin | qemu_mode | unicorn_mode |
|
||||
| ----------------------- |:-------:|:---------:|:----------:|:---------:|:------------:|
|
||||
| laf-intel / CompCov | | x | | x86/arm | x86/arm |
|
||||
| NeverZero | x | x(1) | (2) | x | x |
|
||||
| Persistent mode | | x | x | x86 | x |
|
||||
| Whitelist | | x | x | | |
|
||||
| InsTrim | | x | | | |
|
||||
|
||||
neverZero:
|
||||
|
||||
(1) only in LLVM >= 9.0 due to a bug in llvm in previous versions
|
||||
|
||||
(2) gcc creates non-performant code, hence it is disabled in gcc_plugin
|
||||
|
||||
So all in all this is the best-of afl that is currently out there :-)
|
||||
|
||||
For new versions and additional information, check out:
|
||||
[https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
|
||||
|
||||
To compare notes with other users or get notified about major new features,
|
||||
send a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
|
||||
See [docs/QuickStartGuide.md](docs/QuickStartGuide.md) if you don't have time to
|
||||
read this file.
|
||||
|
||||
|
||||
## 0) Building and installing afl++
|
||||
|
||||
afl++ has many build options.
|
||||
The easiest is to build and install everything:
|
||||
|
||||
```shell
|
||||
$ make distrib
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
Note that "make distrib" also builds llvm_mode, qemu_mode, unicorn_mode and
|
||||
more. If you just want plain afl then do "make all", however compiling and
|
||||
using at least llvm_mode is highly recommended for much better results -
|
||||
hence in this case
|
||||
|
||||
```shell
|
||||
$ make source-only
|
||||
```
|
||||
is what you should choose.
|
||||
|
||||
These build options exist:
|
||||
|
||||
* all: just the main afl++ binaries
|
||||
* binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap, radamsa
|
||||
* source-only: everything for source code fuzzing: llvm_mode, libdislocator, libtokencap, radamsa
|
||||
* distrib: everything (for both binary-only and source code fuzzing)
|
||||
* install: installs everything you have compiled with the build options above
|
||||
* clean: cleans everything. for qemu_mode and unicorn_mode it means it deletes all downloads as well
|
||||
* code-format: format the code, do this before you commit and send a PR please!
|
||||
* tests: runs test cases to ensure that all features are still working as they should
|
||||
* help: shows these build options
|
||||
|
||||
[Unless you are on Mac OS X](https://developer.apple.com/library/archive/qa/qa1118/_index.html) you can also build statically linked versions of the
|
||||
afl++ binaries by passing the STATIC=1 argument to make:
|
||||
|
||||
```shell
|
||||
$ make all STATIC=1
|
||||
```
|
||||
|
||||
Note that afl++ is faster and better the newer the compilers used are.
|
||||
Hence gcc-9 and especially llvm-9 should be the compilers of choice.
|
||||
If your distribution does not have them, you can use the Dockerfile:
|
||||
|
||||
```shell
|
||||
$ docker build -t aflplusplus
|
||||
```
|
||||
|
||||
|
||||
## 1) Challenges of guided fuzzing
|
||||
|
||||
Fuzzing is one of the most powerful and proven strategies for identifying
|
||||
security issues in real-world software; it is responsible for the vast
|
||||
majority of remote code execution and privilege escalation bugs found to date
|
||||
in security-critical software.
|
||||
|
||||
Unfortunately, fuzzing is also relatively shallow; blind, random mutations
|
||||
make it very unlikely to reach certain code paths in the tested code, leaving
|
||||
some vulnerabilities firmly outside the reach of this technique.
|
||||
|
||||
There have been numerous attempts to solve this problem. One of the early
|
||||
approaches - pioneered by Tavis Ormandy - is corpus distillation. The method
|
||||
relies on coverage signals to select a subset of interesting seeds from a
|
||||
massive, high-quality corpus of candidate files, and then fuzz them by
|
||||
traditional means. The approach works exceptionally well, but requires such
|
||||
a corpus to be readily available. In addition, block coverage measurements
|
||||
provide only a very simplistic understanding of program state, and are less
|
||||
useful for guiding the fuzzing effort in the long haul.
|
||||
|
||||
Other, more sophisticated research has focused on techniques such as program
|
||||
flow analysis ("concolic execution"), symbolic execution, or static analysis.
|
||||
All these methods are extremely promising in experimental settings, but tend
|
||||
to suffer from reliability and performance problems in practical uses - and
|
||||
currently do not offer a viable alternative to "dumb" fuzzing techniques.
|
||||
|
||||
|
||||
## 2) The afl-fuzz approach
|
||||
|
||||
American Fuzzy Lop is a brute-force fuzzer coupled with an exceedingly simple
|
||||
but rock-solid instrumentation-guided genetic algorithm. It uses a modified
|
||||
form of edge coverage to effortlessly pick up subtle, local-scale changes to
|
||||
program control flow.
|
||||
|
||||
Simplifying a bit, the overall algorithm can be summed up as:
|
||||
|
||||
1) Load user-supplied initial test cases into the queue,
|
||||
|
||||
2) Take next input file from the queue,
|
||||
|
||||
3) Attempt to trim the test case to the smallest size that doesn't alter
|
||||
the measured behavior of the program,
|
||||
|
||||
4) Repeatedly mutate the file using a balanced and well-researched variety
|
||||
of traditional fuzzing strategies,
|
||||
|
||||
5) If any of the generated mutations resulted in a new state transition
|
||||
recorded by the instrumentation, add mutated output as a new entry in the
|
||||
queue.
|
||||
|
||||
6) Go to 2.
|
||||
|
||||
The discovered test cases are also periodically culled to eliminate ones that
|
||||
have been obsoleted by newer, higher-coverage finds; and undergo several other
|
||||
instrumentation-driven effort minimization steps.
|
||||
|
||||
As a side result of the fuzzing process, the tool creates a small,
|
||||
self-contained corpus of interesting test cases. These are extremely useful
|
||||
for seeding other, labor- or resource-intensive testing regimes - for example,
|
||||
for stress-testing browsers, office applications, graphics suites, or
|
||||
closed-source tools.
|
||||
|
||||
The fuzzer is thoroughly tested to deliver out-of-the-box performance far
|
||||
superior to blind fuzzing or coverage-only tools.
|
||||
|
||||
|
||||
## 3) Instrumenting programs for use with AFL
|
||||
|
||||
PLEASE NOTE: llvm_mode compilation with afl-clang-fast/afl-clang-fast++
|
||||
instead of afl-gcc/afl-g++ is much faster and has a few cool features.
|
||||
See llvm_mode/ - however few code does not compile with llvm.
|
||||
We support llvm versions 3.8.0 to 11.
|
||||
|
||||
When source code is available, instrumentation can be injected by a companion
|
||||
tool that works as a drop-in replacement for gcc or clang in any standard build
|
||||
process for third-party code.
|
||||
|
||||
The instrumentation has a fairly modest performance impact; in conjunction with
|
||||
other optimizations implemented by afl-fuzz, most programs can be fuzzed as fast
|
||||
or even faster than possible with traditional tools.
|
||||
|
||||
The correct way to recompile the target program may vary depending on the
|
||||
specifics of the build process, but a nearly-universal approach would be:
|
||||
|
||||
```shell
|
||||
$ CC=/path/to/afl/afl-gcc ./configure
|
||||
$ make clean all
|
||||
```
|
||||
|
||||
For C++ programs, you'd would also want to set `CXX=/path/to/afl/afl-g++`.
|
||||
|
||||
The clang wrappers (afl-clang and afl-clang++) can be used in the same way;
|
||||
clang users may also opt to leverage a higher-performance instrumentation mode,
|
||||
as described in [llvm_mode/README.md](llvm_mode/README.md).
|
||||
Clang/LLVM has a much better performance and works with LLVM version 3.8.0 to 11.
|
||||
|
||||
Using the LAF Intel performance enhancements are also recommended, see
|
||||
[llvm_mode/README.laf-intel.md](llvm_mode/README.laf-intel.md)
|
||||
|
||||
Using partial instrumentation is also recommended, see
|
||||
[llvm_mode/README.whitelist.md](llvm_mode/README.whitelist.md)
|
||||
|
||||
When testing libraries, you need to find or write a simple program that reads
|
||||
data from stdin or from a file and passes it to the tested library. In such a
|
||||
case, it is essential to link this executable against a static version of the
|
||||
instrumented library, or to make sure that the correct .so file is loaded at
|
||||
runtime (usually by setting `LD_LIBRARY_PATH`). The simplest option is a static
|
||||
build, usually possible via:
|
||||
|
||||
```shell
|
||||
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
|
||||
```
|
||||
|
||||
Setting `AFL_HARDEN=1` when calling 'make' will cause the CC wrapper to
|
||||
automatically enable code hardening options that make it easier to detect
|
||||
simple memory bugs. Libdislocator, a helper library included with AFL (see
|
||||
[libdislocator/README.md](libdislocator/README.md)) can help uncover heap corruption issues, too.
|
||||
|
||||
PS. ASAN users are advised to review [docs/notes_for_asan.md](docs/notes_for_asan.md)
|
||||
file for important caveats.
|
||||
|
||||
|
||||
## 4) Instrumenting binary-only apps
|
||||
|
||||
When source code is *NOT* available, the fuzzer offers experimental support for
|
||||
fast, on-the-fly instrumentation of black-box binaries. This is accomplished
|
||||
with a version of QEMU running in the lesser-known "user space emulation" mode.
|
||||
|
||||
QEMU is a project separate from AFL, but you can conveniently build the
|
||||
feature by doing:
|
||||
|
||||
```shell
|
||||
$ cd qemu_mode
|
||||
$ ./build_qemu_support.sh
|
||||
```
|
||||
|
||||
For additional instructions and caveats, see [qemu_mode/README.md](qemu_mode/README.md).
|
||||
|
||||
The mode is approximately 2-5x slower than compile-time instrumentation, is
|
||||
less conducive to parallelization, and may have some other quirks.
|
||||
|
||||
If [afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst) works for
|
||||
your binary, then you can use afl-fuzz normally and it will have twice
|
||||
the speed compared to qemu_mode.
|
||||
|
||||
A more comprehensive description of these and other options can be found in
|
||||
[docs/binaryonly_fuzzing.md](docs/binaryonly_fuzzing.md)
|
||||
|
||||
|
||||
## 5) Power schedules
|
||||
|
||||
The power schedules were copied from Marcel Böhme's excellent AFLfast
|
||||
implementation and expand on the ability to discover new paths and
|
||||
therefore may increase the code coverage.
|
||||
|
||||
The available schedules are:
|
||||
|
||||
- explore (default)
|
||||
- fast
|
||||
- coe
|
||||
- quad
|
||||
- lin
|
||||
- exploit
|
||||
|
||||
In parallel mode (-M/-S, several instances with shared queue), we suggest to
|
||||
run the master using the exploit schedule (-p exploit) and the slaves with a
|
||||
combination of cut-off-exponential (-p coe), exponential (-p fast; default),
|
||||
and explore (-p explore) schedules.
|
||||
|
||||
In single mode, using -p fast is usually more beneficial than the default
|
||||
explore mode.
|
||||
(We don't want to change the default behaviour of afl, so "fast" has not been
|
||||
made the default mode).
|
||||
|
||||
More details can be found in the paper published at the 23rd ACM Conference on
|
||||
Computer and Communications Security [CCS'16](https://www.sigsac.org/ccs/CCS2016/accepted-papers/)
|
||||
## 6) Choosing initial test cases
|
||||
|
||||
To operate correctly, the fuzzer requires one or more starting file that
|
||||
contains a good example of the input data normally expected by the targeted
|
||||
application. There are two basic rules:
|
||||
|
||||
- Keep the files small. Under 1 kB is ideal, although not strictly necessary.
|
||||
For a discussion of why size matters, see [perf_tips.md](docs/perf_tips.md).
|
||||
|
||||
- Use multiple test cases only if they are functionally different from
|
||||
each other. There is no point in using fifty different vacation photos
|
||||
to fuzz an image library.
|
||||
|
||||
You can find many good examples of starting files in the testcases/ subdirectory
|
||||
that comes with this tool.
|
||||
|
||||
PS. If a large corpus of data is available for screening, you may want to use
|
||||
the afl-cmin utility to identify a subset of functionally distinct files that
|
||||
exercise different code paths in the target binary.
|
||||
|
||||
|
||||
## 7) Fuzzing binaries
|
||||
|
||||
The fuzzing process itself is carried out by the afl-fuzz utility. This program
|
||||
requires a read-only directory with initial test cases, a separate place to
|
||||
store its findings, plus a path to the binary to test.
|
||||
|
||||
For target binaries that accept input directly from stdin, the usual syntax is:
|
||||
|
||||
```shell
|
||||
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
|
||||
```
|
||||
|
||||
For programs that take input from a file, use '@@' to mark the location in
|
||||
the target's command line where the input file name should be placed. The
|
||||
fuzzer will substitute this for you:
|
||||
|
||||
```shell
|
||||
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
|
||||
```
|
||||
|
||||
You can also use the -f option to have the mutated data written to a specific
|
||||
file. This is useful if the program expects a particular file extension or so.
|
||||
|
||||
Non-instrumented binaries can be fuzzed in the QEMU mode (add -Q in the command
|
||||
line) or in a traditional, blind-fuzzer mode (specify -n).
|
||||
|
||||
You can use -t and -m to override the default timeout and memory limit for the
|
||||
executed process; rare examples of targets that may need these settings touched
|
||||
include compilers and video decoders.
|
||||
|
||||
Tips for optimizing fuzzing performance are discussed in [perf_tips.md](docs/perf_tips.md).
|
||||
|
||||
Note that afl-fuzz starts by performing an array of deterministic fuzzing
|
||||
steps, which can take several days, but tend to produce neat test cases. If you
|
||||
want quick & dirty results right away - akin to zzuf and other traditional
|
||||
fuzzers - add the -d option to the command line.
|
||||
|
||||
|
||||
## 8) Interpreting output
|
||||
|
||||
See the [docs/status_screen.md](docs/status_screen.md) file for information on
|
||||
how to interpret the displayed stats and monitor the health of the process. Be
|
||||
sure to consult this file especially if any UI elements are highlighted in red.
|
||||
|
||||
The fuzzing process will continue until you press Ctrl-C. At minimum, you want
|
||||
to allow the fuzzer to complete one queue cycle, which may take anywhere from a
|
||||
couple of hours to a week or so.
|
||||
|
||||
There are three subdirectories created within the output directory and updated
|
||||
in real time:
|
||||
|
||||
- queue/ - test cases for every distinctive execution path, plus all the
|
||||
starting files given by the user. This is the synthesized corpus
|
||||
mentioned in section 2.
|
||||
|
||||
Before using this corpus for any other purposes, you can shrink
|
||||
it to a smaller size using the afl-cmin tool. The tool will find
|
||||
a smaller subset of files offering equivalent edge coverage.
|
||||
|
||||
- crashes/ - unique test cases that cause the tested program to receive a
|
||||
fatal signal (e.g., SIGSEGV, SIGILL, SIGABRT). The entries are
|
||||
grouped by the received signal.
|
||||
|
||||
- hangs/ - unique test cases that cause the tested program to time out. The
|
||||
default time limit before something is classified as a hang is
|
||||
the larger of 1 second and the value of the -t parameter.
|
||||
The value can be fine-tuned by setting AFL_HANG_TMOUT, but this
|
||||
is rarely necessary.
|
||||
|
||||
Crashes and hangs are considered "unique" if the associated execution paths
|
||||
involve any state transitions not seen in previously-recorded faults. If a
|
||||
single bug can be reached in multiple ways, there will be some count inflation
|
||||
early in the process, but this should quickly taper off.
|
||||
|
||||
The file names for crashes and hangs are correlated with parent, non-faulting
|
||||
queue entries. This should help with debugging.
|
||||
|
||||
When you can't reproduce a crash found by afl-fuzz, the most likely cause is
|
||||
that you are not setting the same memory limit as used by the tool. Try:
|
||||
|
||||
```shell
|
||||
$ LIMIT_MB=50
|
||||
$ ( ulimit -Sv $[LIMIT_MB << 10]; /path/to/tested_binary ... )
|
||||
```
|
||||
|
||||
Change LIMIT_MB to match the -m parameter passed to afl-fuzz. On OpenBSD,
|
||||
also change -Sv to -Sd.
|
||||
|
||||
Any existing output directory can be also used to resume aborted jobs; try:
|
||||
|
||||
```shell
|
||||
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
|
||||
```
|
||||
|
||||
If you have gnuplot installed, you can also generate some pretty graphs for any
|
||||
active fuzzing task using afl-plot. For an example of how this looks like,
|
||||
see [http://lcamtuf.coredump.cx/afl/plot/](http://lcamtuf.coredump.cx/afl/plot/).
|
||||
|
||||
|
||||
## 9) Parallelized fuzzing
|
||||
|
||||
Every instance of afl-fuzz takes up roughly one core. This means that on
|
||||
multi-core systems, parallelization is necessary to fully utilize the hardware.
|
||||
For tips on how to fuzz a common target on multiple cores or multiple networked
|
||||
machines, please refer to [docs/parallel_fuzzing.md](docs/parallel_fuzzing.md).
|
||||
|
||||
The parallel fuzzing mode also offers a simple way for interfacing AFL to other
|
||||
fuzzers, to symbolic or concolic execution engines, and so forth; again, see the
|
||||
last section of [docs/parallel_fuzzing.md](docs/parallel_fuzzing.md) for tips.
|
||||
|
||||
|
||||
## 10) Fuzzer dictionaries
|
||||
|
||||
By default, afl-fuzz mutation engine is optimized for compact data formats -
|
||||
say, images, multimedia, compressed data, regular expression syntax, or shell
|
||||
scripts. It is somewhat less suited for languages with particularly verbose and
|
||||
redundant verbiage - notably including HTML, SQL, or JavaScript.
|
||||
|
||||
To avoid the hassle of building syntax-aware tools, afl-fuzz provides a way to
|
||||
seed the fuzzing process with an optional dictionary of language keywords,
|
||||
magic headers, or other special tokens associated with the targeted data type
|
||||
-- and use that to reconstruct the underlying grammar on the go:
|
||||
|
||||
[http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html](http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html)
|
||||
|
||||
To use this feature, you first need to create a dictionary in one of the two
|
||||
formats discussed in [dictionaries/README.md](dictionaries/README.md);
|
||||
and then point the fuzzer to it via the -x option in the command line.
|
||||
|
||||
(Several common dictionaries are already provided in that subdirectory, too.)
|
||||
|
||||
There is no way to provide more structured descriptions of the underlying
|
||||
syntax, but the fuzzer will likely figure out some of this based on the
|
||||
instrumentation feedback alone. This actually works in practice, say:
|
||||
|
||||
[http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html](http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html)
|
||||
|
||||
PS. Even when no explicit dictionary is given, afl-fuzz will try to extract
|
||||
existing syntax tokens in the input corpus by watching the instrumentation
|
||||
very closely during deterministic byte flips. This works for some types of
|
||||
parsers and grammars, but isn't nearly as good as the -x mode.
|
||||
|
||||
If a dictionary is really hard to come by, another option is to let AFL run
|
||||
for a while, and then use the token capture library that comes as a companion
|
||||
utility with AFL. For that, see [libtokencap/README.md](libtokencap/README.tokencap.md).
|
||||
|
||||
|
||||
## 11) Crash triage
|
||||
|
||||
The coverage-based grouping of crashes usually produces a small data set that
|
||||
can be quickly triaged manually or with a very simple GDB or Valgrind script.
|
||||
Every crash is also traceable to its parent non-crashing test case in the
|
||||
queue, making it easier to diagnose faults.
|
||||
|
||||
Having said that, it's important to acknowledge that some fuzzing crashes can be
|
||||
difficult to quickly evaluate for exploitability without a lot of debugging and
|
||||
code analysis work. To assist with this task, afl-fuzz supports a very unique
|
||||
"crash exploration" mode enabled with the -C flag.
|
||||
|
||||
In this mode, the fuzzer takes one or more crashing test cases as the input,
|
||||
and uses its feedback-driven fuzzing strategies to very quickly enumerate all
|
||||
code paths that can be reached in the program while keeping it in the
|
||||
crashing state.
|
||||
|
||||
Mutations that do not result in a crash are rejected; so are any changes that
|
||||
do not affect the execution path.
|
||||
|
||||
The output is a small corpus of files that can be very rapidly examined to see
|
||||
what degree of control the attacker has over the faulting address, or whether
|
||||
it is possible to get past an initial out-of-bounds read - and see what lies
|
||||
beneath.
|
||||
|
||||
Oh, one more thing: for test case minimization, give afl-tmin a try. The tool
|
||||
can be operated in a very simple way:
|
||||
|
||||
```shell
|
||||
$ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
|
||||
```
|
||||
|
||||
The tool works with crashing and non-crashing test cases alike. In the crash
|
||||
mode, it will happily accept instrumented and non-instrumented binaries. In the
|
||||
non-crashing mode, the minimizer relies on standard AFL instrumentation to make
|
||||
the file simpler without altering the execution path.
|
||||
|
||||
The minimizer accepts the -m, -t, -f and @@ syntax in a manner compatible with
|
||||
afl-fuzz.
|
||||
|
||||
Another recent addition to AFL is the afl-analyze tool. It takes an input
|
||||
file, attempts to sequentially flip bytes, and observes the behavior of the
|
||||
tested program. It then color-codes the input based on which sections appear to
|
||||
be critical, and which are not; while not bulletproof, it can often offer quick
|
||||
insights into complex file formats. More info about its operation can be found
|
||||
near the end of [docs/technical_details.md](docs/technical_details.md).
|
||||
|
||||
|
||||
## 12) Going beyond crashes
|
||||
|
||||
Fuzzing is a wonderful and underutilized technique for discovering non-crashing
|
||||
design and implementation errors, too. Quite a few interesting bugs have been
|
||||
found by modifying the target programs to call abort() when, say:
|
||||
|
||||
- Two bignum libraries produce different outputs when given the same
|
||||
fuzzer-generated input,
|
||||
|
||||
- An image library produces different outputs when asked to decode the same
|
||||
input image several times in a row,
|
||||
|
||||
- A serialization / deserialization library fails to produce stable outputs
|
||||
when iteratively serializing and deserializing fuzzer-supplied data,
|
||||
|
||||
- A compression library produces an output inconsistent with the input file
|
||||
when asked to compress and then decompress a particular blob.
|
||||
|
||||
Implementing these or similar sanity checks usually takes very little time;
|
||||
if you are the maintainer of a particular package, you can make this code
|
||||
conditional with `#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` (a flag also
|
||||
shared with libfuzzer) or `#ifdef __AFL_COMPILER` (this one is just for AFL).
|
||||
|
||||
|
||||
## 13) Common-sense risks
|
||||
|
||||
Please keep in mind that, similarly to many other computationally-intensive
|
||||
tasks, fuzzing may put strain on your hardware and on the OS. In particular:
|
||||
|
||||
- Your CPU will run hot and will need adequate cooling. In most cases, if
|
||||
cooling is insufficient or stops working properly, CPU speeds will be
|
||||
automatically throttled. That said, especially when fuzzing on less
|
||||
suitable hardware (laptops, smartphones, etc), it's not entirely impossible
|
||||
for something to blow up.
|
||||
|
||||
- Targeted programs may end up erratically grabbing gigabytes of memory or
|
||||
filling up disk space with junk files. AFL tries to enforce basic memory
|
||||
limits, but can't prevent each and every possible mishap. The bottom line
|
||||
is that you shouldn't be fuzzing on systems where the prospect of data loss
|
||||
is not an acceptable risk.
|
||||
|
||||
- Fuzzing involves billions of reads and writes to the filesystem. On modern
|
||||
systems, this will be usually heavily cached, resulting in fairly modest
|
||||
"physical" I/O - but there are many factors that may alter this equation.
|
||||
It is your responsibility to monitor for potential trouble; with very heavy
|
||||
I/O, the lifespan of many HDDs and SSDs may be reduced.
|
||||
|
||||
A good way to monitor disk I/O on Linux is the 'iostat' command:
|
||||
|
||||
```shell
|
||||
$ iostat -d 3 -x -k [...optional disk ID...]
|
||||
```
|
||||
|
||||
|
||||
## 14) Known limitations & areas for improvement
|
||||
|
||||
Here are some of the most important caveats for AFL:
|
||||
|
||||
- AFL detects faults by checking for the first spawned process dying due to
|
||||
a signal (SIGSEGV, SIGABRT, etc). Programs that install custom handlers for
|
||||
these signals may need to have the relevant code commented out. In the same
|
||||
vein, faults in child processed spawned by the fuzzed target may evade
|
||||
detection unless you manually add some code to catch that.
|
||||
|
||||
- As with any other brute-force tool, the fuzzer offers limited coverage if
|
||||
encryption, checksums, cryptographic signatures, or compression are used to
|
||||
wholly wrap the actual data format to be tested.
|
||||
|
||||
To work around this, you can comment out the relevant checks (see
|
||||
examples/libpng_no_checksum/ for inspiration); if this is not possible,
|
||||
you can also write a postprocessor, as explained in
|
||||
examples/post_library/ (with AFL_POST_LIBRARY)
|
||||
|
||||
- There are some unfortunate trade-offs with ASAN and 64-bit binaries. This
|
||||
isn't due to any specific fault of afl-fuzz; see [docs/notes_for_asan.md](docs/notes_for_asan.md)
|
||||
for tips.
|
||||
|
||||
- There is no direct support for fuzzing network services, background
|
||||
daemons, or interactive apps that require UI interaction to work. You may
|
||||
need to make simple code changes to make them behave in a more traditional
|
||||
way. Preeny may offer a relatively simple option, too - see:
|
||||
[https://github.com/zardus/preeny](https://github.com/zardus/preeny)
|
||||
|
||||
Some useful tips for modifying network-based services can be also found at:
|
||||
[https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop](https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop)
|
||||
|
||||
- AFL doesn't output human-readable coverage data. If you want to monitor
|
||||
coverage, use afl-cov from Michael Rash: [https://github.com/mrash/afl-cov](https://github.com/mrash/afl-cov)
|
||||
|
||||
- Occasionally, sentient machines rise against their creators. If this
|
||||
happens to you, please consult [http://lcamtuf.coredump.cx/prep/](http://lcamtuf.coredump.cx/prep/).
|
||||
|
||||
Beyond this, see INSTALL for platform-specific tips.
|
||||
|
||||
|
||||
## 15) Special thanks
|
||||
|
||||
Many of the improvements to the original afl and afl++ wouldn't be possible
|
||||
without feedback, bug reports, or patches from:
|
||||
|
||||
```
|
||||
Jann Horn Hanno Boeck
|
||||
Felix Groebert Jakub Wilk
|
||||
Richard W. M. Jones Alexander Cherepanov
|
||||
Tom Ritter Hovik Manucharyan
|
||||
Sebastian Roschke Eberhard Mattes
|
||||
Padraig Brady Ben Laurie
|
||||
@dronesec Luca Barbato
|
||||
Tobias Ospelt Thomas Jarosch
|
||||
Martin Carpenter Mudge Zatko
|
||||
Joe Zbiciak Ryan Govostes
|
||||
Michael Rash William Robinet
|
||||
Jonathan Gray Filipe Cabecinhas
|
||||
Nico Weber Jodie Cunningham
|
||||
Andrew Griffiths Parker Thompson
|
||||
Jonathan Neuschaefer Tyler Nighswander
|
||||
Ben Nagy Samir Aguiar
|
||||
Aidan Thornton Aleksandar Nikolich
|
||||
Sam Hakim Laszlo Szekeres
|
||||
David A. Wheeler Turo Lamminen
|
||||
Andreas Stieger Richard Godbee
|
||||
Louis Dassy teor2345
|
||||
Alex Moneger Dmitry Vyukov
|
||||
Keegan McAllister Kostya Serebryany
|
||||
Richo Healey Martijn Bogaard
|
||||
rc0r Jonathan Foote
|
||||
Christian Holler Dominique Pelle
|
||||
Jacek Wielemborek Leo Barnes
|
||||
Jeremy Barnes Jeff Trull
|
||||
Guillaume Endignoux ilovezfs
|
||||
Daniel Godas-Lopez Franjo Ivancic
|
||||
Austin Seipp Daniel Komaromy
|
||||
Daniel Binderman Jonathan Metzman
|
||||
Vegard Nossum Jan Kneschke
|
||||
Kurt Roeckx Marcel Boehme
|
||||
Van-Thuan Pham Abhik Roychoudhury
|
||||
Joshua J. Drake Toby Hutton
|
||||
Rene Freingruber Sergey Davidoff
|
||||
Sami Liedes Craig Young
|
||||
Andrzej Jackowski Daniel Hodson
|
||||
Nathan Voss Dominik Maier
|
||||
Andrea Biondo Vincent Le Garrec
|
||||
Khaled Yakdan Kuang-che Wu
|
||||
```
|
||||
|
||||
Thank you!
|
||||
|
||||
|
||||
## 16) Contact
|
||||
|
||||
Questions? Concerns? Bug reports? The contributors can be reached via
|
||||
[https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
|
||||
|
||||
There is also a mailing list for the afl project; to join, send a mail to
|
||||
<afl-users+subscribe@googlegroups.com>. Or, if you prefer to browse
|
||||
archives first, try: [https://groups.google.com/group/afl-users](https://groups.google.com/group/afl-users)
|
1
docs/README.md
Symbolic link
1
docs/README.md
Symbolic link
@ -0,0 +1 @@
|
||||
../README.md
|
@ -16,6 +16,10 @@ fuzzing by using libraries that perform mutations according to a given grammar.
|
||||
|
||||
The custom mutator is passed to `afl-fuzz` via the `AFL_CUSTOM_MUTATOR_LIBRARY`
|
||||
or `AFL_PYTHON_MODULE` environment variable, and must export a fuzz function.
|
||||
Now afl also supports multiple custom mutators which can be specified in the same `AFL_CUSTOM_MUTATOR_LIBRARY` environment variable like this.
|
||||
```bash
|
||||
export AFL_CUSTOM_MUTATOR_LIBRARY="full/path/to/mutator_first.so;full/path/to/mutator_second.so"
|
||||
```
|
||||
Please see [APIs](#2-apis) and [Usage](#3-usage) for detail.
|
||||
|
||||
The custom mutation stage is set to be the first non-deterministic stage (right before the havoc stage).
|
||||
@ -29,13 +33,14 @@ C/C++:
|
||||
```c
|
||||
void *afl_custom_init(afl_t *afl, unsigned int seed);
|
||||
size_t afl_custom_fuzz(void *data, uint8_t *buf, size_t buf_size, u8 **out_buf, uint8_t *add_buf, size_t add_buf_size, size_t max_size);
|
||||
size_t afl_custom_pre_save(void *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf);
|
||||
size_t afl_custom_post_process(void *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf);
|
||||
int32_t afl_custom_init_trim(void *data, uint8_t *buf, size_t buf_size);
|
||||
size_t afl_custom_trim(void *data, uint8_t **out_buf);
|
||||
int32_t afl_custom_post_trim(void *data, int success) {
|
||||
size_t afl_custom_havoc_mutation(void *data, u8 *buf, size_t buf_size, u8 **out_buf, size_t max_size);
|
||||
uint8_t afl_custom_havoc_mutation_probability(void *data);
|
||||
uint8_t afl_custom_queue_get(void *data, const uint8_t *filename); void afl_custom_queue_new_entry(void *data, const uint8_t *filename_new_queue, const uint8_t *filename_orig_queue);
|
||||
uint8_t afl_custom_queue_get(void *data, const uint8_t *filename);
|
||||
void afl_custom_queue_new_entry(void *data, const uint8_t *filename_new_queue, const uint8_t *filename_orig_queue);
|
||||
void afl_custom_deinit(void *data);
|
||||
```
|
||||
|
||||
@ -47,7 +52,7 @@ def init(seed):
|
||||
def fuzz(buf, add_buf, max_size):
|
||||
return mutated_out
|
||||
|
||||
def pre_save(buf):
|
||||
def post_process(buf):
|
||||
return out_buf
|
||||
|
||||
def init_trim(buf):
|
||||
@ -80,13 +85,16 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
|
||||
|
||||
- `queue_get` (optional):
|
||||
|
||||
This method determines whether the fuzzer should fuzz the current queue
|
||||
entry or not
|
||||
This method determines whether the custom fuzzer should fuzz the current
|
||||
queue entry or not
|
||||
|
||||
- `fuzz` (required):
|
||||
- `fuzz` (optional):
|
||||
|
||||
This method performs custom mutations on a given input. It also accepts an
|
||||
additional test case.
|
||||
Note that this function is optional - but it makes sense to use it.
|
||||
You would only skip this if `post_process` is used to fix checksums etc.
|
||||
so you are using it e.g. as a post processing library.
|
||||
|
||||
- `havoc_mutation` and `havoc_mutation_probability` (optional):
|
||||
|
||||
@ -95,7 +103,7 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
|
||||
`havoc_mutation_probability`, returns the probability that `havoc_mutation`
|
||||
is called in havoc. By default, it is 6%.
|
||||
|
||||
- `pre_save` (optional):
|
||||
- `post_process` (optional):
|
||||
|
||||
For some cases, the format of the mutated data returned from the custom
|
||||
mutator is not suitable to directly execute the target with this input.
|
||||
@ -103,13 +111,20 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
|
||||
protobuf format which corresponds to a given grammar. In order to execute
|
||||
the target, the protobuf data must be converted to the plain-text format
|
||||
expected by the target. In such scenarios, the user can define the
|
||||
`pre_save` function. This function is then transforms the data into the
|
||||
`post_process` function. This function is then transforming the data into the
|
||||
format expected by the API before executing the target.
|
||||
|
||||
- `queue_new_entry` (optional):
|
||||
|
||||
This methods is called after adding a new test case to the queue.
|
||||
|
||||
- `deinit`:
|
||||
|
||||
The last method to be called, deinitializing the state.
|
||||
|
||||
Note that there are also three functions for trimming as described in the
|
||||
next section.
|
||||
|
||||
### Trimming Support
|
||||
|
||||
The generic trimming routines implemented in AFL++ can easily destroy the
|
||||
@ -156,10 +171,8 @@ trimmed input. Here's a quick API description:
|
||||
In any case, this method must return the next trim iteration index (from 0
|
||||
to the maximum amount of steps you returned in `init_trim`).
|
||||
|
||||
`deinit` the last method to be called, deinitializing the state.
|
||||
|
||||
Omitting any of three methods will cause the trimming to be disabled and trigger
|
||||
a fallback to the builtin default trimming routine.
|
||||
Omitting any of three trimming methods will cause the trimming to be disabled
|
||||
and trigger a fallback to the builtin default trimming routine.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
@ -209,12 +222,15 @@ For C/C++ mutator, the source code must be compiled as a shared object:
|
||||
```bash
|
||||
gcc -shared -Wall -O3 example.c -o example.so
|
||||
```
|
||||
Note that if you specify multiple custom mutators, the corresponding functions will
|
||||
be called in the order in which they are specified. e.g first `post_process` function of
|
||||
`example_first.so` will be called and then that of `example_second.so`
|
||||
|
||||
### Run
|
||||
|
||||
C/C++
|
||||
```bash
|
||||
export AFL_CUSTOM_MUTATOR_LIBRARY=/full/path/to/example.so
|
||||
export AFL_CUSTOM_MUTATOR_LIBRARY="/full/path/to/example_first.so;/full/path/to/example_second.so"
|
||||
afl-fuzz /path/to/program
|
||||
```
|
||||
|
||||
|
@ -83,6 +83,10 @@ tools make fairly broad use of environmental variables:
|
||||
The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset
|
||||
of the settings discussed in section #1, with the exception of:
|
||||
|
||||
- Setting AFL_LLVM_SKIPSINGLEBLOCK=1 will skip instrumenting
|
||||
functions with a single basic block. This is useful for most C and
|
||||
some C++ targets. This works for all instrumentation modes.
|
||||
|
||||
- AFL_AS, since this toolchain does not directly invoke GNU as.
|
||||
|
||||
- TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are
|
||||
@ -97,12 +101,15 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
|
||||
- AFL_LLVM_INSTRUMENT - this configures the instrumentation mode.
|
||||
Available options:
|
||||
DEFAULT - classic AFL (map[cur_loc ^ prev_loc >> 1]++)
|
||||
CLASSIC - classic AFL (map[cur_loc ^ prev_loc >> 1]++) (default)
|
||||
CFG - InsTrim instrumentation (see below)
|
||||
LTO - LTO instrumentation (see below)
|
||||
CTX - context sensitive instrumentation (see below)
|
||||
NGRAM-x - deeper previous location coverage (from NGRAM-2 up to NGRAM-16)
|
||||
Only one can be used.
|
||||
In CLASSIC (default) and CFG/INSTRIM you can also specify CTX and/or
|
||||
NGRAM, seperate the options with a comma "," then, e.g.:
|
||||
AFL_LLVM_INSTRUMENT=CFG,CTX,NGRAM-4
|
||||
Not that this is a good idea to use both CTX and NGRAM :)
|
||||
|
||||
### LTO
|
||||
|
||||
@ -113,6 +120,9 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
afl-clang-lto/afl-clang-lto++ instead of afl-clang-fast, but is only
|
||||
built if LLVM 11 or newer is used.
|
||||
|
||||
- AFL_LLVM_INSTRUMENT=CFG will use Control Flow Graph instrumentation.
|
||||
(recommended)
|
||||
|
||||
- AFL_LLVM_LTO_AUTODICTIONARY will generate a dictionary in the target
|
||||
binary based on string compare and memory compare functions.
|
||||
afl-fuzz will automatically get these transmitted when starting to
|
||||
@ -123,6 +133,10 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
These are used if several seperated instrumentation are performed which
|
||||
are then later combined.
|
||||
|
||||
- AFL_LLVM_MAP_ADDR sets the fixed map address to a different address than
|
||||
the default 0x10000. A value of 0 or empty sets the map address to be
|
||||
dynamic (the original afl way, which is slower)
|
||||
- AFL_LLVM_MAP_DYNAMIC sets the shared memory address to be dynamic
|
||||
- AFL_LLVM_LTO_STARTID sets the starting location ID for the instrumentation.
|
||||
This defaults to 1
|
||||
- AFL_LLVM_LTO_DONTWRITEID prevents that the highest location ID written
|
||||
@ -132,7 +146,13 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
|
||||
### INSTRIM
|
||||
|
||||
This feature increases the speed by ~15% without any disadvantages.
|
||||
This feature increases the speed by ~15% without any disadvantages to the
|
||||
classic instrumentation.
|
||||
|
||||
Note that there is also an LTO version (if you have llvm 11 or higher) -
|
||||
that is the best instrumentation we have. Use `afl-clang-lto` to activate.
|
||||
The InsTrim LTO version additionally has all the options and features of
|
||||
LTO (see above).
|
||||
|
||||
- Setting AFL_LLVM_INSTRIM or AFL_LLVM_INSTRUMENT=CFG to activates this mode
|
||||
|
||||
@ -140,10 +160,6 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
afl-fuzz will only be able to see the path the loop took, but not how
|
||||
many times it was called (unless it is a complex loop).
|
||||
|
||||
- Setting AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK=1 will skip instrumenting
|
||||
functions with a single basic block. This is useful for most C and
|
||||
some C++ targets.
|
||||
|
||||
See llvm_mode/README.instrim.md
|
||||
|
||||
### NGRAM
|
||||
@ -200,6 +216,10 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
slowdown due a performance issue that is only fixed in llvm 9+.
|
||||
This feature increases path discovery by a little bit.
|
||||
|
||||
- Setting AFL_LLVM_SKIP_NEVERZERO=1 will not implement the skip zero
|
||||
test. If the target performs only few loops then this will give a
|
||||
small performance boost.
|
||||
|
||||
See llvm_mode/README.neverzero.md
|
||||
|
||||
### CMPLOG
|
||||
@ -290,9 +310,8 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
else. This makes the "own finds" counter in the UI more accurate.
|
||||
Beyond counter aesthetics, not much else should change.
|
||||
|
||||
- Setting AFL_POST_LIBRARY allows you to configure a postprocessor for
|
||||
mutated files - say, to fix up checksums. See examples/post_library/
|
||||
for more.
|
||||
- Note that AFL_POST_LIBRARY is deprecated, use AFL_CUSTOM_MUTATOR_LIBRARY
|
||||
instead (see below).
|
||||
|
||||
- Setting AFL_CUSTOM_MUTATOR_LIBRARY to a shared library with
|
||||
afl_custom_fuzz() creates additional mutations through this library.
|
||||
|
@ -56,13 +56,6 @@ functionality is now available as the "persistent" feature described in
|
||||
|
||||
http://llvm.org/docs/LibFuzzer.html
|
||||
|
||||
## AFL fixup shim (Ben Nagy)
|
||||
|
||||
Allows AFL_POST_LIBRARY postprocessors to be written in arbitrary languages
|
||||
that don't have C / .so bindings. Includes examples in Go.
|
||||
|
||||
https://github.com/bnagy/aflfix
|
||||
|
||||
## TriforceAFL (Tim Newsham and Jesse Hertz)
|
||||
|
||||
Leverages QEMU full system emulation mode to allow AFL to target operating
|
||||
|
43
examples/afl_network_proxy/GNUmakefile
Normal file
43
examples/afl_network_proxy/GNUmakefile
Normal file
@ -0,0 +1,43 @@
|
||||
PREFIX ?= /usr/local
|
||||
BIN_PATH = $(PREFIX)/bin
|
||||
DOC_PATH = $(PREFIX)/share/doc/afl
|
||||
|
||||
PROGRAMS = afl-network-client afl-network-server
|
||||
|
||||
HASH=\#
|
||||
|
||||
CFLAGS += -Wno-pointer-sign
|
||||
|
||||
ifdef STATIC
|
||||
CFLAGS += -static
|
||||
endif
|
||||
|
||||
ifeq "$(shell echo '$(HASH)include <libdeflate.h>@int main() { struct libdeflate_compressor *d = libdeflate_alloc_compressor(1); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 -ldeflate 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1"
|
||||
CFLAGS += -DUSE_DEFLATE=1
|
||||
LDFLAGS += -ldeflate
|
||||
$(info libdeflate-dev was detected, using compression)
|
||||
else
|
||||
$(warn did not find libdeflate-dev, cannot use compression)
|
||||
endif
|
||||
|
||||
all: $(PROGRAMS)
|
||||
|
||||
help:
|
||||
@echo make options:
|
||||
@echo STATIC - build as static binaries
|
||||
@echo COMPRESS_TESTCASES - compress test cases
|
||||
|
||||
afl-network-client: afl-network-client.c
|
||||
$(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS)
|
||||
|
||||
afl-network-server: afl-network-server.c
|
||||
$(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(PROGRAMS) *~ core
|
||||
|
||||
install: all
|
||||
install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(DOC_PATH)
|
||||
install -m 755 $(PROGRAMS) $${DESTDIR}$(BIN_PATH)
|
||||
install -T -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.network_proxy.md
|
||||
|
2
examples/afl_network_proxy/Makefile
Normal file
2
examples/afl_network_proxy/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
all:
|
||||
@echo please use GNU make, thanks!
|
61
examples/afl_network_proxy/README.md
Normal file
61
examples/afl_network_proxy/README.md
Normal file
@ -0,0 +1,61 @@
|
||||
# afl-network-proxy
|
||||
|
||||
If you want to run afl-fuzz over the network than this is what you need :)
|
||||
Note that the impact on fuzzing speed will be huge, expect a loss of 90%.
|
||||
|
||||
## When to use this
|
||||
|
||||
1. when you have to fuzz a target that has to run on a system that cannot
|
||||
contain the fuzzing output (e.g. /tmp too small and file system is read-only)
|
||||
2. when the target instantly reboots on crashes
|
||||
3. ... any other reason you would need this
|
||||
|
||||
## how to get it running
|
||||
|
||||
### Compiling
|
||||
|
||||
Just type `make` and let the autodetection do everything for you.
|
||||
|
||||
Note that you will get a 40-50% performance increase if you have libdeflate-dev
|
||||
installed. The GNUmakefile will autodetect it if present.
|
||||
|
||||
If your target has large test cases (10+kb) that are ascii only or large chunks
|
||||
of zero blocks then set `CFLAGS=-DCOMPRESS_TESTCASES=1` to compress them.
|
||||
For most targets this hurts performance though so it is disabled by default.
|
||||
|
||||
### on the target
|
||||
|
||||
Run `afl-network-server` with your target with the -m and -t values you need.
|
||||
Important is the -i parameter which is the TCP port to listen on.
|
||||
e.g.:
|
||||
```
|
||||
$ afl-network-server -i 1111 -m 25M -t 1000 -- /bin/target -f @@
|
||||
```
|
||||
|
||||
### on the (afl-fuzz) master
|
||||
|
||||
Just run afl-fuzz with your normal options, however the target should be
|
||||
`afl-network-client` with the IP and PORT of the `afl-network-server` and
|
||||
increase the -t value:
|
||||
```
|
||||
$ afl-fuzz -i in -o out -t 2000+ -- afl-network-client TARGET-IP 1111
|
||||
```
|
||||
Note the '+' on the -t parameter value. The afl-network-server will take
|
||||
care of proper timeouts hence afl-fuzz should not. The '+' increases the
|
||||
timeout and the value itself should be 500-1000 higher than the one on
|
||||
afl-network-server.
|
||||
|
||||
### networking
|
||||
|
||||
The TARGET can be an IPv4 or IPv6 address, or a host name that resolves to
|
||||
either. Note that also the outgoing interface can be specified with a '%' for
|
||||
`afl-network-client`, e.g. `fe80::1234%eth0`.
|
||||
|
||||
Also make sure your default TCP window size is larger than your MAP_SIZE
|
||||
(130kb is a good value).
|
||||
On Linux that is the middle value of `/proc/sys/net/ipv4/tcp_rmem`
|
||||
|
||||
## how to compile and install
|
||||
|
||||
`make && sudo make install`
|
||||
|
413
examples/afl_network_proxy/afl-network-client.c
Normal file
413
examples/afl_network_proxy/afl-network-client.c
Normal file
@ -0,0 +1,413 @@
|
||||
/*
|
||||
american fuzzy lop++ - afl-network-client
|
||||
---------------------------------------
|
||||
|
||||
Written by Marc Heuse <mh@mh-sec.de>
|
||||
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef USE_DEFLATE
|
||||
#include <libdeflate.h>
|
||||
#endif
|
||||
|
||||
u8 *__afl_area_ptr;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
u32 __afl_map_size = MAP_SIZE;
|
||||
#else
|
||||
__thread u32 __afl_map_size = MAP_SIZE;
|
||||
#endif
|
||||
|
||||
/* Error reporting to forkserver controller */
|
||||
|
||||
void send_forkserver_error(int error) {
|
||||
|
||||
u32 status;
|
||||
if (!error || error > 0xffff) return;
|
||||
status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error));
|
||||
if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return;
|
||||
|
||||
}
|
||||
|
||||
/* SHM setup. */
|
||||
|
||||
static void __afl_map_shm(void) {
|
||||
|
||||
char *id_str = getenv(SHM_ENV_VAR);
|
||||
char *ptr;
|
||||
|
||||
if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) {
|
||||
|
||||
u32 val = atoi(ptr);
|
||||
if (val > 0) __afl_map_size = val;
|
||||
|
||||
}
|
||||
|
||||
if (__afl_map_size > MAP_SIZE) {
|
||||
|
||||
if (__afl_map_size > FS_OPT_MAX_MAPSIZE) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to "
|
||||
"be able to run this instrumented program!\n",
|
||||
__afl_map_size);
|
||||
if (id_str) {
|
||||
|
||||
send_forkserver_error(FS_ERROR_MAP_SIZE);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fprintf(stderr,
|
||||
"Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to "
|
||||
"be able to run this instrumented program!\n",
|
||||
__afl_map_size);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (id_str) {
|
||||
|
||||
#ifdef USEMMAP
|
||||
const char * shm_file_path = id_str;
|
||||
int shm_fd = -1;
|
||||
unsigned char *shm_base = NULL;
|
||||
|
||||
/* create the shared memory segment as if it was a file */
|
||||
shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
|
||||
if (shm_fd == -1) {
|
||||
|
||||
fprintf(stderr, "shm_open() failed\n");
|
||||
send_forkserver_error(FS_ERROR_SHM_OPEN);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* map the shared memory segment to the address space of the process */
|
||||
shm_base =
|
||||
mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
||||
|
||||
if (shm_base == MAP_FAILED) {
|
||||
|
||||
close(shm_fd);
|
||||
shm_fd = -1;
|
||||
|
||||
fprintf(stderr, "mmap() failed\n");
|
||||
send_forkserver_error(FS_ERROR_MMAP);
|
||||
exit(2);
|
||||
|
||||
}
|
||||
|
||||
__afl_area_ptr = shm_base;
|
||||
#else
|
||||
u32 shm_id = atoi(id_str);
|
||||
|
||||
__afl_area_ptr = shmat(shm_id, 0, 0);
|
||||
|
||||
#endif
|
||||
|
||||
if (__afl_area_ptr == (void *)-1) {
|
||||
|
||||
send_forkserver_error(FS_ERROR_SHMAT);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* Write something into the bitmap so that the parent doesn't give up */
|
||||
|
||||
__afl_area_ptr[0] = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Fork server logic. */
|
||||
|
||||
static void __afl_start_forkserver(void) {
|
||||
|
||||
u8 tmp[4] = {0, 0, 0, 0};
|
||||
u32 status = 0;
|
||||
|
||||
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
|
||||
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
|
||||
if (status) status |= (FS_OPT_ENABLED);
|
||||
memcpy(tmp, &status, 4);
|
||||
|
||||
/* Phone home and tell the parent that we're OK. */
|
||||
|
||||
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
|
||||
|
||||
}
|
||||
|
||||
static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
|
||||
|
||||
s32 status, res = 0x0fffffff; // res is a dummy pid
|
||||
|
||||
/* Wait for parent by reading from the pipe. Abort if read fails. */
|
||||
if (read(FORKSRV_FD, &status, 4) != 4) return 0;
|
||||
|
||||
/* we have a testcase - read it */
|
||||
status = read(0, buf, max_len);
|
||||
|
||||
/* report that we are starting the target */
|
||||
if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0;
|
||||
|
||||
if (status < 1)
|
||||
return 0;
|
||||
else
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static void __afl_end_testcase(int status) {
|
||||
|
||||
if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* you just need to modify the while() loop in this main() */
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
u8 * interface, *buf, *ptr;
|
||||
s32 s = -1;
|
||||
struct addrinfo hints, *hres, *aip;
|
||||
u32 * lenptr, max_len = 65536;
|
||||
#ifdef USE_DEFLATE
|
||||
u8 * buf2;
|
||||
u32 * lenptr1, *lenptr2, buf2_len, compress_len;
|
||||
size_t decompress_len;
|
||||
#endif
|
||||
|
||||
if (argc < 3 || argc > 4) {
|
||||
|
||||
printf("Syntax: %s host port [max-input-size]\n\n", argv[0]);
|
||||
printf("Requires host and port of the remote afl-proxy-server instance.\n");
|
||||
printf(
|
||||
"IPv4 and IPv6 are supported, also binding to an interface with "
|
||||
"\"%%\"\n");
|
||||
printf("The max-input-size default is %u.\n", max_len);
|
||||
printf(
|
||||
"The default map size is %u and can be changed with setting "
|
||||
"AFL_MAP_SIZE.\n",
|
||||
__afl_map_size);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
if ((interface = index(argv[1], '%')) != NULL) *interface++ = 0;
|
||||
|
||||
if (argc > 3)
|
||||
if ((max_len = atoi(argv[3])) < 0)
|
||||
FATAL("max-input-size may not be negative or larger than 2GB: %s",
|
||||
argv[3]);
|
||||
|
||||
if ((ptr = getenv("AFL_MAP_SIZE")) != NULL)
|
||||
if ((__afl_map_size = atoi(ptr)) < 8)
|
||||
FATAL("illegal map size, may not be < 8 or >= 2^30: %s", ptr);
|
||||
|
||||
if ((buf = malloc(max_len + 4)) == NULL)
|
||||
PFATAL("can not allocate %u memory", max_len + 4);
|
||||
lenptr = (u32 *)buf;
|
||||
|
||||
#ifdef USE_DEFLATE
|
||||
buf2_len = (max_len > __afl_map_size ? max_len : __afl_map_size);
|
||||
if ((buf2 = malloc(buf2_len + 8)) == NULL)
|
||||
PFATAL("can not allocate %u memory", buf2_len + 8);
|
||||
lenptr1 = (u32 *)buf2;
|
||||
lenptr2 = (u32 *)(buf2 + 4);
|
||||
#endif
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
|
||||
if (getaddrinfo(argv[1], argv[2], &hints, &hres) != 0)
|
||||
PFATAL("could not resolve target %s", argv[1]);
|
||||
|
||||
for (aip = hres; aip != NULL && s == -1; aip = aip->ai_next) {
|
||||
|
||||
if ((s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol)) >= 0) {
|
||||
|
||||
#ifdef SO_BINDTODEVICE
|
||||
if (interface != NULL)
|
||||
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, interface,
|
||||
strlen(interface) + 1) < 0)
|
||||
fprintf(stderr, "Warning: could not bind to device %s\n", interface);
|
||||
#else
|
||||
fprintf(stderr,
|
||||
"Warning: binding to interface is not supported for your OS\n");
|
||||
#endif
|
||||
|
||||
#ifdef SO_PRIORITY
|
||||
int priority = 7;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) <
|
||||
0) {
|
||||
|
||||
priority = 6;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority,
|
||||
sizeof(priority)) < 0)
|
||||
WARNF("could not set priority on socket");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (connect(s, aip->ai_addr, aip->ai_addrlen) == -1) s = -1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef USE_DEFLATE
|
||||
struct libdeflate_compressor *compressor;
|
||||
compressor = libdeflate_alloc_compressor(1);
|
||||
struct libdeflate_decompressor *decompressor;
|
||||
decompressor = libdeflate_alloc_decompressor();
|
||||
fprintf(stderr, "Compiled with compression support\n");
|
||||
#endif
|
||||
|
||||
if (s == -1)
|
||||
FATAL("could not connect to target tcp://%s:%s", argv[1], argv[2]);
|
||||
else
|
||||
fprintf(stderr, "Connected to target tcp://%s:%s\n", argv[1], argv[2]);
|
||||
|
||||
/* we initialize the shared memory map and start the forkserver */
|
||||
__afl_map_shm();
|
||||
__afl_start_forkserver();
|
||||
|
||||
int i = 1, j, status, ret, received;
|
||||
|
||||
// fprintf(stderr, "Waiting for first testcase\n");
|
||||
while ((*lenptr = __afl_next_testcase(buf + 4, max_len)) > 0) {
|
||||
|
||||
// fprintf(stderr, "Sending testcase with len %u\n", *lenptr);
|
||||
#ifdef USE_DEFLATE
|
||||
#ifdef COMPRESS_TESTCASES
|
||||
// we only compress the testcase if it does not fit in the TCP packet
|
||||
if (*lenptr > 1500 - 20 - 32 - 4) {
|
||||
|
||||
// set highest byte to signify compression
|
||||
*lenptr1 = (*lenptr | 0xff000000);
|
||||
*lenptr2 = (u32)libdeflate_deflate_compress(compressor, buf + 4, *lenptr,
|
||||
buf2 + 8, buf2_len);
|
||||
if (send(s, buf2, *lenptr2 + 8, 0) != *lenptr2 + 8)
|
||||
PFATAL("sending test data failed");
|
||||
// fprintf(stderr, "COMPRESS (%u->%u):\n", *lenptr, *lenptr2);
|
||||
// for (u32 i = 0; i < *lenptr; i++)
|
||||
// fprintf(stderr, "%02x", buf[i + 4]);
|
||||
// fprintf(stderr, "\n");
|
||||
// for (u32 i = 0; i < *lenptr2; i++)
|
||||
// fprintf(stderr, "%02x", buf2[i + 8]);
|
||||
// fprintf(stderr, "\n");
|
||||
|
||||
} else {
|
||||
|
||||
#endif
|
||||
#endif
|
||||
if (send(s, buf, *lenptr + 4, 0) != *lenptr + 4)
|
||||
PFATAL("sending test data failed");
|
||||
#ifdef USE_DEFLATE
|
||||
#ifdef COMPRESS_TESTCASES
|
||||
// fprintf(stderr, "unCOMPRESS (%u)\n", *lenptr);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
received = 0;
|
||||
while (received < 4 &&
|
||||
(ret = recv(s, &status + received, 4 - received, 0)) > 0)
|
||||
received += ret;
|
||||
if (received != 4)
|
||||
FATAL("did not receive waitpid data (%d, %d)", received, ret);
|
||||
// fprintf(stderr, "Received status\n");
|
||||
|
||||
received = 0;
|
||||
#ifdef USE_DEFLATE
|
||||
while (received < 4 &&
|
||||
(ret = recv(s, &compress_len + received, 4 - received, 0)) > 0)
|
||||
received += ret;
|
||||
if (received != 4)
|
||||
FATAL("did not receive compress_len (%d, %d)", received, ret);
|
||||
// fprintf(stderr, "Received status\n");
|
||||
|
||||
received = 0;
|
||||
while (received < compress_len &&
|
||||
(ret = recv(s, buf2 + received, buf2_len - received, 0)) > 0)
|
||||
received += ret;
|
||||
if (received != compress_len)
|
||||
FATAL("did not receive coverage data (%d, %d)", received, ret);
|
||||
|
||||
if (libdeflate_deflate_decompress(decompressor, buf2, compress_len,
|
||||
__afl_area_ptr, __afl_map_size,
|
||||
&decompress_len) != LIBDEFLATE_SUCCESS ||
|
||||
decompress_len != __afl_map_size)
|
||||
FATAL("decompression failed");
|
||||
// fprintf(stderr, "DECOMPRESS (%u->%u): ", compress_len, decompress_len);
|
||||
// for (u32 i = 0; i < __afl_map_size; i++) fprintf(stderr, "%02x",
|
||||
// __afl_area_ptr[i]); fprintf(stderr, "\n");
|
||||
#else
|
||||
while (received < __afl_map_size &&
|
||||
(ret = recv(s, __afl_area_ptr + received, __afl_map_size - received,
|
||||
0)) > 0)
|
||||
received += ret;
|
||||
if (received != __afl_map_size)
|
||||
FATAL("did not receive coverage data (%d, %d)", received, ret);
|
||||
#endif
|
||||
// fprintf(stderr, "Received coverage\n");
|
||||
|
||||
/* report the test case is done and wait for the next */
|
||||
__afl_end_testcase(status);
|
||||
// fprintf(stderr, "Waiting for next testcase %d\n", ++i);
|
||||
|
||||
}
|
||||
|
||||
#ifdef USE_DEFLATE
|
||||
libdeflate_free_compressor(compressor);
|
||||
libdeflate_free_decompressor(decompressor);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
712
examples/afl_network_proxy/afl-network-server.c
Normal file
712
examples/afl_network_proxy/afl-network-server.c
Normal file
@ -0,0 +1,712 @@
|
||||
/*
|
||||
american fuzzy lop++ - network proxy server
|
||||
-------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com> and
|
||||
Dominik Maier <mail@dmnk.co>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
*/
|
||||
|
||||
#define AFL_MAIN
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
#include "hash.h"
|
||||
#include "forkserver.h"
|
||||
#include "sharedmem.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#ifdef USE_DEFLATE
|
||||
#include <libdeflate.h>
|
||||
struct libdeflate_compressor * compressor;
|
||||
struct libdeflate_decompressor *decompressor;
|
||||
#endif
|
||||
|
||||
static u8 *in_file, /* Minimizer input test case */
|
||||
*out_file;
|
||||
|
||||
static u8 *in_data; /* Input data for trimming */
|
||||
static u8 *buf2;
|
||||
|
||||
static s32 in_len;
|
||||
static u32 map_size = MAP_SIZE;
|
||||
static size_t buf2_len;
|
||||
|
||||
static volatile u8 stop_soon; /* Ctrl-C pressed? */
|
||||
|
||||
/* See if any bytes are set in the bitmap. */
|
||||
|
||||
static inline u8 anything_set(afl_forkserver_t *fsrv) {
|
||||
|
||||
u32 *ptr = (u32 *)fsrv->trace_bits;
|
||||
u32 i = (map_size >> 2);
|
||||
|
||||
while (i--) {
|
||||
|
||||
if (*(ptr++)) { return 1; }
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void at_exit_handler(void) {
|
||||
|
||||
afl_fsrv_killall();
|
||||
|
||||
}
|
||||
|
||||
/* Write output file. */
|
||||
|
||||
static s32 write_to_file(u8 *path, u8 *mem, u32 len) {
|
||||
|
||||
s32 ret;
|
||||
|
||||
unlink(path); /* Ignore errors */
|
||||
|
||||
ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
if (ret < 0) { PFATAL("Unable to create '%s'", path); }
|
||||
|
||||
ck_write(ret, mem, len, path);
|
||||
|
||||
lseek(ret, 0, SEEK_SET);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Execute target application. Returns 0 if the changes are a dud, or
|
||||
1 if they should be kept. */
|
||||
|
||||
static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len,
|
||||
u8 first_run) {
|
||||
|
||||
afl_fsrv_write_to_testcase(fsrv, mem, len);
|
||||
|
||||
fsrv_run_result_t ret =
|
||||
afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon);
|
||||
|
||||
if (ret == FSRV_RUN_ERROR) { FATAL("Couldn't run child"); }
|
||||
|
||||
if (stop_soon) {
|
||||
|
||||
SAYF(cRST cLRD "\n+++ aborted by user +++\n" cRST);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Handle Ctrl-C and the like. */
|
||||
|
||||
static void handle_stop_sig(int sig) {
|
||||
|
||||
stop_soon = 1;
|
||||
afl_fsrv_killall();
|
||||
|
||||
}
|
||||
|
||||
/* Do basic preparations - persistent fds, filenames, etc. */
|
||||
|
||||
static void set_up_environment(afl_forkserver_t *fsrv) {
|
||||
|
||||
u8 *x;
|
||||
|
||||
fsrv->dev_null_fd = open("/dev/null", O_RDWR);
|
||||
if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
|
||||
|
||||
if (!out_file) {
|
||||
|
||||
u8 *use_dir = ".";
|
||||
|
||||
if (access(use_dir, R_OK | W_OK | X_OK)) {
|
||||
|
||||
use_dir = get_afl_env("TMPDIR");
|
||||
if (!use_dir) { use_dir = "/tmp"; }
|
||||
|
||||
}
|
||||
|
||||
out_file = alloc_printf("%s/.afl-input-temp-%u", use_dir, getpid());
|
||||
|
||||
}
|
||||
|
||||
unlink(out_file);
|
||||
|
||||
fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); }
|
||||
|
||||
/* Set sane defaults... */
|
||||
|
||||
x = get_afl_env("ASAN_OPTIONS");
|
||||
|
||||
if (x) {
|
||||
|
||||
if (!strstr(x, "abort_on_error=1")) {
|
||||
|
||||
FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!");
|
||||
|
||||
}
|
||||
|
||||
if (!strstr(x, "symbolize=0")) {
|
||||
|
||||
FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
x = get_afl_env("MSAN_OPTIONS");
|
||||
|
||||
if (x) {
|
||||
|
||||
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) {
|
||||
|
||||
FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(
|
||||
MSAN_ERROR) " - please fix!");
|
||||
|
||||
}
|
||||
|
||||
if (!strstr(x, "symbolize=0")) {
|
||||
|
||||
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setenv("ASAN_OPTIONS",
|
||||
"abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"symbolize=0:"
|
||||
"allocator_may_return_null=1",
|
||||
0);
|
||||
|
||||
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||
"symbolize=0:"
|
||||
"abort_on_error=1:"
|
||||
"allocator_may_return_null=1:"
|
||||
"msan_track_origins=0", 0);
|
||||
|
||||
if (get_afl_env("AFL_PRELOAD")) {
|
||||
|
||||
if (fsrv->qemu_mode) {
|
||||
|
||||
u8 *qemu_preload = getenv("QEMU_SET_ENV");
|
||||
u8 *afl_preload = getenv("AFL_PRELOAD");
|
||||
u8 *buf;
|
||||
|
||||
s32 i, afl_preload_size = strlen(afl_preload);
|
||||
for (i = 0; i < afl_preload_size; ++i) {
|
||||
|
||||
if (afl_preload[i] == ',') {
|
||||
|
||||
PFATAL(
|
||||
"Comma (',') is not allowed in AFL_PRELOAD when -Q is "
|
||||
"specified!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (qemu_preload) {
|
||||
|
||||
buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s",
|
||||
qemu_preload, afl_preload, afl_preload);
|
||||
|
||||
} else {
|
||||
|
||||
buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s",
|
||||
afl_preload, afl_preload);
|
||||
|
||||
}
|
||||
|
||||
setenv("QEMU_SET_ENV", buf, 1);
|
||||
|
||||
ck_free(buf);
|
||||
|
||||
} else {
|
||||
|
||||
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
||||
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Setup signal handlers, duh. */
|
||||
|
||||
static void setup_signal_handlers(void) {
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = NULL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_sigaction = NULL;
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
/* Various ways of saying "stop". */
|
||||
|
||||
sa.sa_handler = handle_stop_sig;
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
|
||||
}
|
||||
|
||||
/* Display usage hints. */
|
||||
|
||||
static void usage(u8 *argv0) {
|
||||
|
||||
SAYF(
|
||||
"\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
|
||||
|
||||
"Required parameters:\n"
|
||||
|
||||
" -i port - the port to listen for the client to connect to\n\n"
|
||||
|
||||
"Execution control settings:\n"
|
||||
|
||||
" -f file - input file read by the tested program (stdin)\n"
|
||||
" -t msec - timeout for each run (%d ms)\n"
|
||||
" -m megs - memory limit for child process (%d MB)\n"
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||
" -U - use unicorn-based instrumentation (Unicorn mode)\n"
|
||||
" -W - use qemu-based instrumentation with Wine (Wine "
|
||||
"mode)\n\n"
|
||||
|
||||
"Environment variables used:\n"
|
||||
"TMPDIR: directory to use for temporary input files\n"
|
||||
"ASAN_OPTIONS: custom settings for ASAN\n"
|
||||
" (must contain abort_on_error=1 and symbolize=0)\n"
|
||||
"MSAN_OPTIONS: custom settings for MSAN\n"
|
||||
" (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n"
|
||||
"AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n"
|
||||
" the target was compiled for\n"
|
||||
"AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
|
||||
|
||||
, argv0, EXEC_TIMEOUT, MEM_LIMIT);
|
||||
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
int recv_testcase(int s, void **buf, size_t *max_len) {
|
||||
|
||||
u32 size;
|
||||
s32 ret;
|
||||
size_t received;
|
||||
|
||||
received = 0;
|
||||
while (received < 4 && (ret = recv(s, &size + received, 4 - received, 0)) > 0)
|
||||
received += ret;
|
||||
if (received != 4) FATAL("did not receive size information");
|
||||
if (size == 0) FATAL("did not receive valid size information");
|
||||
// fprintf(stderr, "received size information of %d\n", size);
|
||||
|
||||
if ((size & 0xff000000) != 0xff000000) {
|
||||
|
||||
*buf = ck_maybe_grow(buf, max_len, size);
|
||||
received = 0;
|
||||
// fprintf(stderr, "unCOMPRESS (%u)\n", size);
|
||||
while (received < size &&
|
||||
(ret = recv(s, ((char *)*buf) + received, size - received, 0)) > 0)
|
||||
received += ret;
|
||||
|
||||
} else {
|
||||
|
||||
#ifdef USE_DEFLATE
|
||||
u32 clen;
|
||||
size -= 0xff000000;
|
||||
*buf = ck_maybe_grow(buf, max_len, size);
|
||||
received = 0;
|
||||
while (received < 4 &&
|
||||
(ret = recv(s, &clen + received, 4 - received, 0)) > 0)
|
||||
received += ret;
|
||||
if (received != 4) FATAL("did not receive clen1 information");
|
||||
// fprintf(stderr, "received clen information of %d\n", clen);
|
||||
if (clen < 1)
|
||||
FATAL("did not receive valid compressed len information: %u", clen);
|
||||
buf2 = ck_maybe_grow((void **)&buf2, &buf2_len, clen);
|
||||
received = 0;
|
||||
while (received < clen &&
|
||||
(ret = recv(s, buf2 + received, clen - received, 0)) > 0)
|
||||
received += ret;
|
||||
if (received != clen) FATAL("did not receive compressed information");
|
||||
if (libdeflate_deflate_decompress(decompressor, buf2, clen, (char *)*buf,
|
||||
*max_len,
|
||||
&received) != LIBDEFLATE_SUCCESS)
|
||||
FATAL("decompression failed");
|
||||
// fprintf(stderr, "DECOMPRESS (%u->%u):\n", clen, received);
|
||||
// for (u32 i = 0; i < clen; i++) fprintf(stderr, "%02x", buf2[i]);
|
||||
// fprintf(stderr, "\n");
|
||||
// for (u32 i = 0; i < received; i++) fprintf(stderr, "%02x",
|
||||
// ((u8*)(*buf))[i]); fprintf(stderr, "\n");
|
||||
#else
|
||||
FATAL("Received compressed data but not compiled with compression support");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// fprintf(stderr, "receiving testcase %p %p max %u\n", buf, *buf, *max_len);
|
||||
if (received != size)
|
||||
FATAL("did not receive testcase data %lu != %u, %d", received, size, ret);
|
||||
// fprintf(stderr, "received testcase\n");
|
||||
return size;
|
||||
|
||||
}
|
||||
|
||||
/* Main entry point */
|
||||
|
||||
int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
s32 opt, s, sock, on = 1, port = -1;
|
||||
size_t max_len = 0;
|
||||
u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0;
|
||||
char **use_argv;
|
||||
struct sockaddr_in6 serveraddr, clientaddr;
|
||||
int addrlen = sizeof(clientaddr);
|
||||
char str[INET6_ADDRSTRLEN];
|
||||
char ** argv = argv_cpy_dup(argc, argv_orig);
|
||||
u8 * send_buf;
|
||||
#ifdef USE_DEFLATE
|
||||
u32 *lenptr;
|
||||
#endif
|
||||
|
||||
afl_forkserver_t fsrv_var = {0};
|
||||
afl_forkserver_t *fsrv = &fsrv_var;
|
||||
afl_fsrv_init(fsrv);
|
||||
map_size = get_map_size();
|
||||
fsrv->map_size = map_size;
|
||||
|
||||
if ((send_buf = malloc(map_size + 4)) == NULL) PFATAL("malloc");
|
||||
|
||||
while ((opt = getopt(argc, argv, "+i:f:m:t:QUWh")) > 0) {
|
||||
|
||||
switch (opt) {
|
||||
|
||||
case 'i':
|
||||
|
||||
if (port > 0) { FATAL("Multiple -i options not supported"); }
|
||||
port = atoi(optarg);
|
||||
if (port < 1 || port > 65535)
|
||||
FATAL("invalid port definition, must be between 1-65535: %s", optarg);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
|
||||
if (out_file) { FATAL("Multiple -f options not supported"); }
|
||||
fsrv->use_stdin = 0;
|
||||
out_file = optarg;
|
||||
break;
|
||||
|
||||
case 'm': {
|
||||
|
||||
u8 suffix = 'M';
|
||||
|
||||
if (mem_limit_given) { FATAL("Multiple -m options not supported"); }
|
||||
mem_limit_given = 1;
|
||||
|
||||
if (!optarg) { FATAL("Wrong usage of -m"); }
|
||||
|
||||
if (!strcmp(optarg, "none")) {
|
||||
|
||||
fsrv->mem_limit = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (sscanf(optarg, "%llu%c", &fsrv->mem_limit, &suffix) < 1 ||
|
||||
optarg[0] == '-') {
|
||||
|
||||
FATAL("Bad syntax used for -m");
|
||||
|
||||
}
|
||||
|
||||
switch (suffix) {
|
||||
|
||||
case 'T':
|
||||
fsrv->mem_limit *= 1024 * 1024;
|
||||
break;
|
||||
case 'G':
|
||||
fsrv->mem_limit *= 1024;
|
||||
break;
|
||||
case 'k':
|
||||
fsrv->mem_limit /= 1024;
|
||||
break;
|
||||
case 'M':
|
||||
break;
|
||||
|
||||
default:
|
||||
FATAL("Unsupported suffix or bad syntax for -m");
|
||||
|
||||
}
|
||||
|
||||
if (fsrv->mem_limit < 5) { FATAL("Dangerously low value of -m"); }
|
||||
|
||||
if (sizeof(rlim_t) == 4 && fsrv->mem_limit > 2000) {
|
||||
|
||||
FATAL("Value of -m out of range on 32-bit systems");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 't':
|
||||
|
||||
if (timeout_given) { FATAL("Multiple -t options not supported"); }
|
||||
timeout_given = 1;
|
||||
|
||||
if (!optarg) { FATAL("Wrong usage of -t"); }
|
||||
|
||||
fsrv->exec_tmout = atoi(optarg);
|
||||
|
||||
if (fsrv->exec_tmout < 10 || optarg[0] == '-') {
|
||||
|
||||
FATAL("Dangerously low value of -t");
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'Q':
|
||||
|
||||
if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); }
|
||||
if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; }
|
||||
|
||||
fsrv->qemu_mode = 1;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
|
||||
if (unicorn_mode) { FATAL("Multiple -Q options not supported"); }
|
||||
if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; }
|
||||
|
||||
unicorn_mode = 1;
|
||||
break;
|
||||
|
||||
case 'W': /* Wine+QEMU mode */
|
||||
|
||||
if (use_wine) { FATAL("Multiple -W options not supported"); }
|
||||
fsrv->qemu_mode = 1;
|
||||
use_wine = 1;
|
||||
|
||||
if (!mem_limit_given) { fsrv->mem_limit = 0; }
|
||||
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
usage(argv[0]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (optind == argc || port < 1) { usage(argv[0]); }
|
||||
|
||||
check_environment_vars(envp);
|
||||
|
||||
sharedmem_t shm = {0};
|
||||
fsrv->trace_bits = afl_shm_init(&shm, map_size, 0);
|
||||
|
||||
in_data = ck_maybe_grow((void **)&in_data, &max_len, 65536);
|
||||
|
||||
atexit(at_exit_handler);
|
||||
setup_signal_handlers();
|
||||
|
||||
set_up_environment(fsrv);
|
||||
|
||||
fsrv->target_path = find_binary(argv[optind]);
|
||||
detect_file_args(argv + optind, out_file, &fsrv->use_stdin);
|
||||
|
||||
if (fsrv->qemu_mode) {
|
||||
|
||||
if (use_wine) {
|
||||
|
||||
use_argv = get_wine_argv(argv[0], &fsrv->target_path, argc - optind,
|
||||
argv + optind);
|
||||
|
||||
} else {
|
||||
|
||||
use_argv = get_qemu_argv(argv[0], &fsrv->target_path, argc - optind,
|
||||
argv + optind);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
use_argv = argv + optind;
|
||||
|
||||
}
|
||||
|
||||
if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) PFATAL("socket() failed");
|
||||
|
||||
#ifdef SO_REUSEADDR
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) {
|
||||
|
||||
WARNF("setsockopt(SO_REUSEADDR) failed");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SO_PRIORITY
|
||||
int priority = 7;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) <
|
||||
0) {
|
||||
|
||||
priority = 6;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) <
|
||||
0)
|
||||
WARNF("could not set priority on socket");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
memset(&serveraddr, 0, sizeof(serveraddr));
|
||||
serveraddr.sin6_family = AF_INET6;
|
||||
serveraddr.sin6_port = htons(port);
|
||||
serveraddr.sin6_addr = in6addr_any;
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
|
||||
PFATAL("bind() failed");
|
||||
|
||||
if (listen(sock, 1) < 0) { PFATAL("listen() failed"); }
|
||||
|
||||
afl_fsrv_start(fsrv, use_argv, &stop_soon,
|
||||
get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0);
|
||||
|
||||
#ifdef USE_DEFLATE
|
||||
compressor = libdeflate_alloc_compressor(1);
|
||||
decompressor = libdeflate_alloc_decompressor();
|
||||
buf2 = ck_maybe_grow((void **)&buf2, &buf2_len, map_size + 16);
|
||||
lenptr = (u32 *)(buf2 + 4);
|
||||
fprintf(stderr, "Compiled with compression support\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr,
|
||||
"Waiting for incoming connection from afl-network-client on port %d "
|
||||
"...\n",
|
||||
port);
|
||||
|
||||
if ((s = accept(sock, NULL, NULL)) < 0) { PFATAL("accept() failed"); }
|
||||
fprintf(stderr, "Received connection, starting ...\n");
|
||||
|
||||
#ifdef SO_PRIORITY
|
||||
priority = 7;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) {
|
||||
|
||||
priority = 6;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0)
|
||||
WARNF("could not set priority on socket");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
while ((in_len = recv_testcase(s, (void **)&in_data, &max_len)) > 0) {
|
||||
|
||||
// fprintf(stderr, "received %u\n", in_len);
|
||||
(void)run_target(fsrv, use_argv, in_data, in_len, 1);
|
||||
|
||||
memcpy(send_buf + 4, fsrv->trace_bits, fsrv->map_size);
|
||||
|
||||
#ifdef USE_DEFLATE
|
||||
memcpy(buf2, &fsrv->child_status, 4);
|
||||
*lenptr = (u32)libdeflate_deflate_compress(
|
||||
compressor, send_buf + 4, fsrv->map_size, buf2 + 8, buf2_len - 8);
|
||||
// fprintf(stderr, "COMPRESS (%u->%u): ", fsrv->map_size, *lenptr);
|
||||
// for (u32 i = 0; i < fsrv->map_size; i++) fprintf(stderr, "%02x",
|
||||
// fsrv->trace_bits[i]); fprintf(stderr, "\n");
|
||||
if (send(s, buf2, *lenptr + 8, 0) != 8 + *lenptr)
|
||||
FATAL("could not send data");
|
||||
#else
|
||||
memcpy(send_buf, &fsrv->child_status, 4);
|
||||
if (send(s, send_buf, fsrv->map_size + 4, 0) != 4 + fsrv->map_size)
|
||||
FATAL("could not send data");
|
||||
#endif
|
||||
|
||||
// fprintf(stderr, "sent result\n");
|
||||
|
||||
}
|
||||
|
||||
unlink(out_file);
|
||||
if (out_file) { ck_free(out_file); }
|
||||
out_file = NULL;
|
||||
|
||||
afl_shm_deinit(&shm);
|
||||
afl_fsrv_deinit(fsrv);
|
||||
if (fsrv->target_path) { ck_free(fsrv->target_path); }
|
||||
if (in_data) { ck_free(in_data); }
|
||||
#if USE_DEFLATE
|
||||
if (buf2) { ck_free(buf2); }
|
||||
libdeflate_free_compressor(compressor);
|
||||
libdeflate_free_decompressor(decompressor);
|
||||
#endif
|
||||
|
||||
argv_cpy_free(argv);
|
||||
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
7
examples/afl_proxy/Makefile
Normal file
7
examples/afl_proxy/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
all: afl-proxy
|
||||
|
||||
afl-proxy: afl-proxy.c
|
||||
$(CC) -I../../include -o afl-proxy afl-proxy.c
|
||||
|
||||
clean:
|
||||
rm -f afl-proxy *~ core
|
9
examples/afl_proxy/README.md
Normal file
9
examples/afl_proxy/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# afl-proxy
|
||||
|
||||
afl-proxy is an example skeleton file which can easily be used to fuzz
|
||||
and instrument non-standard things.
|
||||
|
||||
You only need to change the while() loop of the main() to send the
|
||||
data of buf[] with length len to the target and write the coverage
|
||||
information to __afl_area_ptr[__afl_map_size]
|
||||
|
238
examples/afl_proxy/afl-proxy.c
Normal file
238
examples/afl_proxy/afl-proxy.c
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
american fuzzy lop++ - afl-proxy skeleton example
|
||||
---------------------------------------------------
|
||||
|
||||
Written by Marc Heuse <mh@mh-sec.de>
|
||||
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
|
||||
HOW-TO
|
||||
======
|
||||
|
||||
You only need to change the while() loop of the main() to send the
|
||||
data of buf[] with length len to the target and write the coverage
|
||||
information to __afl_area_ptr[__afl_map_size]
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
u8 *__afl_area_ptr;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
u32 __afl_map_size = MAP_SIZE;
|
||||
#else
|
||||
__thread u32 __afl_map_size = MAP_SIZE;
|
||||
#endif
|
||||
|
||||
/* Error reporting to forkserver controller */
|
||||
|
||||
void send_forkserver_error(int error) {
|
||||
|
||||
u32 status;
|
||||
if (!error || error > 0xffff) return;
|
||||
status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error));
|
||||
if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return;
|
||||
|
||||
}
|
||||
|
||||
/* SHM setup. */
|
||||
|
||||
static void __afl_map_shm(void) {
|
||||
|
||||
char *id_str = getenv(SHM_ENV_VAR);
|
||||
char *ptr;
|
||||
|
||||
if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) {
|
||||
|
||||
u32 val = atoi(ptr);
|
||||
if (val > 0) __afl_map_size = val;
|
||||
|
||||
}
|
||||
|
||||
if (__afl_map_size > MAP_SIZE) {
|
||||
|
||||
if (__afl_map_size > FS_OPT_MAX_MAPSIZE) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to "
|
||||
"be able to run this instrumented program!\n",
|
||||
__afl_map_size);
|
||||
if (id_str) {
|
||||
|
||||
send_forkserver_error(FS_ERROR_MAP_SIZE);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fprintf(stderr,
|
||||
"Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to "
|
||||
"be able to run this instrumented program!\n",
|
||||
__afl_map_size);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (id_str) {
|
||||
|
||||
#ifdef USEMMAP
|
||||
const char * shm_file_path = id_str;
|
||||
int shm_fd = -1;
|
||||
unsigned char *shm_base = NULL;
|
||||
|
||||
/* create the shared memory segment as if it was a file */
|
||||
shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
|
||||
if (shm_fd == -1) {
|
||||
|
||||
fprintf(stderr, "shm_open() failed\n");
|
||||
send_forkserver_error(FS_ERROR_SHM_OPEN);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* map the shared memory segment to the address space of the process */
|
||||
shm_base =
|
||||
mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
||||
|
||||
if (shm_base == MAP_FAILED) {
|
||||
|
||||
close(shm_fd);
|
||||
shm_fd = -1;
|
||||
|
||||
fprintf(stderr, "mmap() failed\n");
|
||||
send_forkserver_error(FS_ERROR_MMAP);
|
||||
exit(2);
|
||||
|
||||
}
|
||||
|
||||
__afl_area_ptr = shm_base;
|
||||
#else
|
||||
u32 shm_id = atoi(id_str);
|
||||
|
||||
__afl_area_ptr = shmat(shm_id, 0, 0);
|
||||
|
||||
#endif
|
||||
|
||||
if (__afl_area_ptr == (void *)-1) {
|
||||
|
||||
send_forkserver_error(FS_ERROR_SHMAT);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* Write something into the bitmap so that the parent doesn't give up */
|
||||
|
||||
__afl_area_ptr[0] = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Fork server logic. */
|
||||
|
||||
static void __afl_start_forkserver(void) {
|
||||
|
||||
u8 tmp[4] = {0, 0, 0, 0};
|
||||
u32 status = 0;
|
||||
|
||||
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
|
||||
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
|
||||
if (status) status |= (FS_OPT_ENABLED);
|
||||
memcpy(tmp, &status, 4);
|
||||
|
||||
/* Phone home and tell the parent that we're OK. */
|
||||
|
||||
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
|
||||
|
||||
}
|
||||
|
||||
static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
|
||||
|
||||
s32 status, res = 0xffffff;
|
||||
|
||||
/* Wait for parent by reading from the pipe. Abort if read fails. */
|
||||
if (read(FORKSRV_FD, &status, 4) != 4) return 0;
|
||||
|
||||
/* we have a testcase - read it */
|
||||
status = read(0, buf, max_len);
|
||||
|
||||
/* report that we are starting the target */
|
||||
if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0;
|
||||
|
||||
if (status < 1)
|
||||
return 0;
|
||||
else
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static void __afl_end_testcase(void) {
|
||||
|
||||
int status = 0xffffff;
|
||||
|
||||
if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* you just need to modify the while() loop in this main() */
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
/* This is were the testcase data is written into */
|
||||
u8 buf[1024]; // this is the maximum size for a test case! set it!
|
||||
u32 len;
|
||||
|
||||
/* here you specify the map size you need that you are reporting to
|
||||
afl-fuzz. */
|
||||
__afl_map_size = MAP_SIZE; // default is 65536
|
||||
|
||||
/* then we initialize the shared memory map and start the forkserver */
|
||||
__afl_map_shm();
|
||||
__afl_start_forkserver();
|
||||
|
||||
while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) {
|
||||
|
||||
/* here you have to create the magic that feeds the buf/len to the
|
||||
target and write the coverage to __afl_area_ptr */
|
||||
|
||||
// ... the magic ...
|
||||
|
||||
/* report the test case is done and wait for the next */
|
||||
__afl_end_testcase();
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
16
examples/afl_untracer/Makefile
Normal file
16
examples/afl_untracer/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
ifdef DEBUG
|
||||
OPT=-O0
|
||||
else
|
||||
OPT=-O3
|
||||
endif
|
||||
|
||||
all: afl-untracer libtestinstr.so
|
||||
|
||||
afl-untracer: afl-untracer.c
|
||||
$(CC) $(OPT) -I../../include -g -o afl-untracer afl-untracer.c -ldl
|
||||
|
||||
libtestinstr.so: libtestinstr.c
|
||||
$(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c
|
||||
|
||||
clean:
|
||||
rm -f afl-untracer libtestinstr.so *~ core
|
59
examples/afl_untracer/README.md
Normal file
59
examples/afl_untracer/README.md
Normal file
@ -0,0 +1,59 @@
|
||||
# afl-untracer - fast fuzzing of binary-only libraries
|
||||
|
||||
## Introduction
|
||||
|
||||
afl-untracer is an example skeleton file which can easily be used to fuzz
|
||||
a closed source library.
|
||||
|
||||
It requires less memory and is x3-5 faster than qemu_mode however it is way
|
||||
more course grained and does not provide interesting features like compcov
|
||||
or cmplog.
|
||||
|
||||
Supported is so far Intel (i386/x86_64) and AARCH64.
|
||||
|
||||
## How-to
|
||||
|
||||
### Modify afl-untracer.c
|
||||
|
||||
Read and modify afl-untracer.c then `make`.
|
||||
To adapt afl-untracer.c to your needs, read the header of the file and then
|
||||
search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations.
|
||||
|
||||
### Generate patches.txt file
|
||||
|
||||
To generate the `patches.txt` file for your target library use the
|
||||
`ida_get_patchpoints.py` script for IDA Pro or
|
||||
`ghidra_get_patchpoints.java` for Ghidra.
|
||||
|
||||
The patches.txt file has to be pointed to by `AFL_UNTRACER_FILE`.
|
||||
|
||||
To easily run the scripts without needing to run the GUI with Ghidra:
|
||||
```
|
||||
$ /opt/ghidra/support/analyzeHeadless /tmp/ tmp$$ -import libtestinstr.so -postscript ./ghidra_get_patchpoints.java
|
||||
$ rm -rf /tmp/tmp$$
|
||||
```
|
||||
|
||||
### Fuzzing
|
||||
|
||||
Example (after modifying afl-untracer.c to your needs, compiling and creating
|
||||
patches.txt):
|
||||
```
|
||||
AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer
|
||||
```
|
||||
(or even remote via afl-network-proxy).
|
||||
|
||||
### Testing and debugging
|
||||
|
||||
For testing/debugging you can try:
|
||||
```
|
||||
make DEBUG=1
|
||||
AFL_UNTRACER_FILE=./patches.txt AFL_DEBUG=1 gdb ./afl-untracer
|
||||
```
|
||||
and then you can easily set breakpoints to "breakpoint" and "fuzz".
|
||||
|
||||
# Background
|
||||
|
||||
This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL)
|
||||
and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz).
|
||||
This implementation is slower because the traps are not patched out with each
|
||||
run, but on the other hand gives much better coverage information.
|
756
examples/afl_untracer/afl-untracer.c
Normal file
756
examples/afl_untracer/afl-untracer.c
Normal file
@ -0,0 +1,756 @@
|
||||
/*
|
||||
american fuzzy lop++ - afl-untracer skeleton example
|
||||
---------------------------------------------------
|
||||
|
||||
Written by Marc Heuse <mh@mh-sec.de>
|
||||
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
|
||||
HOW-TO
|
||||
======
|
||||
|
||||
You only need to change the following:
|
||||
|
||||
1. decide if you want to receive data from stdin [DEFAULT] or file(name)
|
||||
-> use_stdin = 0 if via file, and what the maximum input size is
|
||||
2. dl load the library you want to fuzz, lookup the functions you need
|
||||
and setup the calls to these
|
||||
3. in the while loop you call the functions in the necessary order -
|
||||
incl the cleanup. the cleanup is important!
|
||||
|
||||
Just look these steps up in the code, look for "// STEP x:"
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#define __USE_GNU
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/ucontext.h>
|
||||
#elif defined(__APPLE__) && defined(__LP64__)
|
||||
#include <mach-o/dyld_images.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
#define MEMORY_MAP_DECREMENT 0x200000000000
|
||||
#define MAX_LIB_COUNT 128
|
||||
|
||||
// STEP 1:
|
||||
|
||||
/* use stdin (1) or a file on the commandline (0) */
|
||||
static u32 use_stdin = 1;
|
||||
|
||||
/* This is were the testcase data is written into */
|
||||
static u8 buf[10000]; // this is the maximum size for a test case! set it!
|
||||
|
||||
/* If you want to have debug output set this to 1, can also be set with
|
||||
AFL_DEBUG */
|
||||
static u32 debug = 0;
|
||||
|
||||
// END STEP 1
|
||||
|
||||
typedef struct library_list {
|
||||
|
||||
u8 *name;
|
||||
u64 addr_start, addr_end;
|
||||
|
||||
} library_list_t;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
u32 __afl_map_size = MAP_SIZE;
|
||||
u32 do_exit;
|
||||
#else
|
||||
__thread u32 __afl_map_size = MAP_SIZE;
|
||||
__thread u32 do_exit;
|
||||
#endif
|
||||
|
||||
static pid_t pid = 65537;
|
||||
static pthread_t __afl_thread;
|
||||
static u8 __afl_dummy[MAP_SIZE];
|
||||
static u8 * __afl_area_ptr = __afl_dummy;
|
||||
static u8 * inputfile; // this will point to argv[1]
|
||||
static u32 len;
|
||||
|
||||
static library_list_t liblist[MAX_LIB_COUNT];
|
||||
static u32 liblist_cnt;
|
||||
|
||||
static void sigtrap_handler(int signum, siginfo_t *si, void *context);
|
||||
static void fuzz();
|
||||
|
||||
/* read the library information */
|
||||
void read_library_information() {
|
||||
|
||||
#if defined(__linux__)
|
||||
FILE *f;
|
||||
u8 buf[1024], *b, *m, *e, *n;
|
||||
|
||||
if ((f = fopen("/proc/self/maps", "r")) == NULL)
|
||||
FATAL("cannot open /proc/self/maps");
|
||||
|
||||
if (debug) fprintf(stderr, "Library list:\n");
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
|
||||
if (strstr(buf, " r-x")) {
|
||||
|
||||
if (liblist_cnt >= MAX_LIB_COUNT) {
|
||||
|
||||
WARNF("too many libraries to old, maximum count of %d reached",
|
||||
liblist_cnt);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
b = buf;
|
||||
m = index(buf, '-');
|
||||
e = index(buf, ' ');
|
||||
if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' ');
|
||||
if (n &&
|
||||
((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '('))
|
||||
n = NULL;
|
||||
else
|
||||
n++;
|
||||
if (b && m && e && n && *n) {
|
||||
|
||||
*m++ = 0;
|
||||
*e = 0;
|
||||
if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0;
|
||||
|
||||
liblist[liblist_cnt].name = strdup(n);
|
||||
liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16);
|
||||
liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16);
|
||||
if (debug)
|
||||
fprintf(
|
||||
stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name,
|
||||
liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start,
|
||||
liblist[liblist_cnt].addr_start,
|
||||
liblist[liblist_cnt].addr_end - 1);
|
||||
liblist_cnt++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (debug) fprintf(stderr, "\n");
|
||||
|
||||
#elif defined(__FreeBSD__)
|
||||
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()};
|
||||
char * buf, *start, *end;
|
||||
size_t miblen = sizeof(mib) / sizeof(mib[0]);
|
||||
size_t len;
|
||||
|
||||
if (debug) fprintf(stderr, "Library list:\n");
|
||||
if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; }
|
||||
|
||||
len = len * 4 / 3;
|
||||
|
||||
buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
|
||||
if (buf == MAP_FAILED) { return; }
|
||||
|
||||
if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) {
|
||||
|
||||
munmap(buf, len);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
start = buf;
|
||||
end = buf + len;
|
||||
|
||||
while (start < end) {
|
||||
|
||||
struct kinfo_vmentry *region = (struct kinfo_vmentry *)start;
|
||||
size_t size = region->kve_structsize;
|
||||
|
||||
if (size == 0) { break; }
|
||||
|
||||
if ((region->kve_protection & KVME_PROT_READ) &&
|
||||
!(region->kve_protection & KVME_PROT_EXEC)) {
|
||||
|
||||
liblist[liblist_cnt].name =
|
||||
region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0;
|
||||
liblist[liblist_cnt].addr_start = region->kve_start;
|
||||
liblist[liblist_cnt].addr_end = region->kve_end;
|
||||
|
||||
if (debug) {
|
||||
|
||||
fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name,
|
||||
liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start,
|
||||
liblist[liblist_cnt].addr_start,
|
||||
liblist[liblist_cnt].addr_end - 1);
|
||||
|
||||
}
|
||||
|
||||
liblist_cnt++;
|
||||
|
||||
}
|
||||
|
||||
start += size;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
library_list_t *find_library(char *name) {
|
||||
|
||||
#if defined(__linux__)
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < liblist_cnt; i++)
|
||||
if (strncmp(liblist[i].name, name, strlen(name)) == 0) return &liblist[i];
|
||||
#elif defined(__APPLE__) && defined(__LP64__)
|
||||
kern_return_t err;
|
||||
static library_list_t lib;
|
||||
|
||||
// get the list of all loaded modules from dyld
|
||||
// the task_info mach API will get the address of the dyld all_image_info
|
||||
// struct for the given task from which we can get the names and load
|
||||
// addresses of all modules
|
||||
task_dyld_info_data_t task_dyld_info;
|
||||
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
|
||||
err = task_info(mach_task_self(), TASK_DYLD_INFO,
|
||||
(task_info_t)&task_dyld_info, &count);
|
||||
|
||||
const struct dyld_all_image_infos *all_image_infos =
|
||||
(const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr;
|
||||
const struct dyld_image_info *image_infos = all_image_infos->infoArray;
|
||||
|
||||
for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) {
|
||||
|
||||
const char * image_name = image_infos[i].imageFilePath;
|
||||
mach_vm_address_t image_load_address =
|
||||
(mach_vm_address_t)image_infos[i].imageLoadAddress;
|
||||
if (strstr(image_name, name)) {
|
||||
|
||||
lib.name = name;
|
||||
lib.addr_start = (u64)image_load_address;
|
||||
lib.addr_end = 0;
|
||||
return &lib;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/* for having an easy breakpoint location after loading the shared library */
|
||||
// this seems to work for clang too. nice :) requires gcc 4.4+
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("O0")
|
||||
void breakpoint() {
|
||||
|
||||
if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n");
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
/* Error reporting to forkserver controller */
|
||||
|
||||
void send_forkserver_error(int error) {
|
||||
|
||||
u32 status;
|
||||
if (!error || error > 0xffff) return;
|
||||
status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error));
|
||||
if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return;
|
||||
|
||||
}
|
||||
|
||||
/* SHM setup. */
|
||||
|
||||
static void __afl_map_shm(void) {
|
||||
|
||||
char *id_str = getenv(SHM_ENV_VAR);
|
||||
char *ptr;
|
||||
|
||||
if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) {
|
||||
|
||||
u32 val = atoi(ptr);
|
||||
if (val > 0) __afl_map_size = val;
|
||||
|
||||
}
|
||||
|
||||
if (__afl_map_size > MAP_SIZE) {
|
||||
|
||||
if (__afl_map_size > FS_OPT_MAX_MAPSIZE) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to "
|
||||
"be able to run this instrumented program!\n",
|
||||
__afl_map_size);
|
||||
if (id_str) {
|
||||
|
||||
send_forkserver_error(FS_ERROR_MAP_SIZE);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fprintf(stderr,
|
||||
"Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to "
|
||||
"be able to run this instrumented program!\n",
|
||||
__afl_map_size);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (id_str) {
|
||||
|
||||
#ifdef USEMMAP
|
||||
const char * shm_file_path = id_str;
|
||||
int shm_fd = -1;
|
||||
unsigned char *shm_base = NULL;
|
||||
|
||||
/* create the shared memory segment as if it was a file */
|
||||
shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
|
||||
if (shm_fd == -1) {
|
||||
|
||||
fprintf(stderr, "shm_open() failed\n");
|
||||
send_forkserver_error(FS_ERROR_SHM_OPEN);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* map the shared memory segment to the address space of the process */
|
||||
shm_base =
|
||||
mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
||||
|
||||
if (shm_base == MAP_FAILED) {
|
||||
|
||||
close(shm_fd);
|
||||
shm_fd = -1;
|
||||
|
||||
fprintf(stderr, "mmap() failed\n");
|
||||
send_forkserver_error(FS_ERROR_MMAP);
|
||||
exit(2);
|
||||
|
||||
}
|
||||
|
||||
__afl_area_ptr = shm_base;
|
||||
#else
|
||||
u32 shm_id = atoi(id_str);
|
||||
|
||||
__afl_area_ptr = shmat(shm_id, 0, 0);
|
||||
|
||||
#endif
|
||||
|
||||
if (__afl_area_ptr == (void *)-1) {
|
||||
|
||||
send_forkserver_error(FS_ERROR_SHMAT);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* Write something into the bitmap so that the parent doesn't give up */
|
||||
|
||||
__afl_area_ptr[0] = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Fork server logic. */
|
||||
static void __afl_start_forkserver(void) {
|
||||
|
||||
u8 tmp[4] = {0, 0, 0, 0};
|
||||
u32 status = 0;
|
||||
|
||||
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
|
||||
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
|
||||
if (status) status |= (FS_OPT_ENABLED);
|
||||
memcpy(tmp, &status, 4);
|
||||
|
||||
/* Phone home and tell the parent that we're OK. */
|
||||
if (write(FORKSRV_FD + 1, tmp, 4) != 4) do_exit = 1;
|
||||
// fprintf(stderr, "write0 %d\n", do_exit);
|
||||
|
||||
}
|
||||
|
||||
static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
|
||||
|
||||
s32 status;
|
||||
|
||||
/* Wait for parent by reading from the pipe. Abort if read fails. */
|
||||
if (read(FORKSRV_FD, &status, 4) != 4) do_exit = 1;
|
||||
// fprintf(stderr, "read %d\n", do_exit);
|
||||
|
||||
/* we have a testcase - read it if we read from stdin */
|
||||
if (use_stdin) {
|
||||
|
||||
if ((status = read(0, buf, max_len)) <= 0) exit(-1);
|
||||
|
||||
} else
|
||||
|
||||
status = 1;
|
||||
// fprintf(stderr, "stdin: %d %d\n", use_stdin, status);
|
||||
|
||||
/* report that we are starting the target */
|
||||
if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1;
|
||||
// fprintf(stderr, "write1 %d\n", do_exit);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static void __afl_end_testcase(int status) {
|
||||
|
||||
if (write(FORKSRV_FD + 1, &status, 4) != 4) do_exit = 1;
|
||||
// fprintf(stderr, "write2 %d\n", do_exit);
|
||||
if (do_exit) exit(0);
|
||||
|
||||
}
|
||||
|
||||
#ifdef __aarch64__
|
||||
#define SHADOW(addr) \
|
||||
((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \
|
||||
MEMORY_MAP_DECREMENT - \
|
||||
((uintptr_t)addr & 0x7) * 0x10000000000))
|
||||
#else
|
||||
#define SHADOW(addr) \
|
||||
((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \
|
||||
MEMORY_MAP_DECREMENT - \
|
||||
((uintptr_t)addr & 0x3) * 0x10000000000))
|
||||
#endif
|
||||
|
||||
void setup_trap_instrumentation() {
|
||||
|
||||
library_list_t *lib_base = NULL;
|
||||
size_t lib_size = 0;
|
||||
u8 * lib_addr;
|
||||
char * line = NULL;
|
||||
size_t nread, len = 0;
|
||||
char * filename = getenv("AFL_UNTRACER_FILE");
|
||||
if (!filename) filename = getenv("TRAPFUZZ_FILE");
|
||||
if (!filename) FATAL("AFL_UNTRACER_FILE environment variable not set");
|
||||
|
||||
FILE *patches = fopen(filename, "r");
|
||||
if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename);
|
||||
|
||||
// Index into the coverage bitmap for the current trap instruction.
|
||||
#ifdef __aarch64__
|
||||
uint64_t bitmap_index = 0;
|
||||
#else
|
||||
uint32_t bitmap_index = 0;
|
||||
#endif
|
||||
|
||||
while ((nread = getline(&line, &len, patches)) != -1) {
|
||||
|
||||
char *end = line + len;
|
||||
|
||||
char *col = strchr(line, ':');
|
||||
if (col) {
|
||||
|
||||
// It's a library:size pair
|
||||
*col++ = 0;
|
||||
|
||||
lib_base = find_library(line);
|
||||
if (!lib_base) FATAL("Library %s does not appear to be loaded", line);
|
||||
|
||||
// we ignore the defined lib_size
|
||||
lib_size = strtoul(col, NULL, 16);
|
||||
#if (__linux__)
|
||||
if (lib_size < lib_base->addr_end - lib_base->addr_start)
|
||||
lib_size = lib_base->addr_end - lib_base->addr_start;
|
||||
#endif
|
||||
if (lib_size % 0x1000 != 0)
|
||||
WARNF("Invalid library size 0x%zx. Must be multiple of 0x1000",
|
||||
lib_size);
|
||||
|
||||
lib_addr = (u8 *)lib_base->addr_start;
|
||||
|
||||
// Make library code writable.
|
||||
if (mprotect((void *)lib_addr, lib_size,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
|
||||
FATAL("Failed to mprotect library %s writable", line);
|
||||
|
||||
// Create shadow memory.
|
||||
#ifdef __aarch64__
|
||||
for (int i = 0; i < 8; i++) {
|
||||
|
||||
#else
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
||||
#endif
|
||||
|
||||
void *shadow_addr = SHADOW(lib_addr + i);
|
||||
void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0);
|
||||
if (debug)
|
||||
fprintf(stderr, "Shadow: %s %d = %p-%p for %p\n", line, i, shadow,
|
||||
shadow + lib_size - 1, lib_addr);
|
||||
if (shadow == MAP_FAILED) FATAL("Failed to mmap shadow memory");
|
||||
|
||||
}
|
||||
|
||||
// Done, continue with next line.
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
// It's an offset, parse it and do the patching.
|
||||
unsigned long offset = strtoul(line, NULL, 16);
|
||||
|
||||
if (offset > lib_size)
|
||||
FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large",
|
||||
offset, lib_size);
|
||||
|
||||
if (bitmap_index >= __afl_map_size)
|
||||
FATAL("Too many basic blocks to instrument");
|
||||
|
||||
#ifdef __arch64__
|
||||
uint64_t
|
||||
#else
|
||||
uint32_t
|
||||
#endif
|
||||
*shadow = SHADOW(lib_addr + offset);
|
||||
if (*shadow != 0) continue; // skip duplicates
|
||||
|
||||
// Make lookup entry in shadow memory.
|
||||
|
||||
#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \
|
||||
defined(__i386__))
|
||||
|
||||
// this is for Intel x64
|
||||
|
||||
uint8_t orig_byte = lib_addr[offset];
|
||||
*shadow = (bitmap_index << 8) | orig_byte;
|
||||
lib_addr[offset] = 0xcc; // replace instruction with debug trap
|
||||
if (debug)
|
||||
fprintf(stderr,
|
||||
"Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %08x\n",
|
||||
lib_addr, offset, lib_addr + offset, orig_byte, shadow,
|
||||
bitmap_index, *shadow);
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
// this is for aarch64
|
||||
|
||||
uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset);
|
||||
uint32_t orig_bytes = *patch_bytes;
|
||||
*shadow = (bitmap_index << 32) | orig_bytes;
|
||||
*patch_bytes = 0xd4200000; // replace instruction with debug trap
|
||||
if (debug)
|
||||
fprintf(stderr,
|
||||
"Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n",
|
||||
lib_addr, offset, lib_addr + offset, orig_bytes, shadow,
|
||||
bitmap_index, *shadow);
|
||||
|
||||
#else
|
||||
// this will be ARM and AARCH64
|
||||
// for ARM we will need to identify if the code is in thumb or ARM
|
||||
#error "non x86_64/aarch64 not supported yet"
|
||||
//__arm__:
|
||||
// linux thumb: 0xde01
|
||||
// linux arm: 0xe7f001f0
|
||||
//__aarch64__:
|
||||
// linux aarch64: 0xd4200000
|
||||
#endif
|
||||
|
||||
bitmap_index++;
|
||||
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(patches);
|
||||
|
||||
// Install signal handler for SIGTRAP.
|
||||
struct sigaction s;
|
||||
s.sa_flags = SA_SIGINFO;
|
||||
s.sa_sigaction = sigtrap_handler;
|
||||
sigemptyset(&s.sa_mask);
|
||||
sigaction(SIGTRAP, &s, 0);
|
||||
|
||||
if (debug) fprintf(stderr, "Patched %u locations.\n", bitmap_index);
|
||||
__afl_map_size = bitmap_index;
|
||||
if (__afl_map_size % 8) __afl_map_size = (((__afl_map_size + 7) >> 3) << 3);
|
||||
|
||||
}
|
||||
|
||||
/* the signal handler for the traps / debugging interrupts
|
||||
No debug output here because this would cost speed */
|
||||
static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
|
||||
|
||||
uint64_t addr;
|
||||
// Must re-execute the instruction, so decrement PC by one instruction.
|
||||
ucontext_t *ctx = (ucontext_t *)context;
|
||||
#if defined(__APPLE__) && defined(__LP64__)
|
||||
ctx->uc_mcontext->__ss.__rip -= 1;
|
||||
addr = ctx->uc_mcontext->__ss.__rip;
|
||||
#elif defined(__linux__)
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
ctx->uc_mcontext.gregs[REG_RIP] -= 1;
|
||||
addr = ctx->uc_mcontext.gregs[REG_RIP];
|
||||
#elif defined(__aarch64__)
|
||||
ctx->uc_mcontext.pc -= 4;
|
||||
addr = ctx->uc_mcontext.pc;
|
||||
#else
|
||||
#error "Unsupported processor"
|
||||
#endif
|
||||
#elif defined(__FreeBSD__) && defined(__LP64__)
|
||||
ctx->uc_mcontext.mc_rip -= 1;
|
||||
addr = ctx->uc_mcontext.mc_rip;
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
// fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr,
|
||||
// si->si_addr);
|
||||
|
||||
// If the trap didn't come from our instrumentation, then we probably will
|
||||
// just segfault here
|
||||
uint8_t *faultaddr;
|
||||
if (unlikely(si->si_addr))
|
||||
faultaddr = (u8 *)si->si_addr - 1;
|
||||
else
|
||||
faultaddr = (u8 *)addr;
|
||||
// if (debug) fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr));
|
||||
uint32_t shadow = *SHADOW(faultaddr);
|
||||
uint8_t orig_byte = shadow & 0xff;
|
||||
uint32_t index = shadow >> 8;
|
||||
|
||||
// if (debug) fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n",
|
||||
// shadow, orig_byte, index);
|
||||
|
||||
// Index zero is invalid so that it is still possible to catch actual trap
|
||||
// instructions in instrumented libraries.
|
||||
if (unlikely(index == 0)) abort();
|
||||
|
||||
// Restore original instruction
|
||||
*faultaddr = orig_byte;
|
||||
|
||||
__afl_area_ptr[index] = 128;
|
||||
|
||||
}
|
||||
|
||||
/* here you need to specify the parameter for the target function */
|
||||
static void *(*o_function)(u8 *buf, int len);
|
||||
|
||||
/* the MAIN function */
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
pid = getpid();
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
|
||||
/* by default we use stdin, but also a filename can be passed, in this
|
||||
case the input is argv[1] and we have to disable stdin */
|
||||
if (argc > 1) {
|
||||
|
||||
use_stdin = 0;
|
||||
inputfile = argv[1];
|
||||
|
||||
}
|
||||
|
||||
// STEP 2: load the library you want to fuzz and lookup the functions,
|
||||
// inclusive of the cleanup functions
|
||||
// NOTE: above the main() you have to define the functions!
|
||||
|
||||
void *dl = dlopen("./libtestinstr.so", RTLD_LAZY);
|
||||
if (!dl) FATAL("could not find target library");
|
||||
o_function = dlsym(dl, "testinstr");
|
||||
if (!o_function) FATAL("could not resolve target function from library");
|
||||
if (debug) fprintf(stderr, "Function address: %p\n", o_function);
|
||||
|
||||
// END STEP 2
|
||||
|
||||
/* setup instrumentation, shared memory and forkserver */
|
||||
breakpoint();
|
||||
read_library_information();
|
||||
setup_trap_instrumentation();
|
||||
__afl_map_shm();
|
||||
__afl_start_forkserver();
|
||||
|
||||
while (1) {
|
||||
|
||||
if ((pid = fork()) == -1) PFATAL("fork failed");
|
||||
|
||||
if (pid) {
|
||||
|
||||
u32 status;
|
||||
if (waitpid(pid, &status, 0) < 0) exit(1);
|
||||
/* report the test case is done and wait for the next */
|
||||
__afl_end_testcase(status);
|
||||
|
||||
} else {
|
||||
|
||||
pid = getpid();
|
||||
while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) {
|
||||
|
||||
// in this function the fuzz magic happens, this is STEP 3
|
||||
fuzz();
|
||||
|
||||
// we can use _exit which is faster because our target library
|
||||
// was loaded via dlopen and therefore cannot have deconstructors
|
||||
// registered.
|
||||
_exit(0);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void fuzz() {
|
||||
|
||||
// STEP 3: call the function to fuzz, also the functions you might
|
||||
// need to call to prepare the function and - important! -
|
||||
// to clean everything up
|
||||
|
||||
// in this example we use the input file, not stdin!
|
||||
(*o_function)(buf, len);
|
||||
|
||||
// normally you also need to cleanup
|
||||
//(*o_LibFree)(foo);
|
||||
|
||||
// END STEP 3
|
||||
|
||||
}
|
||||
|
84
examples/afl_untracer/ghidra_get_patchpoints.java
Normal file
84
examples/afl_untracer/ghidra_get_patchpoints.java
Normal file
@ -0,0 +1,84 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Find patch points for untracer tools (e.g. afl++ examples/afl_untracer)
|
||||
//
|
||||
// Copy to ..../Ghidra/Features/Search/ghidra_scripts/
|
||||
// Writes the results to ~/Desktop/patches.txt
|
||||
//
|
||||
// This is my very first Ghidra script. I am sure this could be done better.
|
||||
//
|
||||
//@category Search
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.block.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ghidra_get_patchpoints extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
long segment_start = 0;
|
||||
Memory memory = currentProgram.getMemory();
|
||||
MultEntSubModel model = new MultEntSubModel(currentProgram);
|
||||
CodeBlockIterator subIter = model.getCodeBlocks(monitor);
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "patches.txt"));
|
||||
|
||||
while (subIter.hasNext()) {
|
||||
|
||||
CodeBlock multiEntryBlock = subIter.next();
|
||||
SimpleBlockModel basicBlockModel = new SimpleBlockModel(currentProgram);
|
||||
CodeBlockIterator bbIter = basicBlockModel.getCodeBlocksContaining(multiEntryBlock, monitor);
|
||||
|
||||
while (bbIter.hasNext()) {
|
||||
|
||||
CodeBlock basicBlock = bbIter.next();
|
||||
|
||||
if (segment_start == 0) {
|
||||
|
||||
Address firstAddr = basicBlock.getFirstStartAddress();
|
||||
long firstBlockAddr = firstAddr.getAddressableWordOffset();
|
||||
MemoryBlock mb = memory.getBlock(firstAddr);
|
||||
Address startAddr = mb.getStart();
|
||||
Address endAddr = mb.getEnd();
|
||||
segment_start = startAddr.getAddressableWordOffset();
|
||||
if ((firstBlockAddr - segment_start) >= 0x1000)
|
||||
segment_start += 0x1000;
|
||||
long segment_end = endAddr.getAddressableWordOffset();
|
||||
long segment_size = segment_end - segment_start;
|
||||
if ((segment_size % 0x1000) > 0)
|
||||
segment_size = (((segment_size / 0x1000) + 1) * 0x1000);
|
||||
out.write(currentProgram.getName() + ":0x" + Long.toHexString(segment_size) + "\n");
|
||||
//println("Start: " + Long.toHexString(segment_start));
|
||||
//println("End: " + Long.toHexString(segment_end));
|
||||
|
||||
}
|
||||
|
||||
if (basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start > 0)
|
||||
out.write("0x" + Long.toHexString(basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start) + "\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
out.close();
|
||||
|
||||
}
|
||||
}
|
62
examples/afl_untracer/ida_get_patchpoints.py
Normal file
62
examples/afl_untracer/ida_get_patchpoints.py
Normal file
@ -0,0 +1,62 @@
|
||||
#
|
||||
# IDAPython script for IDA Pro
|
||||
# Slightly modified from https://github.com/googleprojectzero/p0tools/blob/master/TrapFuzz/findPatchPoints.py
|
||||
#
|
||||
|
||||
import idautils
|
||||
import idaapi
|
||||
import ida_nalt
|
||||
import idc
|
||||
|
||||
# See https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml
|
||||
|
||||
from os.path import expanduser
|
||||
home = expanduser("~")
|
||||
|
||||
patchpoints = set()
|
||||
|
||||
max_offset = 0
|
||||
for seg_ea in idautils.Segments():
|
||||
name = idc.get_segm_name(seg_ea)
|
||||
#print("Segment: " + name)
|
||||
if name != "__text" and name != ".text":
|
||||
continue
|
||||
|
||||
start = idc.get_segm_start(seg_ea)
|
||||
end = idc.get_segm_end(seg_ea)
|
||||
first = 0
|
||||
subtract_addr = 0
|
||||
#print("Start: " + hex(start) + " End: " + hex(end))
|
||||
for func_ea in idautils.Functions(start, end):
|
||||
f = idaapi.get_func(func_ea)
|
||||
if not f:
|
||||
continue
|
||||
for block in idaapi.FlowChart(f):
|
||||
if start <= block.start_ea < end:
|
||||
if first == 0:
|
||||
if block.start_ea >= 0x1000:
|
||||
subtract_addr = 0x1000
|
||||
first = 1
|
||||
|
||||
max_offset = max(max_offset, block.start_ea)
|
||||
patchpoints.add(block.start_ea - subtract_addr)
|
||||
#else:
|
||||
# print("Warning: broken CFG?")
|
||||
|
||||
# Round up max_offset to page size
|
||||
size = max_offset
|
||||
rem = size % 0x1000
|
||||
if rem != 0:
|
||||
size += 0x1000 - rem
|
||||
|
||||
print("Writing to " + home + "/Desktop/patches.txt")
|
||||
|
||||
with open(home + "/Desktop/patches.txt", "w") as f:
|
||||
f.write(ida_nalt.get_root_filename() + ':' + hex(size) + '\n')
|
||||
f.write('\n'.join(map(hex, sorted(patchpoints))))
|
||||
f.write('\n')
|
||||
|
||||
print("Done, found {} patchpoints".format(len(patchpoints)))
|
||||
|
||||
# For headless script running remove the comment from the next line
|
||||
#ida_pro.qexit()
|
35
examples/afl_untracer/libtestinstr.c
Normal file
35
examples/afl_untracer/libtestinstr.c
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
american fuzzy lop++ - a trivial program to test the build
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
void testinstr(char *buf, int len) {
|
||||
|
||||
if (len < 1) return;
|
||||
buf[len] = 0;
|
||||
|
||||
// we support three input cases
|
||||
if (buf[0] == '0')
|
||||
printf("Looks like a zero to me!\n");
|
||||
else if (buf[0] == '1')
|
||||
printf("Pretty sure that is a one!\n");
|
||||
else
|
||||
printf("Neither one or zero? How quaint!\n");
|
||||
|
||||
}
|
||||
|
23
examples/afl_untracer/patches.txt
Normal file
23
examples/afl_untracer/patches.txt
Normal file
@ -0,0 +1,23 @@
|
||||
libtestinstr.so:0x2000L
|
||||
0x1050L
|
||||
0x1063L
|
||||
0x106fL
|
||||
0x1078L
|
||||
0x1080L
|
||||
0x10a4L
|
||||
0x10b0L
|
||||
0x10b8L
|
||||
0x10c0L
|
||||
0x10c9L
|
||||
0x10d7L
|
||||
0x10e3L
|
||||
0x10f8L
|
||||
0x1100L
|
||||
0x1105L
|
||||
0x111aL
|
||||
0x1135L
|
||||
0x1143L
|
||||
0x114eL
|
||||
0x115cL
|
||||
0x116aL
|
||||
0x116bL
|
@ -44,10 +44,10 @@ M32FLAG=$(___M32FLAG)
|
||||
all: argvfuzz32.so argvfuzz64.so
|
||||
|
||||
argvfuzz32.so: argvfuzz.c
|
||||
-$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ || echo "argvfuzz32 build failure (that's fine)"
|
||||
-@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz32 build failure (that's fine)"
|
||||
|
||||
argvfuzz64.so: argvfuzz.c
|
||||
-$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ || echo "argvfuzz64 build failure (that's fine)"
|
||||
-@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz64 build failure (that's fine)"
|
||||
|
||||
install: argvfuzz32.so argvfuzz64.so
|
||||
install -d -m 755 $(DESTDIR)$(HELPER_PATH)/
|
||||
|
@ -6,6 +6,9 @@ See [docs/custom_mutators.md](../docs/custom_mutators.md) for more information
|
||||
Note that if you compile with python3.7 you must use python3 scripts, and if
|
||||
you use python2.7 to compile python2 scripts!
|
||||
|
||||
simple_example.c - most simplest example. generates a random sized buffer
|
||||
filled with 'A'
|
||||
|
||||
example.c - this is a simple example written in C and should be compiled to a
|
||||
shared library. Use make to compile it and produce libexamplemutator.so
|
||||
|
||||
|
@ -38,7 +38,7 @@ typedef struct my_mutator {
|
||||
BUF_VAR(u8, data);
|
||||
BUF_VAR(u8, havoc);
|
||||
BUF_VAR(u8, trim);
|
||||
BUF_VAR(u8, pre_save);
|
||||
BUF_VAR(u8, post_process);
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
@ -139,11 +139,12 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
* @return Size of the output buffer after processing or the needed amount.
|
||||
* A return of 0 indicates an error.
|
||||
*/
|
||||
size_t afl_custom_pre_save(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
uint8_t **out_buf) {
|
||||
size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf,
|
||||
size_t buf_size, uint8_t **out_buf) {
|
||||
|
||||
uint8_t *pre_save_buf = maybe_grow(BUF_PARAMS(data, pre_save), buf_size + 5);
|
||||
if (!pre_save_buf) {
|
||||
uint8_t *post_process_buf =
|
||||
maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5);
|
||||
if (!post_process_buf) {
|
||||
|
||||
perror("custom mutator realloc failed.");
|
||||
*out_buf = NULL;
|
||||
@ -151,14 +152,14 @@ size_t afl_custom_pre_save(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
|
||||
}
|
||||
|
||||
memcpy(pre_save_buf + 5, buf, buf_size);
|
||||
pre_save_buf[0] = 'A';
|
||||
pre_save_buf[1] = 'F';
|
||||
pre_save_buf[2] = 'L';
|
||||
pre_save_buf[3] = '+';
|
||||
pre_save_buf[4] = '+';
|
||||
memcpy(post_process_buf + 5, buf, buf_size);
|
||||
post_process_buf[0] = 'A';
|
||||
post_process_buf[1] = 'F';
|
||||
post_process_buf[2] = 'L';
|
||||
post_process_buf[3] = '+';
|
||||
post_process_buf[4] = '+';
|
||||
|
||||
*out_buf = pre_save_buf;
|
||||
*out_buf = post_process_buf;
|
||||
|
||||
return buf_size + 5;
|
||||
|
||||
@ -364,7 +365,7 @@ void afl_custom_queue_new_entry(my_mutator_t * data,
|
||||
*/
|
||||
void afl_custom_deinit(my_mutator_t *data) {
|
||||
|
||||
free(data->pre_save_buf);
|
||||
free(data->post_process_buf);
|
||||
free(data->havoc_buf);
|
||||
free(data->data_buf);
|
||||
free(data->fuzz_buf);
|
||||
|
@ -21,6 +21,7 @@ COMMANDS = [
|
||||
b"GET",
|
||||
b"PUT",
|
||||
b"DEL",
|
||||
b"AAAAAAAAAAAAAAAAA",
|
||||
]
|
||||
|
||||
|
||||
@ -119,7 +120,7 @@ def fuzz(buf, add_buf, max_size):
|
||||
#
|
||||
# return next_index
|
||||
#
|
||||
# def pre_save(buf):
|
||||
# def post_process(buf):
|
||||
# '''
|
||||
# Called just before the execution to write the test case in the format
|
||||
# expected by the target
|
||||
|
74
examples/custom_mutators/simple_example.c
Normal file
74
examples/custom_mutators/simple_example.c
Normal file
@ -0,0 +1,74 @@
|
||||
// This simple example just creates random buffer <= 100 filled with 'A'
|
||||
// needs -I /path/to/AFLplusplus/include
|
||||
#include "custom_mutator_helpers.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _FIXED_CHAR
|
||||
#define 0x41
|
||||
#endif
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
|
||||
// Reused buffers:
|
||||
BUF_VAR(u8, fuzz);
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
|
||||
|
||||
srand(seed);
|
||||
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||
if (!data) {
|
||||
|
||||
perror("afl_custom_init alloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
data->afl = afl;
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
u8 **out_buf, uint8_t *add_buf,
|
||||
size_t add_buf_size, // add_buf can be NULL
|
||||
size_t max_size) {
|
||||
|
||||
int size = (rand() % 100) + 1;
|
||||
if (size > max_size) size = max_size;
|
||||
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size);
|
||||
if (!mutated_out) {
|
||||
|
||||
*out_buf = NULL;
|
||||
perror("custom mutator allocation (maybe_grow)");
|
||||
return 0; /* afl-fuzz will very likely error out after this. */
|
||||
|
||||
}
|
||||
|
||||
memset(mutated_out, _FIXED_CHAR, size);
|
||||
|
||||
*out_buf = mutated_out;
|
||||
return size;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitialize everything
|
||||
*
|
||||
* @param data The data ptr from afl_custom_init
|
||||
*/
|
||||
void afl_custom_deinit(my_mutator_t *data) {
|
||||
|
||||
free(data->fuzz_buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
@ -1,159 +0,0 @@
|
||||
/*
|
||||
american fuzzy lop++ - postprocessor library example
|
||||
--------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
Edited by Dominik Maier, 2020
|
||||
|
||||
Copyright 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
|
||||
|
||||
Postprocessor libraries can be passed to afl-fuzz to perform final cleanup
|
||||
of any mutated test cases - for example, to fix up checksums in PNG files.
|
||||
|
||||
Please heed the following warnings:
|
||||
|
||||
1) In almost all cases, it is more productive to comment out checksum logic
|
||||
in the targeted binary (as shown in ../libpng_no_checksum/). One possible
|
||||
exception is the process of fuzzing binary-only software in QEMU mode.
|
||||
|
||||
2) The use of postprocessors for anything other than checksums is
|
||||
questionable and may cause more harm than good. AFL is normally pretty good
|
||||
about dealing with length fields, magic values, etc.
|
||||
|
||||
3) Postprocessors that do anything non-trivial must be extremely robust to
|
||||
gracefully handle malformed data and other error conditions - otherwise,
|
||||
they will crash and take afl-fuzz down with them. Be wary of reading past
|
||||
*len and of integer overflows when calculating file offsets.
|
||||
|
||||
In other words, THIS IS PROBABLY NOT WHAT YOU WANT - unless you really,
|
||||
honestly know what you're doing =)
|
||||
|
||||
With that out of the way: the postprocessor library is passed to afl-fuzz
|
||||
via AFL_POST_LIBRARY. The library must be compiled with:
|
||||
|
||||
gcc -shared -Wall -O3 post_library.so.c -o post_library.so
|
||||
|
||||
AFL will call the afl_postprocess() function for every mutated output buffer.
|
||||
From there, you have three choices:
|
||||
|
||||
1) If you don't want to modify the test case, simply set `*out_buf = in_buf`
|
||||
and return the original `len`.
|
||||
|
||||
2) If you want to skip this test case altogether and have AFL generate a
|
||||
new one, return 0 or set `*out_buf = NULL`.
|
||||
Use this sparingly - it's faster than running the target program
|
||||
with patently useless inputs, but still wastes CPU time.
|
||||
|
||||
3) If you want to modify the test case, allocate an appropriately-sized
|
||||
buffer, move the data into that buffer, make the necessary changes, and
|
||||
then return the new pointer as out_buf. Return an appropriate len
|
||||
afterwards.
|
||||
|
||||
Note that the buffer will *not* be freed for you. To avoid memory leaks,
|
||||
you need to free it or reuse it on subsequent calls (as shown below).
|
||||
|
||||
*** Feel free to reuse the original 'in_buf' BUFFER and return it. ***
|
||||
|
||||
Aight. The example below shows a simple postprocessor that tries to make
|
||||
sure that all input files start with "GIF89a".
|
||||
|
||||
PS. If you don't like C, you can try out the unix-based wrapper from
|
||||
Ben Nagy instead: https://github.com/bnagy/aflfix
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Header that must be present at the beginning of every test case: */
|
||||
|
||||
#define HEADER "GIF89a"
|
||||
|
||||
typedef struct post_state {
|
||||
|
||||
unsigned char *buf;
|
||||
size_t size;
|
||||
|
||||
} post_state_t;
|
||||
|
||||
void *afl_postprocess_init(void *afl) {
|
||||
|
||||
post_state_t *state = malloc(sizeof(post_state_t));
|
||||
if (!state) {
|
||||
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
state->buf = calloc(sizeof(unsigned char), 4096);
|
||||
if (!state->buf) { return NULL; }
|
||||
|
||||
return state;
|
||||
|
||||
}
|
||||
|
||||
/* The actual postprocessor routine called by afl-fuzz: */
|
||||
|
||||
size_t afl_postprocess(post_state_t *data, unsigned char *in_buf,
|
||||
unsigned int len, unsigned char **out_buf) {
|
||||
|
||||
/* Skip execution altogether for buffers shorter than 6 bytes (just to
|
||||
show how it's done). We can trust len to be sane. */
|
||||
|
||||
if (len < strlen(HEADER)) return 0;
|
||||
|
||||
/* Do nothing for buffers that already start with the expected header. */
|
||||
|
||||
if (!memcmp(in_buf, HEADER, strlen(HEADER))) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Allocate memory for new buffer, reusing previous allocation if
|
||||
possible. */
|
||||
|
||||
*out_buf = realloc(data->buf, len);
|
||||
|
||||
/* If we're out of memory, the most graceful thing to do is to return the
|
||||
original buffer and give up on modifying it. Let AFL handle OOM on its
|
||||
own later on. */
|
||||
|
||||
if (!*out_buf) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Copy the original data to the new location. */
|
||||
|
||||
memcpy(*out_buf, in_buf, len);
|
||||
|
||||
/* Insert the new header. */
|
||||
|
||||
memcpy(*out_buf, HEADER, strlen(HEADER));
|
||||
|
||||
/* Return the new len. It hasn't changed, so it's just len. */
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Gets called afterwards */
|
||||
void afl_postprocess_deinit(post_state_t *data) {
|
||||
|
||||
free(data->buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
american fuzzy lop++ - postprocessor for PNG
|
||||
------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
Adapted to the new API, 2020 by Dominik Maier
|
||||
|
||||
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
|
||||
|
||||
See post_library.so.c for a general discussion of how to implement
|
||||
postprocessors. This specific postprocessor attempts to fix up PNG
|
||||
checksums, providing a slightly more complicated example than found
|
||||
in post_library.so.c.
|
||||
|
||||
Compile with:
|
||||
|
||||
gcc -shared -Wall -O3 post_library_png.so.c -o post_library_png.so -lz
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/* A macro to round an integer up to 4 kB. */
|
||||
|
||||
#define UP4K(_i) ((((_i) >> 12) + 1) << 12)
|
||||
|
||||
typedef struct post_state {
|
||||
|
||||
unsigned char *buf;
|
||||
size_t size;
|
||||
|
||||
} post_state_t;
|
||||
|
||||
void *afl_postprocess_init(void *afl) {
|
||||
|
||||
post_state_t *state = malloc(sizeof(post_state_t));
|
||||
if (!state) {
|
||||
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
state->buf = calloc(sizeof(unsigned char), 4096);
|
||||
if (!state->buf) { return NULL; }
|
||||
|
||||
return state;
|
||||
|
||||
}
|
||||
|
||||
size_t afl_postprocess(post_state_t *data, const unsigned char *in_buf,
|
||||
unsigned int len, const unsigned char **out_buf) {
|
||||
|
||||
unsigned char *new_buf = (unsigned char *)in_buf;
|
||||
unsigned int pos = 8;
|
||||
|
||||
/* Don't do anything if there's not enough room for the PNG header
|
||||
(8 bytes). */
|
||||
|
||||
if (len < 8) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Minimum size of a zero-length PNG chunk is 12 bytes; if we
|
||||
don't have that, we can bail out. */
|
||||
|
||||
while (pos + 12 <= len) {
|
||||
|
||||
unsigned int chunk_len, real_cksum, file_cksum;
|
||||
|
||||
/* Chunk length is the first big-endian dword in the chunk. */
|
||||
|
||||
chunk_len = ntohl(*(uint32_t *)(in_buf + pos));
|
||||
|
||||
/* Bail out if chunk size is too big or goes past EOF. */
|
||||
|
||||
if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > len) break;
|
||||
|
||||
/* Chunk checksum is calculated for chunk ID (dword) and the actual
|
||||
payload. */
|
||||
|
||||
real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4));
|
||||
|
||||
/* The in-file checksum is the last dword past the chunk data. */
|
||||
|
||||
file_cksum = *(uint32_t *)(in_buf + pos + 8 + chunk_len);
|
||||
|
||||
/* If the checksums do not match, we need to fix the file. */
|
||||
|
||||
if (real_cksum != file_cksum) {
|
||||
|
||||
/* First modification? Make a copy of the input buffer. Round size
|
||||
up to 4 kB to minimize the number of reallocs needed. */
|
||||
|
||||
if (new_buf == in_buf) {
|
||||
|
||||
if (len <= data->size) {
|
||||
|
||||
new_buf = data->buf;
|
||||
|
||||
} else {
|
||||
|
||||
new_buf = realloc(data->buf, UP4K(len));
|
||||
if (!new_buf) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
data->buf = new_buf;
|
||||
data->size = UP4K(len);
|
||||
memcpy(new_buf, in_buf, len);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum;
|
||||
|
||||
}
|
||||
|
||||
/* Skip the entire chunk and move to the next one. */
|
||||
|
||||
pos += 12 + chunk_len;
|
||||
|
||||
}
|
||||
|
||||
*out_buf = new_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Gets called afterwards */
|
||||
void afl_postprocess_deinit(post_state_t *data) {
|
||||
|
||||
free(data->buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ int target_func(unsigned char *buf, int size) {
|
||||
}
|
||||
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
@ -47,10 +47,10 @@ M32FLAG=$(___M32FLAG)
|
||||
all: socketfuzz32.so socketfuzz64.so
|
||||
|
||||
socketfuzz32.so: socketfuzz.c
|
||||
-$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ || echo "socketfuzz32 build failure (that's fine)"
|
||||
-@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz32 build failure (that's fine)"
|
||||
|
||||
socketfuzz64.so: socketfuzz.c
|
||||
-$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ || echo "socketfuzz64 build failure (that's fine)"
|
||||
-@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz64 build failure (that's fine)"
|
||||
|
||||
install: socketfuzz32.so socketfuzz64.so
|
||||
install -d -m 755 $(DESTDIR)$(HELPER_PATH)/
|
||||
|
@ -68,8 +68,7 @@ LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-lrt)
|
||||
CFLAGS += $(CFLAGS_ADD)
|
||||
LDFLAGS += $(LDFLAGS_ADD)
|
||||
|
||||
PROGS = ../afl-gcc-fast ../afl-gcc-pass.so ../afl-gcc-rt.o
|
||||
|
||||
PROGS = ../afl-gcc-pass.so ../afl-gcc-fast ../afl-gcc-rt.o
|
||||
|
||||
all: test_shm test_deps $(PROGS) afl-gcc-fast.8 test_build all_done
|
||||
|
||||
|
@ -123,11 +123,17 @@ static void edit_params(u32 argc, char **argv) {
|
||||
u8 *alt_cxx = getenv("AFL_CXX");
|
||||
cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)AFL_GCC_CXX;
|
||||
|
||||
} else {
|
||||
} else if (!strcmp(name, "afl-gcc-fast")) {
|
||||
|
||||
u8 *alt_cc = getenv("AFL_CC");
|
||||
cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)AFL_GCC_CC;
|
||||
|
||||
} else {
|
||||
|
||||
fprintf(stderr, "Name of the binary: %s\n", argv[0]);
|
||||
FATAL(
|
||||
"Name of the binary is not a known name, expected afl-(gcc|g++)-fast");
|
||||
|
||||
}
|
||||
|
||||
char *fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path);
|
||||
|
@ -55,10 +55,10 @@
|
||||
/* clear helper macros AFL types pull in, which intervene with gcc-plugin
|
||||
* headers from GCC-8 */
|
||||
#ifdef likely
|
||||
#undef likely
|
||||
#undef likely
|
||||
#endif
|
||||
#ifdef unlikely
|
||||
#undef unlikely
|
||||
#undef unlikely
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@ -295,16 +295,16 @@ static unsigned int inline_instrument(function *fun) {
|
||||
update_stmt(g);
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
#if 0
|
||||
tree addr = build2(ADDR_EXPR, map_type, map_ptr, area_off);
|
||||
g = gimple_build_assign(map_ptr2, MODIFY_EXPR, addr);
|
||||
gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off
|
||||
update_stmt(g);
|
||||
#else
|
||||
#else
|
||||
g = gimple_build_assign(map_ptr2, PLUS_EXPR, map_ptr, area_off);
|
||||
gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off
|
||||
update_stmt(g);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// gimple_assign <mem_ref, _3, *p_6, NULL, NULL>
|
||||
tree tmp1 = create_tmp_var_raw(unsigned_char_type_node, "tmp1");
|
||||
|
@ -20,13 +20,13 @@
|
||||
*/
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "../config.h"
|
||||
#include "../types.h"
|
||||
|
||||
#ifdef USEMMAP
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
@ -404,9 +404,9 @@ static const u8 *main_payload_32 =
|
||||
recognize .string. */
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define CALL_L64(str) "call _" str "\n"
|
||||
#define CALL_L64(str) "call _" str "\n"
|
||||
#else
|
||||
#define CALL_L64(str) "call " str "@PLT\n"
|
||||
#define CALL_L64(str) "call " str "@PLT\n"
|
||||
#endif /* ^__APPLE__ */
|
||||
|
||||
static const u8 *main_payload_64 =
|
||||
@ -744,9 +744,9 @@ static const u8 *main_payload_64 =
|
||||
#ifdef __APPLE__
|
||||
|
||||
" .comm __afl_area_ptr, 8\n"
|
||||
#ifndef COVERAGE_ONLY
|
||||
#ifndef COVERAGE_ONLY
|
||||
" .comm __afl_prev_loc, 8\n"
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
" .comm __afl_fork_pid, 4\n"
|
||||
" .comm __afl_temp, 4\n"
|
||||
" .comm __afl_setup_failure, 1\n"
|
||||
@ -754,9 +754,9 @@ static const u8 *main_payload_64 =
|
||||
#else
|
||||
|
||||
" .lcomm __afl_area_ptr, 8\n"
|
||||
#ifndef COVERAGE_ONLY
|
||||
#ifndef COVERAGE_ONLY
|
||||
" .lcomm __afl_prev_loc, 8\n"
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
" .lcomm __afl_fork_pid, 4\n"
|
||||
" .lcomm __afl_temp, 4\n"
|
||||
" .lcomm __afl_setup_failure, 1\n"
|
||||
|
@ -31,12 +31,14 @@
|
||||
#define MESSAGES_TO_STDOUT
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE 1
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
@ -74,7 +76,7 @@
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
|
||||
defined(__NetBSD__) || defined(__DragonFly__)
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/sysctl.h>
|
||||
#endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */
|
||||
|
||||
/* For systems that have sched_setaffinity; right now just Linux, but one
|
||||
@ -82,31 +84,31 @@
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
|
||||
defined(__DragonFly__)
|
||||
#define HAVE_AFFINITY 1
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#include <sys/param.h>
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/cpuset.h>
|
||||
#endif
|
||||
#include <sys/user.h>
|
||||
#include <pthread.h>
|
||||
#include <pthread_np.h>
|
||||
#define cpu_set_t cpuset_t
|
||||
#elif defined(__NetBSD__)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#define HAVE_AFFINITY 1
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#include <sys/param.h>
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/cpuset.h>
|
||||
#endif
|
||||
#include <sys/user.h>
|
||||
#include <pthread.h>
|
||||
#include <pthread_np.h>
|
||||
#define cpu_set_t cpuset_t
|
||||
#elif defined(__NetBSD__)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif /* __linux__ */
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <TargetConditionals.h>
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#undef LIST_FOREACH /* clashes with FreeBSD */
|
||||
#include "list.h"
|
||||
#ifndef SIMPLE_FILES
|
||||
#define CASE_PREFIX "id:"
|
||||
#define CASE_PREFIX "id:"
|
||||
#else
|
||||
#define CASE_PREFIX "id_"
|
||||
#define CASE_PREFIX "id_"
|
||||
#endif /* ^!SIMPLE_FILES */
|
||||
|
||||
#define STAGE_BUF_SIZE (64) /* usable size for stage name buf in afl_state */
|
||||
@ -229,36 +231,36 @@ enum {
|
||||
/* Python stuff */
|
||||
#ifdef USE_PYTHON
|
||||
|
||||
// because Python sets stuff it should not ...
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#define _SAVE_POSIX_C_SOURCE _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#ifdef _XOPEN_SOURCE
|
||||
#define _SAVE_XOPEN_SOURCE _XOPEN_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
// because Python sets stuff it should not ...
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#define _SAVE_POSIX_C_SOURCE _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#ifdef _XOPEN_SOURCE
|
||||
#define _SAVE_XOPEN_SOURCE _XOPEN_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
#include <Python.h>
|
||||
|
||||
#ifdef _SAVE_POSIX_C_SOURCE
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#define _POSIX_C_SOURCE _SAVE_POSIX_C_SOURCE
|
||||
#endif
|
||||
#ifdef _SAVE_XOPEN_SOURCE
|
||||
#ifdef _XOPEN_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
#define _XOPEN_SOURCE _SAVE_XOPEN_SOURCE
|
||||
#endif
|
||||
#ifdef _SAVE_POSIX_C_SOURCE
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#define _POSIX_C_SOURCE _SAVE_POSIX_C_SOURCE
|
||||
#endif
|
||||
#ifdef _SAVE_XOPEN_SOURCE
|
||||
#ifdef _XOPEN_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
#define _XOPEN_SOURCE _SAVE_XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
enum {
|
||||
|
||||
/* 00 */ PY_FUNC_INIT,
|
||||
/* 01 */ PY_FUNC_FUZZ,
|
||||
/* 02 */ PY_FUNC_PRE_SAVE,
|
||||
/* 02 */ PY_FUNC_POST_PROCESS,
|
||||
/* 03 */ PY_FUNC_INIT_TRIM,
|
||||
/* 04 */ PY_FUNC_POST_TRIM,
|
||||
/* 05 */ PY_FUNC_TRIM,
|
||||
@ -281,8 +283,8 @@ typedef struct py_mutator {
|
||||
u8 * fuzz_buf;
|
||||
size_t fuzz_size;
|
||||
|
||||
u8 * pre_save_buf;
|
||||
size_t pre_save_size;
|
||||
u8 * post_process_buf;
|
||||
size_t post_process_size;
|
||||
|
||||
u8 * trim_buf;
|
||||
size_t trim_size;
|
||||
@ -321,9 +323,8 @@ typedef struct afl_env_vars {
|
||||
afl_bench_until_crash, afl_debug_child_output, afl_autoresume,
|
||||
afl_cal_fast;
|
||||
|
||||
u8 *afl_tmpdir, *afl_post_library, *afl_custom_mutator_library,
|
||||
*afl_python_module, *afl_path, *afl_hang_tmout, *afl_skip_crashes,
|
||||
*afl_preload;
|
||||
u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path,
|
||||
*afl_hang_tmout, *afl_skip_crashes, *afl_preload;
|
||||
|
||||
} afl_env_vars_t;
|
||||
|
||||
@ -405,7 +406,9 @@ typedef struct afl_state {
|
||||
no_unlink, /* do not unlink cur_input */
|
||||
debug, /* Debug mode */
|
||||
custom_only, /* Custom mutator only mode */
|
||||
python_only; /* Python-only mode */
|
||||
python_only, /* Python-only mode */
|
||||
is_master, /* if this is a master */
|
||||
is_slave; /* if this is a slave */
|
||||
|
||||
u32 stats_update_freq; /* Stats update frequency (execs) */
|
||||
|
||||
@ -416,7 +419,6 @@ typedef struct afl_state {
|
||||
size_t (*radamsa_mutate_ptr)(u8 *, size_t, u8 *, size_t, u32);
|
||||
|
||||
u8 skip_deterministic, /* Skip deterministic stages? */
|
||||
force_deterministic, /* Force deterministic stages? */
|
||||
use_splicing, /* Recombine input files? */
|
||||
dumb_mode, /* Run in non-instrumented mode? */
|
||||
score_changed, /* Scoring for favorites changed? */
|
||||
@ -543,11 +545,9 @@ typedef struct afl_state {
|
||||
struct extra_data *a_extras; /* Automatically selected extras */
|
||||
u32 a_extras_cnt; /* Total number of tokens available */
|
||||
|
||||
/* afl_postprocess API */
|
||||
void *(*post_init)(struct afl_state *afl);
|
||||
size_t (*post_handler)(void *data, u8 *buf, u32 len, u8 **out_buf);
|
||||
void *(*post_deinit)(void *data);
|
||||
void *post_data;
|
||||
/* afl_postprocess API - Now supported via custom mutators */
|
||||
|
||||
struct custom_mutator *post_library_mutator;
|
||||
|
||||
/* CmpLog */
|
||||
|
||||
@ -607,23 +607,23 @@ typedef struct afl_state {
|
||||
|
||||
u8 * ex_buf;
|
||||
size_t ex_size;
|
||||
u32 custom_mutators_count;
|
||||
|
||||
/* this is a fixed buffer of size map_size that can be used by any function if they do not call another function */
|
||||
u8 * map_tmp_buf;
|
||||
list_t custom_mutator_list;
|
||||
|
||||
/* this is a fixed buffer of size map_size that can be used by any function if
|
||||
* they do not call another function */
|
||||
u8 *map_tmp_buf;
|
||||
|
||||
} afl_state_t;
|
||||
|
||||
/* A global pointer to all instances is needed (for now) for signals to arrive
|
||||
*/
|
||||
|
||||
extern list_t afl_states;
|
||||
|
||||
struct custom_mutator {
|
||||
|
||||
const char *name;
|
||||
void * dh;
|
||||
u8 * pre_save_buf;
|
||||
size_t pre_save_size;
|
||||
u8 * post_process_buf;
|
||||
size_t post_process_size;
|
||||
u8 stacked_custom_prob, stacked_custom;
|
||||
|
||||
void *data; /* custom mutator data ptr */
|
||||
|
||||
@ -673,8 +673,8 @@ struct custom_mutator {
|
||||
* It can chose to alter buf in-place, if the space is large enough.
|
||||
* @return Size of the output buffer.
|
||||
*/
|
||||
size_t (*afl_custom_pre_save)(void *data, u8 *buf, size_t buf_size,
|
||||
u8 **out_buf);
|
||||
size_t (*afl_custom_post_process)(void *data, u8 *buf, size_t buf_size,
|
||||
u8 **out_buf);
|
||||
|
||||
/**
|
||||
* This method is called at the start of each trimming operation and receives
|
||||
@ -799,22 +799,31 @@ struct custom_mutator {
|
||||
|
||||
void afl_state_init(afl_state_t *, uint32_t map_size);
|
||||
void afl_state_deinit(afl_state_t *);
|
||||
|
||||
/* Set stop_soon flag on all childs, kill all childs */
|
||||
void afl_states_stop(void);
|
||||
/* Set clear_screen flag on all states */
|
||||
void afl_states_clear_screen(void);
|
||||
/* Sets the skip flag on all states */
|
||||
void afl_states_request_skip(void);
|
||||
|
||||
void read_afl_environment(afl_state_t *, char **);
|
||||
|
||||
/**** Prototypes ****/
|
||||
|
||||
/* Custom mutators */
|
||||
void setup_custom_mutator(afl_state_t *);
|
||||
void destroy_custom_mutator(afl_state_t *);
|
||||
u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf);
|
||||
void setup_custom_mutators(afl_state_t *);
|
||||
void destroy_custom_mutators(afl_state_t *);
|
||||
u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf,
|
||||
struct custom_mutator *mutator);
|
||||
|
||||
/* Python */
|
||||
#ifdef USE_PYTHON
|
||||
|
||||
void load_custom_mutator_py(afl_state_t *, char *);
|
||||
void finalize_py_module(void *);
|
||||
struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *);
|
||||
void finalize_py_module(void *);
|
||||
|
||||
size_t pre_save_py(void *, u8 *, size_t, u8 **);
|
||||
size_t post_process_py(void *, u8 *, size_t, u8 **);
|
||||
s32 init_trim_py(void *, u8 *, size_t);
|
||||
s32 post_trim_py(void *, u8);
|
||||
size_t trim_py(void *, u8 **);
|
||||
@ -956,7 +965,7 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) {
|
||||
|
||||
static inline u32 get_rand_seed(afl_state_t *afl) {
|
||||
|
||||
if (unlikely(afl->fixed_seed)) return (u32)afl->init_seed;
|
||||
if (unlikely(afl->fixed_seed)) { return (u32)afl->init_seed; }
|
||||
return afl->rand_seed[0];
|
||||
|
||||
}
|
||||
@ -967,8 +976,12 @@ static inline u32 get_rand_seed(afl_state_t *afl) {
|
||||
static inline u64 next_p2(u64 val) {
|
||||
|
||||
u64 ret = 1;
|
||||
while (val > ret)
|
||||
while (val > ret) {
|
||||
|
||||
ret <<= 1;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@ -41,44 +41,44 @@
|
||||
// Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators
|
||||
|
||||
#ifndef _WANT_ORIGINAL_AFL_ALLOC
|
||||
// afl++ stuff without memory corruption checks - for speed
|
||||
// afl++ stuff without memory corruption checks - for speed
|
||||
|
||||
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
|
||||
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
|
||||
|
||||
#define alloc_printf(_str...) \
|
||||
({ \
|
||||
\
|
||||
u8 *_tmp; \
|
||||
s32 _len = snprintf(NULL, 0, _str); \
|
||||
if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
|
||||
_tmp = ck_alloc(_len + 1); \
|
||||
snprintf((char *)_tmp, _len + 1, _str); \
|
||||
_tmp; \
|
||||
\
|
||||
})
|
||||
#define alloc_printf(_str...) \
|
||||
({ \
|
||||
\
|
||||
u8 *_tmp; \
|
||||
s32 _len = snprintf(NULL, 0, _str); \
|
||||
if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
|
||||
_tmp = ck_alloc(_len + 1); \
|
||||
snprintf((char *)_tmp, _len + 1, _str); \
|
||||
_tmp; \
|
||||
\
|
||||
})
|
||||
|
||||
/* Macro to enforce allocation limits as a last-resort defense against
|
||||
integer overflows. */
|
||||
/* Macro to enforce allocation limits as a last-resort defense against
|
||||
integer overflows. */
|
||||
|
||||
#define ALLOC_CHECK_SIZE(_s) \
|
||||
do { \
|
||||
\
|
||||
if ((_s) > MAX_ALLOC) ABORT("Bad alloc request: %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
#define ALLOC_CHECK_SIZE(_s) \
|
||||
do { \
|
||||
\
|
||||
if ((_s) > MAX_ALLOC) ABORT("Bad alloc request: %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Macro to check malloc() failures and the like. */
|
||||
/* Macro to check malloc() failures and the like. */
|
||||
|
||||
#define ALLOC_CHECK_RESULT(_r, _s) \
|
||||
do { \
|
||||
\
|
||||
if (!(_r)) ABORT("Out of memory: can't allocate %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
#define ALLOC_CHECK_RESULT(_r, _s) \
|
||||
do { \
|
||||
\
|
||||
if (!(_r)) ABORT("Out of memory: can't allocate %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Allocator increments for ck_realloc_block(). */
|
||||
/* Allocator increments for ck_realloc_block(). */
|
||||
|
||||
#define ALLOC_BLK_INC 256
|
||||
#define ALLOC_BLK_INC 256
|
||||
|
||||
/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized
|
||||
requests. */
|
||||
@ -87,7 +87,7 @@ static inline void *DFL_ck_alloc_nozero(u32 size) {
|
||||
|
||||
void *ret;
|
||||
|
||||
if (!size) return NULL;
|
||||
if (!size) { return NULL; }
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = malloc(size);
|
||||
@ -103,7 +103,7 @@ static inline void *DFL_ck_alloc(u32 size) {
|
||||
|
||||
void *mem;
|
||||
|
||||
if (!size) return NULL;
|
||||
if (!size) { return NULL; }
|
||||
mem = DFL_ck_alloc_nozero(size);
|
||||
|
||||
return memset(mem, 0, size);
|
||||
@ -115,7 +115,7 @@ static inline void *DFL_ck_alloc(u32 size) {
|
||||
|
||||
static inline void DFL_ck_free(void *mem) {
|
||||
|
||||
if (!mem) return;
|
||||
if (!mem) { return; }
|
||||
|
||||
free(mem);
|
||||
|
||||
@ -165,7 +165,7 @@ static inline u8 *DFL_ck_strdup(u8 *str) {
|
||||
u8 *ret;
|
||||
u32 size;
|
||||
|
||||
if (!str) return NULL;
|
||||
if (!str) { return NULL; }
|
||||
|
||||
size = strlen((char *)str) + 1;
|
||||
|
||||
@ -184,7 +184,7 @@ static inline void *DFL_ck_memdup(void *mem, u32 size) {
|
||||
|
||||
void *ret;
|
||||
|
||||
if (!mem || !size) return NULL;
|
||||
if (!mem || !size) { return NULL; }
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = malloc(size);
|
||||
@ -201,7 +201,7 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) {
|
||||
|
||||
u8 *ret;
|
||||
|
||||
if (!mem || !size) return NULL;
|
||||
if (!mem || !size) { return NULL; }
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = malloc(size + 1);
|
||||
@ -214,105 +214,104 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) {
|
||||
|
||||
}
|
||||
|
||||
/* In non-debug mode, we just do straightforward aliasing of the above functions
|
||||
to user-visible names such as ck_alloc(). */
|
||||
/* In non-debug mode, we just do straightforward aliasing of the above
|
||||
functions to user-visible names such as ck_alloc(). */
|
||||
|
||||
#define ck_alloc DFL_ck_alloc
|
||||
#define ck_alloc_nozero DFL_ck_alloc_nozero
|
||||
#define ck_realloc DFL_ck_realloc
|
||||
#define ck_realloc_block DFL_ck_realloc_block
|
||||
#define ck_strdup DFL_ck_strdup
|
||||
#define ck_memdup DFL_ck_memdup
|
||||
#define ck_memdup_str DFL_ck_memdup_str
|
||||
#define ck_free DFL_ck_free
|
||||
#define ck_alloc DFL_ck_alloc
|
||||
#define ck_alloc_nozero DFL_ck_alloc_nozero
|
||||
#define ck_realloc DFL_ck_realloc
|
||||
#define ck_realloc_block DFL_ck_realloc_block
|
||||
#define ck_strdup DFL_ck_strdup
|
||||
#define ck_memdup DFL_ck_memdup
|
||||
#define ck_memdup_str DFL_ck_memdup_str
|
||||
#define ck_free DFL_ck_free
|
||||
|
||||
#define alloc_report()
|
||||
#define alloc_report()
|
||||
|
||||
#else
|
||||
// This is the original alloc-inl of stock afl
|
||||
// This is the original alloc-inl of stock afl
|
||||
|
||||
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
|
||||
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
|
||||
|
||||
#define alloc_printf(_str...) \
|
||||
({ \
|
||||
\
|
||||
u8 *_tmp; \
|
||||
s32 _len = snprintf(NULL, 0, _str); \
|
||||
if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
|
||||
_tmp = ck_alloc(_len + 1); \
|
||||
snprintf((char *)_tmp, _len + 1, _str); \
|
||||
_tmp; \
|
||||
\
|
||||
})
|
||||
#define alloc_printf(_str...) \
|
||||
({ \
|
||||
\
|
||||
u8 *_tmp; \
|
||||
s32 _len = snprintf(NULL, 0, _str); \
|
||||
if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
|
||||
_tmp = ck_alloc(_len + 1); \
|
||||
snprintf((char *)_tmp, _len + 1, _str); \
|
||||
_tmp; \
|
||||
\
|
||||
})
|
||||
|
||||
/* Macro to enforce allocation limits as a last-resort defense against
|
||||
integer overflows. */
|
||||
/* Macro to enforce allocation limits as a last-resort defense against
|
||||
integer overflows. */
|
||||
#define ALLOC_CHECK_SIZE(_s) \
|
||||
do { \
|
||||
\
|
||||
if ((_s) > MAX_ALLOC) ABORT("Bad alloc request: %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define ALLOC_CHECK_SIZE(_s) \
|
||||
do { \
|
||||
\
|
||||
if ((_s) > MAX_ALLOC) ABORT("Bad alloc request: %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
/* Macro to check malloc() failures and the like. */
|
||||
|
||||
/* Macro to check malloc() failures and the like. */
|
||||
#define ALLOC_CHECK_RESULT(_r, _s) \
|
||||
do { \
|
||||
\
|
||||
if (!(_r)) ABORT("Out of memory: can't allocate %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define ALLOC_CHECK_RESULT(_r, _s) \
|
||||
do { \
|
||||
\
|
||||
if (!(_r)) ABORT("Out of memory: can't allocate %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
/* Magic tokens used to mark used / freed chunks. */
|
||||
|
||||
/* Magic tokens used to mark used / freed chunks. */
|
||||
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */
|
||||
#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */
|
||||
#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
|
||||
|
||||
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */
|
||||
#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */
|
||||
#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
|
||||
/* Positions of guard tokens in relation to the user-visible pointer. */
|
||||
|
||||
/* Positions of guard tokens in relation to the user-visible pointer. */
|
||||
#define ALLOC_C1(_ptr) (((u32 *)(_ptr))[-2])
|
||||
#define ALLOC_S(_ptr) (((u32 *)(_ptr))[-1])
|
||||
#define ALLOC_C2(_ptr) (((u8 *)(_ptr))[ALLOC_S(_ptr)])
|
||||
|
||||
#define ALLOC_C1(_ptr) (((u32 *)(_ptr))[-2])
|
||||
#define ALLOC_S(_ptr) (((u32 *)(_ptr))[-1])
|
||||
#define ALLOC_C2(_ptr) (((u8 *)(_ptr))[ALLOC_S(_ptr)])
|
||||
#define ALLOC_OFF_HEAD 8
|
||||
#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1)
|
||||
|
||||
#define ALLOC_OFF_HEAD 8
|
||||
#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1)
|
||||
/* Allocator increments for ck_realloc_block(). */
|
||||
|
||||
/* Allocator increments for ck_realloc_block(). */
|
||||
#define ALLOC_BLK_INC 256
|
||||
|
||||
#define ALLOC_BLK_INC 256
|
||||
/* Sanity-checking macros for pointers. */
|
||||
|
||||
/* Sanity-checking macros for pointers. */
|
||||
#define CHECK_PTR(_p) \
|
||||
do { \
|
||||
\
|
||||
if (_p) { \
|
||||
\
|
||||
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) { \
|
||||
\
|
||||
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
||||
ABORT("Use after free."); \
|
||||
else \
|
||||
ABORT("Corrupted head alloc canary."); \
|
||||
\
|
||||
} \
|
||||
if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \
|
||||
ABORT("Corrupted tail alloc canary."); \
|
||||
\
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define CHECK_PTR(_p) \
|
||||
do { \
|
||||
\
|
||||
if (_p) { \
|
||||
\
|
||||
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) { \
|
||||
\
|
||||
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
||||
ABORT("Use after free."); \
|
||||
else \
|
||||
ABORT("Corrupted head alloc canary."); \
|
||||
\
|
||||
} \
|
||||
if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \
|
||||
ABORT("Corrupted tail alloc canary."); \
|
||||
\
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define CHECK_PTR_EXPR(_p) \
|
||||
({ \
|
||||
\
|
||||
typeof(_p) _tmp = (_p); \
|
||||
CHECK_PTR(_tmp); \
|
||||
_tmp; \
|
||||
\
|
||||
})
|
||||
#define CHECK_PTR_EXPR(_p) \
|
||||
({ \
|
||||
\
|
||||
typeof(_p) _tmp = (_p); \
|
||||
CHECK_PTR(_tmp); \
|
||||
_tmp; \
|
||||
\
|
||||
})
|
||||
|
||||
/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized
|
||||
requests. */
|
||||
@ -358,13 +357,12 @@ static inline void DFL_ck_free(void *mem) {
|
||||
if (!mem) return;
|
||||
|
||||
CHECK_PTR(mem);
|
||||
|
||||
#ifdef DEBUG_BUILD
|
||||
#ifdef DEBUG_BUILD
|
||||
|
||||
/* Catch pointer issues sooner. */
|
||||
memset(mem, 0xFF, ALLOC_S(mem));
|
||||
|
||||
#endif /* DEBUG_BUILD */
|
||||
#endif /* DEBUG_BUILD */
|
||||
|
||||
ALLOC_C1(mem) = ALLOC_MAGIC_F;
|
||||
|
||||
@ -379,7 +377,7 @@ static inline void DFL_ck_free(void *mem) {
|
||||
static inline void *DFL_ck_realloc(void *orig, u32 size) {
|
||||
|
||||
void *ret;
|
||||
u32 old_size = 0;
|
||||
u32 old_size = 0;
|
||||
|
||||
if (!size) {
|
||||
|
||||
@ -392,9 +390,9 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) {
|
||||
|
||||
CHECK_PTR(orig);
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
#ifndef DEBUG_BUILD
|
||||
ALLOC_C1(orig) = ALLOC_MAGIC_F;
|
||||
#endif /* !DEBUG_BUILD */
|
||||
#endif /* !DEBUG_BUILD */
|
||||
|
||||
old_size = ALLOC_S(orig);
|
||||
orig -= ALLOC_OFF_HEAD;
|
||||
@ -405,12 +403,12 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) {
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
#ifndef DEBUG_BUILD
|
||||
|
||||
ret = realloc(orig, size + ALLOC_OFF_TOTAL);
|
||||
ALLOC_CHECK_RESULT(ret, size);
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
/* Catch pointer issues sooner: force relocation and make sure that the
|
||||
original buffer is wiped. */
|
||||
@ -429,7 +427,7 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
|
||||
ret += ALLOC_OFF_HEAD;
|
||||
|
||||
@ -448,7 +446,7 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) {
|
||||
|
||||
static inline void *DFL_ck_realloc_block(void *orig, u32 size) {
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
#ifndef DEBUG_BUILD
|
||||
|
||||
if (orig) {
|
||||
|
||||
@ -460,7 +458,7 @@ static inline void *DFL_ck_realloc_block(void *orig, u32 size) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* !DEBUG_BUILD */
|
||||
#endif /* !DEBUG_BUILD */
|
||||
|
||||
return DFL_ck_realloc(orig, size);
|
||||
|
||||
@ -471,7 +469,7 @@ static inline void *DFL_ck_realloc_block(void *orig, u32 size) {
|
||||
static inline u8 *DFL_ck_strdup(u8 *str) {
|
||||
|
||||
void *ret;
|
||||
u32 size;
|
||||
u32 size;
|
||||
|
||||
if (!str) return NULL;
|
||||
|
||||
@ -540,30 +538,30 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) {
|
||||
|
||||
}
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
#ifndef DEBUG_BUILD
|
||||
|
||||
/* In non-debug mode, we just do straightforward aliasing of the above functions
|
||||
to user-visible names such as ck_alloc(). */
|
||||
/* In non-debug mode, we just do straightforward aliasing of the above
|
||||
functions to user-visible names such as ck_alloc(). */
|
||||
|
||||
#define ck_alloc DFL_ck_alloc
|
||||
#define ck_alloc_nozero DFL_ck_alloc_nozero
|
||||
#define ck_realloc DFL_ck_realloc
|
||||
#define ck_realloc_block DFL_ck_realloc_block
|
||||
#define ck_strdup DFL_ck_strdup
|
||||
#define ck_memdup DFL_ck_memdup
|
||||
#define ck_memdup_str DFL_ck_memdup_str
|
||||
#define ck_free DFL_ck_free
|
||||
#define ck_alloc DFL_ck_alloc
|
||||
#define ck_alloc_nozero DFL_ck_alloc_nozero
|
||||
#define ck_realloc DFL_ck_realloc
|
||||
#define ck_realloc_block DFL_ck_realloc_block
|
||||
#define ck_strdup DFL_ck_strdup
|
||||
#define ck_memdup DFL_ck_memdup
|
||||
#define ck_memdup_str DFL_ck_memdup_str
|
||||
#define ck_free DFL_ck_free
|
||||
|
||||
#define alloc_report()
|
||||
#define alloc_report()
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
/* In debugging mode, we also track allocations to detect memory leaks, and the
|
||||
flow goes through one more layer of indirection. */
|
||||
/* In debugging mode, we also track allocations to detect memory leaks, and
|
||||
the flow goes through one more layer of indirection. */
|
||||
|
||||
/* Alloc tracking data structures: */
|
||||
/* Alloc tracking data structures: */
|
||||
|
||||
#define ALLOC_BUCKETS 4096
|
||||
#define ALLOC_BUCKETS 4096
|
||||
|
||||
struct TRK_obj {
|
||||
|
||||
@ -573,25 +571,25 @@ struct TRK_obj {
|
||||
|
||||
};
|
||||
|
||||
#ifdef AFL_MAIN
|
||||
#ifdef AFL_MAIN
|
||||
|
||||
struct TRK_obj *TRK[ALLOC_BUCKETS];
|
||||
u32 TRK_cnt[ALLOC_BUCKETS];
|
||||
|
||||
#define alloc_report() TRK_report()
|
||||
#define alloc_report() TRK_report()
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
extern struct TRK_obj *TRK[ALLOC_BUCKETS];
|
||||
extern u32 TRK_cnt[ALLOC_BUCKETS];
|
||||
|
||||
#define alloc_report()
|
||||
#define alloc_report()
|
||||
|
||||
#endif /* ^AFL_MAIN */
|
||||
#endif /* ^AFL_MAIN */
|
||||
|
||||
/* Bucket-assigning function for a given pointer: */
|
||||
/* Bucket-assigning function for a given pointer: */
|
||||
|
||||
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
|
||||
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
|
||||
|
||||
/* Add a new entry to the list of allocated objects. */
|
||||
|
||||
@ -741,29 +739,30 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func,
|
||||
|
||||
}
|
||||
|
||||
/* Aliasing user-facing names to tracking functions: */
|
||||
/* Aliasing user-facing names to tracking functions: */
|
||||
|
||||
#define ck_alloc(_p1) TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_alloc(_p1) TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_alloc_nozero(_p1) TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_alloc_nozero(_p1) \
|
||||
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_realloc(_p1, _p2) \
|
||||
TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_realloc(_p1, _p2) \
|
||||
TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_realloc_block(_p1, _p2) \
|
||||
TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_realloc_block(_p1, _p2) \
|
||||
TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_strdup(_p1) TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_strdup(_p1) TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_memdup(_p1, _p2) \
|
||||
TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_memdup(_p1, _p2) \
|
||||
TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_memdup_str(_p1, _p2) \
|
||||
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_memdup_str(_p1, _p2) \
|
||||
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
|
||||
#endif /* _WANT_ORIGINAL_AFL_ALLOC */
|
||||
|
||||
@ -772,8 +771,12 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func,
|
||||
*/
|
||||
static inline size_t next_pow2(size_t in) {
|
||||
|
||||
if (in == 0 || in > (size_t)-1)
|
||||
if (in == 0 || in > (size_t)-1) {
|
||||
|
||||
return 0; /* avoid undefined behaviour under-/overflow */
|
||||
|
||||
}
|
||||
|
||||
size_t out = in - 1;
|
||||
out |= out >> 1;
|
||||
out |= out >> 2;
|
||||
@ -794,10 +797,10 @@ static inline size_t next_pow2(size_t in) {
|
||||
static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
|
||||
|
||||
/* No need to realloc */
|
||||
if (likely(size_needed && *size >= size_needed)) return *buf;
|
||||
if (likely(size_needed && *size >= size_needed)) { return *buf; }
|
||||
|
||||
/* No initial size was set */
|
||||
if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE;
|
||||
if (size_needed < INITIAL_GROWTH_SIZE) { size_needed = INITIAL_GROWTH_SIZE; }
|
||||
|
||||
/* grow exponentially */
|
||||
size_t next_size = next_pow2(size_needed);
|
||||
@ -824,13 +827,13 @@ static inline void *ck_maybe_grow(void **buf, size_t *size,
|
||||
size_t size_needed) {
|
||||
|
||||
/* Oops. found a bug? */
|
||||
if (unlikely(size_needed < 1)) FATAL("cannot grow to non-positive size");
|
||||
if (unlikely(size_needed < 1)) { FATAL("cannot grow to non-positive size"); }
|
||||
|
||||
/* No need to realloc */
|
||||
if (likely(*size >= size_needed)) return *buf;
|
||||
if (likely(*size >= size_needed)) { return *buf; }
|
||||
|
||||
/* No initial size was set */
|
||||
if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE;
|
||||
if (size_needed < INITIAL_GROWTH_SIZE) { size_needed = INITIAL_GROWTH_SIZE; }
|
||||
|
||||
/* grow exponentially */
|
||||
size_t next_size = next_pow2(size_needed);
|
||||
|
@ -28,26 +28,27 @@
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/ashmem.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/ashmem.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#if __ANDROID_API__ >= 26
|
||||
#define shmat bionic_shmat
|
||||
#define shmctl bionic_shmctl
|
||||
#define shmdt bionic_shmdt
|
||||
#define shmget bionic_shmget
|
||||
#endif
|
||||
#include <sys/shm.h>
|
||||
#undef shmat
|
||||
#undef shmctl
|
||||
#undef shmdt
|
||||
#undef shmget
|
||||
#include <stdio.h>
|
||||
#if __ANDROID_API__ >= 26
|
||||
#define shmat bionic_shmat
|
||||
#define shmctl bionic_shmctl
|
||||
#define shmdt bionic_shmdt
|
||||
#define shmget bionic_shmget
|
||||
#endif
|
||||
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
#include <sys/shm.h>
|
||||
#undef shmat
|
||||
#undef shmctl
|
||||
#undef shmdt
|
||||
#undef shmget
|
||||
#include <stdio.h>
|
||||
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
|
||||
static inline int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf) {
|
||||
|
||||
@ -105,7 +106,7 @@ static inline void *shmat(int __shmid, const void *__shmaddr, int __shmflg) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* __ANDROID__ */
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -115,7 +115,7 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms);
|
||||
u32 read_timed(s32 fd, void *buf, size_t len, u32 timeout_ms,
|
||||
volatile u8 *stop_soon_p);
|
||||
|
||||
u32 get_map_size();
|
||||
u32 get_map_size(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
/* Version string: */
|
||||
|
||||
// c = release, d = volatile github dev, e = experimental branch
|
||||
#define VERSION "++2.64c"
|
||||
#define VERSION "++2.65c"
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
@ -49,7 +49,7 @@
|
||||
/* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */
|
||||
|
||||
#ifndef ANDROID_DISABLE_FANCY // Fancy boxes are ugly from adb
|
||||
#define FANCY_BOXES
|
||||
#define FANCY_BOXES
|
||||
#endif
|
||||
|
||||
/* Default timeout for fuzzed code (milliseconds). This is the upper bound,
|
||||
@ -63,20 +63,20 @@
|
||||
|
||||
/* 64bit arch MACRO */
|
||||
#if (defined(__x86_64__) || defined(__arm64__) || defined(__aarch64__))
|
||||
#define WORD_SIZE_64 1
|
||||
#define WORD_SIZE_64 1
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
#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): */
|
||||
|
||||
@ -395,19 +395,10 @@
|
||||
|
||||
/* for *BSD: use ARC4RANDOM and save a file descriptor */
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
#define HAVE_ARC4RANDOM 1
|
||||
#endif
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
#define HAVE_ARC4RANDOM 1
|
||||
#endif
|
||||
#endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */
|
||||
|
||||
/* Extended forkserver option values */
|
||||
|
||||
#define FS_OPT_ENABLED 0x8f000001
|
||||
#define FS_OPT_MAPSIZE 0x40000000
|
||||
#define FS_OPT_SNAPSHOT 0x20000000
|
||||
#define FS_OPT_AUTODICT 0x10000000
|
||||
#define FS_OPT_GET_MAPSIZE(x) (((x & 0x00fffffe) >> 1) + 1)
|
||||
#define FS_OPT_SET_MAPSIZE(x) (x <= 1 || x > 0x1000000 ? 0 : ((x - 1) << 1))
|
||||
|
||||
#endif /* ! _HAVE_CONFIG_H */
|
||||
|
||||
|
200
include/debug.h
200
include/debug.h
@ -30,7 +30,7 @@
|
||||
|
||||
/* __FUNCTION__ is non-iso */
|
||||
#ifdef __func__
|
||||
#define __FUNCTION__ __func__
|
||||
#define __FUNCTION__ __func__
|
||||
#endif
|
||||
|
||||
/*******************
|
||||
@ -38,82 +38,82 @@
|
||||
*******************/
|
||||
|
||||
#ifndef MESSAGES_TO_STDOUT
|
||||
#define MESSAGES_TO_STDOUT
|
||||
#define MESSAGES_TO_STDOUT
|
||||
#endif
|
||||
|
||||
#ifdef USE_COLOR
|
||||
|
||||
#define cBLK "\x1b[0;30m"
|
||||
#define cRED "\x1b[0;31m"
|
||||
#define cGRN "\x1b[0;32m"
|
||||
#define cBRN "\x1b[0;33m"
|
||||
#define cBLU "\x1b[0;34m"
|
||||
#define cMGN "\x1b[0;35m"
|
||||
#define cCYA "\x1b[0;36m"
|
||||
#define cLGR "\x1b[0;37m"
|
||||
#define cGRA "\x1b[1;90m"
|
||||
#define cLRD "\x1b[1;91m"
|
||||
#define cLGN "\x1b[1;92m"
|
||||
#define cYEL "\x1b[1;93m"
|
||||
#define cLBL "\x1b[1;94m"
|
||||
#define cPIN "\x1b[1;95m"
|
||||
#define cLCY "\x1b[1;96m"
|
||||
#define cBRI "\x1b[1;97m"
|
||||
#define cRST "\x1b[0m"
|
||||
#define cBLK "\x1b[0;30m"
|
||||
#define cRED "\x1b[0;31m"
|
||||
#define cGRN "\x1b[0;32m"
|
||||
#define cBRN "\x1b[0;33m"
|
||||
#define cBLU "\x1b[0;34m"
|
||||
#define cMGN "\x1b[0;35m"
|
||||
#define cCYA "\x1b[0;36m"
|
||||
#define cLGR "\x1b[0;37m"
|
||||
#define cGRA "\x1b[1;90m"
|
||||
#define cLRD "\x1b[1;91m"
|
||||
#define cLGN "\x1b[1;92m"
|
||||
#define cYEL "\x1b[1;93m"
|
||||
#define cLBL "\x1b[1;94m"
|
||||
#define cPIN "\x1b[1;95m"
|
||||
#define cLCY "\x1b[1;96m"
|
||||
#define cBRI "\x1b[1;97m"
|
||||
#define cRST "\x1b[0m"
|
||||
|
||||
#define bgBLK "\x1b[40m"
|
||||
#define bgRED "\x1b[41m"
|
||||
#define bgGRN "\x1b[42m"
|
||||
#define bgBRN "\x1b[43m"
|
||||
#define bgBLU "\x1b[44m"
|
||||
#define bgMGN "\x1b[45m"
|
||||
#define bgCYA "\x1b[46m"
|
||||
#define bgLGR "\x1b[47m"
|
||||
#define bgGRA "\x1b[100m"
|
||||
#define bgLRD "\x1b[101m"
|
||||
#define bgLGN "\x1b[102m"
|
||||
#define bgYEL "\x1b[103m"
|
||||
#define bgLBL "\x1b[104m"
|
||||
#define bgPIN "\x1b[105m"
|
||||
#define bgLCY "\x1b[106m"
|
||||
#define bgBRI "\x1b[107m"
|
||||
#define bgBLK "\x1b[40m"
|
||||
#define bgRED "\x1b[41m"
|
||||
#define bgGRN "\x1b[42m"
|
||||
#define bgBRN "\x1b[43m"
|
||||
#define bgBLU "\x1b[44m"
|
||||
#define bgMGN "\x1b[45m"
|
||||
#define bgCYA "\x1b[46m"
|
||||
#define bgLGR "\x1b[47m"
|
||||
#define bgGRA "\x1b[100m"
|
||||
#define bgLRD "\x1b[101m"
|
||||
#define bgLGN "\x1b[102m"
|
||||
#define bgYEL "\x1b[103m"
|
||||
#define bgLBL "\x1b[104m"
|
||||
#define bgPIN "\x1b[105m"
|
||||
#define bgLCY "\x1b[106m"
|
||||
#define bgBRI "\x1b[107m"
|
||||
|
||||
#else
|
||||
|
||||
#define cBLK ""
|
||||
#define cRED ""
|
||||
#define cGRN ""
|
||||
#define cBRN ""
|
||||
#define cBLU ""
|
||||
#define cMGN ""
|
||||
#define cCYA ""
|
||||
#define cLGR ""
|
||||
#define cGRA ""
|
||||
#define cLRD ""
|
||||
#define cLGN ""
|
||||
#define cYEL ""
|
||||
#define cLBL ""
|
||||
#define cPIN ""
|
||||
#define cLCY ""
|
||||
#define cBRI ""
|
||||
#define cRST ""
|
||||
#define cBLK ""
|
||||
#define cRED ""
|
||||
#define cGRN ""
|
||||
#define cBRN ""
|
||||
#define cBLU ""
|
||||
#define cMGN ""
|
||||
#define cCYA ""
|
||||
#define cLGR ""
|
||||
#define cGRA ""
|
||||
#define cLRD ""
|
||||
#define cLGN ""
|
||||
#define cYEL ""
|
||||
#define cLBL ""
|
||||
#define cPIN ""
|
||||
#define cLCY ""
|
||||
#define cBRI ""
|
||||
#define cRST ""
|
||||
|
||||
#define bgBLK ""
|
||||
#define bgRED ""
|
||||
#define bgGRN ""
|
||||
#define bgBRN ""
|
||||
#define bgBLU ""
|
||||
#define bgMGN ""
|
||||
#define bgCYA ""
|
||||
#define bgLGR ""
|
||||
#define bgGRA ""
|
||||
#define bgLRD ""
|
||||
#define bgLGN ""
|
||||
#define bgYEL ""
|
||||
#define bgLBL ""
|
||||
#define bgPIN ""
|
||||
#define bgLCY ""
|
||||
#define bgBRI ""
|
||||
#define bgBLK ""
|
||||
#define bgRED ""
|
||||
#define bgGRN ""
|
||||
#define bgBRN ""
|
||||
#define bgBLU ""
|
||||
#define bgMGN ""
|
||||
#define bgCYA ""
|
||||
#define bgLGR ""
|
||||
#define bgGRA ""
|
||||
#define bgLRD ""
|
||||
#define bgLGN ""
|
||||
#define bgYEL ""
|
||||
#define bgLBL ""
|
||||
#define bgPIN ""
|
||||
#define bgLCY ""
|
||||
#define bgBRI ""
|
||||
|
||||
#endif /* ^USE_COLOR */
|
||||
|
||||
@ -123,39 +123,39 @@
|
||||
|
||||
#ifdef FANCY_BOXES
|
||||
|
||||
#define SET_G1 "\x1b)0" /* Set G1 for box drawing */
|
||||
#define RESET_G1 "\x1b)B" /* Reset G1 to ASCII */
|
||||
#define bSTART "\x0e" /* Enter G1 drawing mode */
|
||||
#define bSTOP "\x0f" /* Leave G1 drawing mode */
|
||||
#define bH "q" /* Horizontal line */
|
||||
#define bV "x" /* Vertical line */
|
||||
#define bLT "l" /* Left top corner */
|
||||
#define bRT "k" /* Right top corner */
|
||||
#define bLB "m" /* Left bottom corner */
|
||||
#define bRB "j" /* Right bottom corner */
|
||||
#define bX "n" /* Cross */
|
||||
#define bVR "t" /* Vertical, branch right */
|
||||
#define bVL "u" /* Vertical, branch left */
|
||||
#define bHT "v" /* Horizontal, branch top */
|
||||
#define bHB "w" /* Horizontal, branch bottom */
|
||||
#define SET_G1 "\x1b)0" /* Set G1 for box drawing */
|
||||
#define RESET_G1 "\x1b)B" /* Reset G1 to ASCII */
|
||||
#define bSTART "\x0e" /* Enter G1 drawing mode */
|
||||
#define bSTOP "\x0f" /* Leave G1 drawing mode */
|
||||
#define bH "q" /* Horizontal line */
|
||||
#define bV "x" /* Vertical line */
|
||||
#define bLT "l" /* Left top corner */
|
||||
#define bRT "k" /* Right top corner */
|
||||
#define bLB "m" /* Left bottom corner */
|
||||
#define bRB "j" /* Right bottom corner */
|
||||
#define bX "n" /* Cross */
|
||||
#define bVR "t" /* Vertical, branch right */
|
||||
#define bVL "u" /* Vertical, branch left */
|
||||
#define bHT "v" /* Horizontal, branch top */
|
||||
#define bHB "w" /* Horizontal, branch bottom */
|
||||
|
||||
#else
|
||||
|
||||
#define SET_G1 ""
|
||||
#define RESET_G1 ""
|
||||
#define bSTART ""
|
||||
#define bSTOP ""
|
||||
#define bH "-"
|
||||
#define bV "|"
|
||||
#define bLT "+"
|
||||
#define bRT "+"
|
||||
#define bLB "+"
|
||||
#define bRB "+"
|
||||
#define bX "+"
|
||||
#define bVR "+"
|
||||
#define bVL "+"
|
||||
#define bHT "+"
|
||||
#define bHB "+"
|
||||
#define SET_G1 ""
|
||||
#define RESET_G1 ""
|
||||
#define bSTART ""
|
||||
#define bSTOP ""
|
||||
#define bH "-"
|
||||
#define bV "|"
|
||||
#define bLT "+"
|
||||
#define bRT "+"
|
||||
#define bLB "+"
|
||||
#define bRB "+"
|
||||
#define bX "+"
|
||||
#define bVR "+"
|
||||
#define bVL "+"
|
||||
#define bHT "+"
|
||||
#define bHB "+"
|
||||
|
||||
#endif /* ^FANCY_BOXES */
|
||||
|
||||
@ -176,9 +176,9 @@
|
||||
/* Just print stuff to the appropriate stream. */
|
||||
|
||||
#ifdef MESSAGES_TO_STDOUT
|
||||
#define SAYF(x...) printf(x)
|
||||
#define SAYF(x...) printf(x)
|
||||
#else
|
||||
#define SAYF(x...) fprintf(stderr, x)
|
||||
#define SAYF(x...) fprintf(stderr, x)
|
||||
#endif /* ^MESSAGES_TO_STDOUT */
|
||||
|
||||
/* Show a prefixed warning. */
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct afl_forkserver {
|
||||
|
||||
/* a program that includes afl-forkserver needs to define these */
|
||||
@ -42,6 +43,7 @@ typedef struct afl_forkserver {
|
||||
|
||||
s32 fsrv_pid, /* PID of the fork server */
|
||||
child_pid, /* PID of the fuzzed program */
|
||||
child_status, /* waitpid result for the child */
|
||||
out_dir_fd; /* FD of the lock file */
|
||||
|
||||
s32 out_fd, /* Persistent fd for fsrv->out_file */
|
||||
@ -108,20 +110,20 @@ void afl_fsrv_killall(void);
|
||||
void afl_fsrv_deinit(afl_forkserver_t *fsrv);
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define MSG_FORK_ON_APPLE \
|
||||
" - On MacOS X, the semantics of fork() syscalls are non-standard and " \
|
||||
"may\n" \
|
||||
" break afl-fuzz performance optimizations when running " \
|
||||
"platform-specific\n" \
|
||||
" targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"
|
||||
#define MSG_FORK_ON_APPLE \
|
||||
" - On MacOS X, the semantics of fork() syscalls are non-standard and " \
|
||||
"may\n" \
|
||||
" break afl-fuzz performance optimizations when running " \
|
||||
"platform-specific\n" \
|
||||
" targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"
|
||||
#else
|
||||
#define MSG_FORK_ON_APPLE ""
|
||||
#define MSG_FORK_ON_APPLE ""
|
||||
#endif
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
#define MSG_ULIMIT_USAGE " ( ulimit -Sv $[%llu << 10];"
|
||||
#define MSG_ULIMIT_USAGE " ( ulimit -Sv $[%llu << 10];"
|
||||
#else
|
||||
#define MSG_ULIMIT_USAGE " ( ulimit -Sd $[%llu << 10];"
|
||||
#define MSG_ULIMIT_USAGE " ( ulimit -Sd $[%llu << 10];"
|
||||
#endif /* ^RLIMIT_AS */
|
||||
|
||||
#endif
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define ROL64(_x, _r) ((((u64)(_x)) << (_r)) | (((u64)(_x)) >> (64 - (_r))))
|
||||
#define ROL64(_x, _r) ((((u64)(_x)) << (_r)) | (((u64)(_x)) >> (64 - (_r))))
|
||||
|
||||
static inline u32 hash32(const void *key, u32 len, u32 seed) {
|
||||
|
||||
@ -67,7 +67,7 @@ static inline u32 hash32(const void *key, u32 len, u32 seed) {
|
||||
|
||||
#else
|
||||
|
||||
#define ROL32(_x, _r) ((((u32)(_x)) << (_r)) | (((u32)(_x)) >> (32 - (_r))))
|
||||
#define ROL32(_x, _r) ((((u32)(_x)) << (_r)) | (((u32)(_x)) >> (32 - (_r))))
|
||||
|
||||
static inline u32 hash32(const void *key, u32 len, u32 seed) {
|
||||
|
||||
|
@ -83,7 +83,7 @@ static inline void list_append(list_t *list, void *el) {
|
||||
element_t *el_box = NULL;
|
||||
PRE_ALLOC(el_box, list->element_prealloc_buf, LIST_PREALLOC_SIZE,
|
||||
list->element_prealloc_count);
|
||||
if (!el_box) FATAL("failed to allocate list element");
|
||||
if (!el_box) { FATAL("failed to allocate list element"); }
|
||||
el_box->data = el;
|
||||
el_box->next = head;
|
||||
el_box->prev = head->prev;
|
||||
|
105
include/types.h
105
include/types.h
@ -30,6 +30,29 @@ typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
|
||||
/* Extended forkserver option values */
|
||||
|
||||
/* Reporting errors */
|
||||
#define FS_OPT_ERROR 0xf800008f
|
||||
#define FS_OPT_GET_ERROR(x) ((x & 0x00ffff00) >> 8)
|
||||
#define FS_OPT_SET_ERROR(x) ((x & 0x0000ffff) << 8)
|
||||
#define FS_ERROR_MAP_SIZE 1
|
||||
#define FS_ERROR_MAP_ADDR 2
|
||||
#define FS_ERROR_SHM_OPEN 4
|
||||
#define FS_ERROR_SHMAT 8
|
||||
#define FS_ERROR_MMAP 16
|
||||
|
||||
/* Reporting options */
|
||||
#define FS_OPT_ENABLED 0x8f000001
|
||||
#define FS_OPT_MAPSIZE 0x40000000
|
||||
#define FS_OPT_SNAPSHOT 0x20000000
|
||||
#define FS_OPT_AUTODICT 0x10000000
|
||||
// FS_OPT_MAX_MAPSIZE is 8388608 = 0x800000 = 2^23 = 1 << 22
|
||||
#define FS_OPT_MAX_MAPSIZE ((0x00fffffe >> 1) + 1)
|
||||
#define FS_OPT_GET_MAPSIZE(x) (((x & 0x00fffffe) >> 1) + 1)
|
||||
#define FS_OPT_SET_MAPSIZE(x) \
|
||||
(x <= 1 || x > FS_OPT_MAX_MAPSIZE ? 0 : ((x - 1) << 1))
|
||||
|
||||
/*
|
||||
|
||||
Ugh. There is an unintended compiler / glibc #include glitch caused by
|
||||
@ -58,22 +81,24 @@ typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) \
|
||||
({ \
|
||||
\
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
\
|
||||
})
|
||||
#define MAX(a, b) \
|
||||
({ \
|
||||
\
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
\
|
||||
})
|
||||
#define MIN(a, b) \
|
||||
({ \
|
||||
\
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
\
|
||||
})
|
||||
|
||||
#define MAX(a, b) \
|
||||
({ \
|
||||
\
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
\
|
||||
})
|
||||
|
||||
#endif /* !MIN */
|
||||
|
||||
#define SWAP16(_x) \
|
||||
@ -108,21 +133,21 @@ typedef int64_t s64;
|
||||
})
|
||||
|
||||
#ifdef AFL_LLVM_PASS
|
||||
#if defined(__linux__)
|
||||
#define AFL_SR(s) (srandom(s))
|
||||
#define AFL_R(x) (random() % (x))
|
||||
#if defined(__linux__) || !defined(__ANDROID__)
|
||||
#define AFL_SR(s) (srandom(s))
|
||||
#define AFL_R(x) (random() % (x))
|
||||
#else
|
||||
#define AFL_SR(s) ((void)s)
|
||||
#define AFL_R(x) (arc4random_uniform(x))
|
||||
#endif
|
||||
#else
|
||||
#define AFL_SR(s) ((void)s)
|
||||
#define AFL_R(x) (arc4random_uniform(x))
|
||||
#endif
|
||||
#else
|
||||
#if defined(__linux__)
|
||||
#define SR(s) (srandom(s))
|
||||
#define R(x) (random() % (x))
|
||||
#else
|
||||
#define SR(s) ((void)s)
|
||||
#define R(x) (arc4random_uniform(x))
|
||||
#endif
|
||||
#if defined(__linux__) || !defined(__ANDROID__)
|
||||
#define SR(s) (srandom(s))
|
||||
#define R(x) (random() % (x))
|
||||
#else
|
||||
#define SR(s) ((void)s)
|
||||
#define R(x) (arc4random_uniform(x))
|
||||
#endif
|
||||
#endif /* ^AFL_LLVM_PASS */
|
||||
|
||||
#define STRINGIFY_INTERNAL(x) #x
|
||||
@ -131,15 +156,19 @@ typedef int64_t s64;
|
||||
#define MEM_BARRIER() __asm__ volatile("" ::: "memory")
|
||||
|
||||
#if __GNUC__ < 6
|
||||
#define likely(_x) (_x)
|
||||
#define unlikely(_x) (_x)
|
||||
#ifndef likely
|
||||
#define likely(_x) (_x)
|
||||
#endif
|
||||
#ifndef unlikely
|
||||
#define unlikely(_x) (_x)
|
||||
#endif
|
||||
#else
|
||||
#ifndef likely
|
||||
#define likely(_x) __builtin_expect(!!(_x), 1)
|
||||
#endif
|
||||
#ifndef unlikely
|
||||
#define unlikely(_x) __builtin_expect(!!(_x), 0)
|
||||
#endif
|
||||
#ifndef likely
|
||||
#define likely(_x) __builtin_expect(!!(_x), 1)
|
||||
#endif
|
||||
#ifndef unlikely
|
||||
#define unlikely(_x) __builtin_expect(!!(_x), 0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* ! _HAVE_TYPES_H */
|
||||
|
@ -39,5 +39,5 @@ clean:
|
||||
install: all
|
||||
install -m 755 -d $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 755 ../libdislocator.so $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 644 README.dislocator.md $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 644 -T README.md $${DESTDIR}$(HELPER_PATH)/README.dislocator.md
|
||||
|
||||
|
@ -30,39 +30,39 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach/vm_statistics.h>
|
||||
#include <mach/vm_statistics.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/param.h>
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#ifdef __NR_getrandom
|
||||
#define arc4random_buf(p, l) \
|
||||
do { \
|
||||
\
|
||||
ssize_t rd = syscall(__NR_getrandom, p, l, 0); \
|
||||
if (rd != l) DEBUGF("getrandom failed"); \
|
||||
\
|
||||
} while (0)
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#ifdef __NR_getrandom
|
||||
#define arc4random_buf(p, l) \
|
||||
do { \
|
||||
\
|
||||
ssize_t rd = syscall(__NR_getrandom, p, l, 0); \
|
||||
if (rd != l) DEBUGF("getrandom failed"); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#include <time.h>
|
||||
#define arc4random_buf(p, l) \
|
||||
do { \
|
||||
\
|
||||
srand(time(NULL)); \
|
||||
u32 i; \
|
||||
u8 *ptr = (u8 *)p; \
|
||||
for (i = 0; i < l; i++) \
|
||||
ptr[i] = rand() % INT_MAX; \
|
||||
\
|
||||
} while (0)
|
||||
#else
|
||||
#include <time.h>
|
||||
#define arc4random_buf(p, l) \
|
||||
do { \
|
||||
\
|
||||
srand(time(NULL)); \
|
||||
u32 i; \
|
||||
u8 *ptr = (u8 *)p; \
|
||||
for (i = 0; i < l; i++) \
|
||||
ptr[i] = rand() % INT_MAX; \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
@ -83,11 +83,11 @@ typedef struct {
|
||||
#define ALLOC_ALIGN_SIZE (_Alignof(max_align_t))
|
||||
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 4096
|
||||
#define PAGE_SIZE 4096
|
||||
#endif /* !PAGE_SIZE */
|
||||
|
||||
#ifndef MAP_ANONYMOUS
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif /* !MAP_ANONYMOUS */
|
||||
|
||||
#define SUPER_PAGE_SIZE 1 << 21
|
||||
@ -148,8 +148,8 @@ static u8 alloc_verbose, /* Additional debug messages */
|
||||
align_allocations; /* Force alignment to sizeof(void*) */
|
||||
|
||||
#if defined __OpenBSD__ || defined __APPLE__
|
||||
#define __thread
|
||||
#warning no thread support available
|
||||
#define __thread
|
||||
#warning no thread support available
|
||||
#endif
|
||||
static __thread size_t total_mem; /* Currently allocated mem */
|
||||
|
||||
@ -183,38 +183,38 @@ static void *__dislocator_alloc(size_t len) {
|
||||
else
|
||||
rlen = len;
|
||||
|
||||
/* We will also store buffer length and a canary below the actual buffer, so
|
||||
let's add 8 bytes for that. */
|
||||
|
||||
tlen = (1 + PG_COUNT(rlen + 8)) * PAGE_SIZE;
|
||||
flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
fd = -1;
|
||||
#if defined(USEHUGEPAGE)
|
||||
sp = (rlen >= SUPER_PAGE_SIZE && !(rlen % SUPER_PAGE_SIZE));
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__APPLE__)
|
||||
if (sp) fd = VM_FLAGS_SUPERPAGE_SIZE_2MB;
|
||||
#elif defined(__linux__)
|
||||
#elif defined(__linux__)
|
||||
if (sp) flags |= MAP_HUGETLB;
|
||||
#elif defined(__FreeBSD__)
|
||||
#elif defined(__FreeBSD__)
|
||||
if (sp) flags |= MAP_ALIGNED_SUPER;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
(void)sp;
|
||||
#endif
|
||||
|
||||
/* We will also store buffer length and a canary below the actual buffer, so
|
||||
let's add 8 bytes for that. */
|
||||
|
||||
ret = (u8 *)mmap(NULL, tlen, PROT_READ | PROT_WRITE, flags, fd, 0);
|
||||
#if defined(USEHUGEPAGE)
|
||||
/* We try one more time with regular call */
|
||||
if (ret == MAP_FAILED) {
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__APPLE__)
|
||||
fd = -1;
|
||||
#elif defined(__linux__)
|
||||
#elif defined(__linux__)
|
||||
flags &= -MAP_HUGETLB;
|
||||
#elif defined(__FreeBSD__)
|
||||
#elif defined(__FreeBSD__)
|
||||
flags &= -MAP_ALIGNED_SUPER;
|
||||
#endif
|
||||
#endif
|
||||
ret = (u8 *)mmap(NULL, tlen, PROT_READ | PROT_WRITE, flags, fd, 0);
|
||||
|
||||
}
|
||||
@ -296,10 +296,6 @@ void *calloc(size_t elem_len, size_t elem_cnt) {
|
||||
|
||||
}
|
||||
|
||||
/* TODO: add a wrapper for posix_memalign, otherwise apps who use it,
|
||||
will fail when freeing the memory.
|
||||
*/
|
||||
|
||||
/* The wrapper for malloc(). Roughly the same, also clobbers the returned
|
||||
memory (unlike calloc(), malloc() is not guaranteed to return zeroed
|
||||
memory). */
|
||||
@ -468,6 +464,18 @@ void *reallocarray(void *ptr, size_t elem_len, size_t elem_cnt) {
|
||||
|
||||
}
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
size_t malloc_usable_size(void *ptr) {
|
||||
|
||||
#else
|
||||
size_t malloc_usable_size(const void *ptr) {
|
||||
|
||||
#endif
|
||||
|
||||
return ptr ? PTR_L(ptr) : 0;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void __dislocator_init(void) {
|
||||
|
||||
u8 *tmp = (u8 *)getenv("AFL_LD_LIMIT_MB");
|
||||
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -35,18 +35,20 @@
|
||||
|
||||
#if !defined __linux__ && !defined __APPLE__ && !defined __FreeBSD__ && \
|
||||
!defined __OpenBSD__ && !defined __NetBSD__ && !defined __DragonFly__
|
||||
#error "Sorry, this library is unsupported in this platform for now!"
|
||||
#error "Sorry, this library is unsupported in this platform for now!"
|
||||
#endif /* !__linux__ && !__APPLE__ && ! __FreeBSD__ && ! __OpenBSD__ && \
|
||||
!__NetBSD__*/
|
||||
|
||||
#if defined __APPLE__
|
||||
#include <mach/vm_map.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mach/mach_init.h>
|
||||
#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#if !defined __NetBSD__
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
@ -152,25 +154,25 @@ static void __tokencap_load_mappings(void) {
|
||||
|
||||
#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
|
||||
|
||||
#if defined __FreeBSD__
|
||||
#if defined __FreeBSD__
|
||||
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid};
|
||||
#elif defined __OpenBSD__
|
||||
#elif defined __OpenBSD__
|
||||
int mib[] = {CTL_KERN, KERN_PROC_VMMAP, __tokencap_pid};
|
||||
#elif defined __NetBSD__
|
||||
#elif defined __NetBSD__
|
||||
int mib[] = {CTL_VM, VM_PROC, VM_PROC_MAP, __tokencap_pid,
|
||||
sizeof(struct kinfo_vmentry)};
|
||||
#endif
|
||||
#endif
|
||||
char * buf, *low, *high;
|
||||
size_t miblen = sizeof(mib) / sizeof(mib[0]);
|
||||
size_t len;
|
||||
|
||||
if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) return;
|
||||
|
||||
#if defined __FreeBSD__ || defined __NetBSD__
|
||||
#if defined __FreeBSD__ || defined __NetBSD__
|
||||
len = len * 4 / 3;
|
||||
#elif defined __OpenBSD__
|
||||
#elif defined __OpenBSD__
|
||||
len -= len % sizeof(struct kinfo_vmentry);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
|
||||
if (buf == MAP_FAILED) return;
|
||||
@ -191,22 +193,22 @@ static void __tokencap_load_mappings(void) {
|
||||
|
||||
struct kinfo_vmentry *region = (struct kinfo_vmentry *)low;
|
||||
|
||||
#if defined __FreeBSD__ || defined __NetBSD__
|
||||
#if defined __FreeBSD__ || defined __NetBSD__
|
||||
|
||||
#if defined __FreeBSD__
|
||||
#if defined __FreeBSD__
|
||||
size_t size = region->kve_structsize;
|
||||
|
||||
if (size == 0) break;
|
||||
#elif defined __NetBSD__
|
||||
#elif defined __NetBSD__
|
||||
size_t size = sizeof(*region);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* We go through the whole mapping of the process and track read-only
|
||||
* addresses */
|
||||
if ((region->kve_protection & KVME_PROT_READ) &&
|
||||
!(region->kve_protection & KVME_PROT_WRITE)) {
|
||||
|
||||
#elif defined __OpenBSD__
|
||||
#elif defined __OpenBSD__
|
||||
|
||||
size_t size = sizeof(*region);
|
||||
|
||||
@ -215,7 +217,7 @@ static void __tokencap_load_mappings(void) {
|
||||
if ((region->kve_protection & KVE_PROT_READ) &&
|
||||
!(region->kve_protection & KVE_PROT_WRITE)) {
|
||||
|
||||
#endif
|
||||
#endif
|
||||
__tokencap_ro[__tokencap_ro_cnt].st = (void *)region->kve_start;
|
||||
__tokencap_ro[__tokencap_ro_cnt].en = (void *)region->kve_end;
|
||||
|
||||
@ -274,7 +276,8 @@ static void __tokencap_dump(const u8 *ptr, size_t len, u8 is_text) {
|
||||
pos += 4;
|
||||
break;
|
||||
|
||||
default: buf[pos++] = ptr[i];
|
||||
default:
|
||||
buf[pos++] = ptr[i];
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
#
|
||||
# american fuzzy lop++ - LLVM instrumentation
|
||||
# -----------------------------------------
|
||||
#
|
||||
@ -39,13 +38,14 @@ else
|
||||
LLVM_CONFIG ?= llvm-config
|
||||
endif
|
||||
|
||||
LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-7]|^1[2-9]' && echo 1 || echo 0 )
|
||||
LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^1[2-9]' && echo 1 || echo 0 )
|
||||
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 )
|
||||
LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//')
|
||||
LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null)
|
||||
LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null)
|
||||
LLVM_STDCXX = gnu++11
|
||||
LLVM_APPLE = $(shell clang -v 2>&1 | grep -iq apple && echo 1 || echo 0)
|
||||
LLVM_APPLE_XCODE = $(shell clang -v 2>&1 | grep -q Apple && echo 1 || echo 0)
|
||||
LLVM_LTO = 0
|
||||
|
||||
ifeq "$(LLVMVER)" ""
|
||||
@ -68,13 +68,14 @@ endif
|
||||
ifeq "$(LLVM_MAJOR)" "11"
|
||||
$(info [+] llvm_mode detected llvm 11, enabling afl-clang-lto LTO implementation)
|
||||
LLVM_LTO = 1
|
||||
TEST_MMAP = 1
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_LTO)" "0"
|
||||
$(info [+] llvm_mode detected llvm < 11, afl-clang-lto LTO will not be build.)
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_APPLE)" "1"
|
||||
ifeq "$(LLVM_APPLE_XCODE)" "1"
|
||||
$(warning llvm_mode will not compile with Xcode clang...)
|
||||
endif
|
||||
|
||||
@ -82,30 +83,79 @@ endif
|
||||
# this seems to be busted on some distros, so using the one in $PATH is
|
||||
# probably better.
|
||||
|
||||
CC ?= $(LLVM_BINDIR)/clang
|
||||
CXX ?= $(LLVM_BINDIR)/clang++
|
||||
CC = $(LLVM_BINDIR)/clang
|
||||
CXX = $(LLVM_BINDIR)/clang++
|
||||
|
||||
# llvm-config --bindir may not providing a valid path, so ...
|
||||
ifeq "$(shell test -e $(CC) || echo 1 )" "1"
|
||||
# llvm-config --bindir may not providing a valid path, so ...
|
||||
ifeq "$(shell test -e '$(BIN_DIR)/clang' && echo 1)" "1"
|
||||
# we found one in the local install directory, lets use these
|
||||
CC = $(BIN_DIR)/clang
|
||||
CXX = $(BIN_DIR)/clang++
|
||||
else
|
||||
# hope for the best
|
||||
$(warning we have trouble finding clang/clang++ - llvm-config is not helping us)
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
# however we must ensure that this is not a "CC=gcc make"
|
||||
ifeq "$(shell command -v $(CC) 2> /dev/null)" ""
|
||||
# we do not have a valid CC variable so we try alternatives
|
||||
ifeq "$(shell test -e '$(BIN_DIR)/clang' && echo 1)" "1"
|
||||
# we found one in the local install directory, lets use these
|
||||
CC = $(BIN_DIR)/clang
|
||||
else
|
||||
# hope for the best
|
||||
$(warning we have trouble finding clang - llvm-config is not helping us)
|
||||
CC = clang
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
# llvm-config --bindir may not providing a valid path, so ...
|
||||
ifeq "$(shell test -e $(CXX) || echo 1 )" "1"
|
||||
# however we must ensure that this is not a "CC=gcc make"
|
||||
ifeq "$(shell command -v $(CXX) 2> /dev/null)" ""
|
||||
# we do not have a valid CC variable so we try alternatives
|
||||
ifeq "$(shell test -e '$(BIN_DIR)/clang++' && echo 1)" "1"
|
||||
# we found one in the local install directory, lets use these
|
||||
CXX = $(BIN_DIR)/clang++
|
||||
else
|
||||
# hope for the best
|
||||
$(warning we have trouble finding clang++ - llvm-config is not helping us)
|
||||
CXX = clang++
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# sanity check.
|
||||
# Are versions of clang --version and llvm-config --version equal?
|
||||
CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*version\ ([0-9]\.[0-9]\.[0-9]).*/s//\1/p')
|
||||
CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*version\ (1?[0-9]\.[0-9]\.[0-9]).*/s//\1/p')
|
||||
|
||||
ifneq "$(CLANGVER)" "$(LLVMVER)"
|
||||
CC = $(shell $(LLVM_CONFIG) --bindir)/clang
|
||||
CXX = $(shell $(LLVM_CONFIG) --bindir)/clang++
|
||||
# I disable this because it does not make sense with what we did before (marc)
|
||||
# We did exactly set these 26 lines above with these values, and it would break
|
||||
# "CC=gcc make" etc. usages
|
||||
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
|
||||
CC_SAVE := $(LLVM_BINDIR)/clang
|
||||
else
|
||||
CC_SAVE := $(CC)
|
||||
endif
|
||||
ifeq "$(findstring clang, $(shell $(CXX) --version 2>/dev/null))" ""
|
||||
CXX_SAVE := $(LLVM_BINDIR)/clang++
|
||||
else
|
||||
CXX_SAVE := $(CXX)
|
||||
endif
|
||||
|
||||
CLANG_BIN := $(CC_SAVE)
|
||||
CLANGPP_BIN := $(CXX_SAVE)
|
||||
|
||||
ifeq "$(CC_SAVE)" "$(LLVM_BINDIR)/clang"
|
||||
USE_BINDIR = 1
|
||||
else
|
||||
ifeq "$(CXX_SAVE)" "$(LLVM_BINDIR)/clang++"
|
||||
USE_BINDIR = 1
|
||||
else
|
||||
USE_BINDIR = 0
|
||||
endif
|
||||
endif
|
||||
|
||||
# On old platform we cannot compile with clang because std++ libraries are too
|
||||
# old. For these we need to use gcc/g++, so if we find REAL_CC and REAL_CXX
|
||||
# variable we override the compiler variables here
|
||||
ifneq "$(REAL_CC)" ""
|
||||
CC = $(REAL_CC)
|
||||
endif
|
||||
ifneq "$(REAL_CXX)" ""
|
||||
CXX = $(REAL_CXX)
|
||||
endif
|
||||
|
||||
# After we set CC/CXX we can start makefile magic tests
|
||||
@ -114,13 +164,13 @@ ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .te
|
||||
CFLAGS_OPT = -march=native
|
||||
endif
|
||||
|
||||
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"
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
AFL_CLANG_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"
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
AFL_CLANG_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"
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
AFL_CLANG_FLTO ?= -flto
|
||||
endif
|
||||
endif
|
||||
@ -141,32 +191,20 @@ endif
|
||||
|
||||
AFL_CLANG_FUSELD=
|
||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -fuse-ld=`command -v ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=`command -v ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
AFL_CLANG_FUSELD=1
|
||||
endif
|
||||
endif
|
||||
|
||||
CLANG_BIN = $(basename $(CC))
|
||||
CLANGPP_BIN = $(basename $(CXX))
|
||||
ifeq "$(shell test -e $(CLANG_BIN) || echo 1 )" "1"
|
||||
CLANG_BIN = $(CC)
|
||||
CLANGPP_BIN = $(CXX)
|
||||
endif
|
||||
|
||||
ifeq "$(CC)" "$(LLVM_BINDIR)/clang"
|
||||
USE_BINDIR = 1
|
||||
else
|
||||
USE_BINDIR = 0
|
||||
endif
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2
|
||||
override CFLAGS += -Wall \
|
||||
-g -Wno-pointer-sign -I ../include/ \
|
||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
||||
-DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \
|
||||
-DLLVM_VERSION=\"$(LLVMVER)\" -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \
|
||||
-DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \
|
||||
-DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \
|
||||
-DAFL_REAL_LD=\"$(AFL_REAL_LD)\" -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \
|
||||
-DCLANG_BIN=\"$(CC)\" -DCLANGPP_BIN=\"$(CXX)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function
|
||||
-DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function
|
||||
ifdef AFL_TRACE_PC
|
||||
$(info Compile option AFL_TRACE_PC is deprecated, just set AFL_LLVM_INSTRUMENT=PCGUARD to activate when compiling targets )
|
||||
endif
|
||||
@ -175,13 +213,15 @@ CXXFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2
|
||||
override CXXFLAGS += -Wall -g -I ../include/ \
|
||||
-DVERSION=\"$(VERSION)\" -Wno-variadic-macros
|
||||
|
||||
CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS)
|
||||
CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fpic $(CXXFLAGS)
|
||||
CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS)
|
||||
|
||||
|
||||
# User teor2345 reports that this is required to make things work on MacOS X.
|
||||
ifeq "$(shell uname)" "Darwin"
|
||||
CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress
|
||||
else
|
||||
CLANG_CFL += -Wl,-znodelete
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname)" "OpenBSD"
|
||||
@ -193,23 +233,23 @@ ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int ma
|
||||
else
|
||||
SHMAT_OK=0
|
||||
CFLAGS+=-DUSEMMAP=1
|
||||
LDFLAGS += -lrt
|
||||
LDFLAGS += -Wno-deprecated-declarations
|
||||
endif
|
||||
|
||||
ifeq "$(TEST_MMAP)" "1"
|
||||
SHMAT_OK=0
|
||||
CFLAGS+=-DUSEMMAP=1
|
||||
LDFLAGS += -lrt
|
||||
LDFLAGS += -Wno-deprecated-declarations
|
||||
endif
|
||||
|
||||
PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-lto-whitelist.so ../afl-llvm-lto-instrumentation.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so
|
||||
PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-lto-whitelist.so ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so
|
||||
|
||||
# If prerequisites are not given, warn, do not build anything, and exit with code 0
|
||||
ifeq "$(LLVMVER)" ""
|
||||
NO_BUILD = 1
|
||||
endif
|
||||
|
||||
ifneq "$(LLVM_UNSUPPORTED)$(LLVM_APPLE)" "00"
|
||||
ifneq "$(LLVM_UNSUPPORTED)$(LLVM_APPLE_XCODE)" "00"
|
||||
NO_BUILD = 1
|
||||
endif
|
||||
|
||||
@ -241,7 +281,7 @@ no_build:
|
||||
|
||||
test_deps:
|
||||
@echo "[*] Checking for working 'llvm-config'..."
|
||||
ifneq "$(LLVM_APPLE)" "1"
|
||||
ifneq "$(LLVM_APPLE_XCODE)" "1"
|
||||
@type $(LLVM_CONFIG) >/dev/null 2>&1 || ( echo "[-] Oops, can't find 'llvm-config'. Install clang or set \$$LLVM_CONFIG or \$$PATH beforehand."; echo " (Sometimes, the binary will be named llvm-config-3.5 or something like that.)"; exit 1 )
|
||||
endif
|
||||
@echo "[*] Checking for working '$(CC)'..."
|
||||
@ -249,7 +289,6 @@ test_deps:
|
||||
@echo "[*] Checking for matching versions of '$(CC)' and '$(LLVM_CONFIG)'"
|
||||
ifneq "$(CLANGVER)" "$(LLVMVER)"
|
||||
@echo "[!] WARNING: we have llvm-config version $(LLVMVER) and a clang version $(CLANGVER)"
|
||||
@echo "[!] Retrying with the clang compiler from llvm: CC=`llvm-config --bindir`/clang"
|
||||
else
|
||||
@echo "[*] We have llvm-config version $(LLVMVER) with a clang version $(CLANGVER), good."
|
||||
endif
|
||||
@ -261,7 +300,7 @@ afl-common.o: ../src/afl-common.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
|
||||
|
||||
../afl-clang-fast: afl-clang-fast.c afl-common.o | test_deps
|
||||
$(CC) $(CFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\"
|
||||
$(CC) $(CFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" -Dxxx
|
||||
ln -sf afl-clang-fast ../afl-clang-fast++
|
||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
@ -270,42 +309,50 @@ ifeq "$(LLVM_LTO)" "1"
|
||||
endif
|
||||
endif
|
||||
|
||||
../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc | test_deps
|
||||
-$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL)
|
||||
afl-llvm-common.o: afl-llvm-common.cc afl-llvm-common.h
|
||||
$(CXX) $(CFLAGS) `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC -std=$(LLVM_STDCXX) -c $< -o $@
|
||||
|
||||
../afl-llvm-pass.so: afl-llvm-pass.so.cc | test_deps
|
||||
../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc afl-llvm-common.o | test_deps
|
||||
-$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
|
||||
../afl-llvm-pass.so: afl-llvm-pass.so.cc afl-llvm-common.o | test_deps
|
||||
ifeq "$(LLVM_MIN_4_0_1)" "0"
|
||||
$(info [!] N-gram branch coverage instrumentation is not available for llvm version $(LLVMVER))
|
||||
endif
|
||||
$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL)
|
||||
$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
|
||||
../afl-llvm-lto-whitelist.so: afl-llvm-lto-whitelist.so.cc
|
||||
../afl-llvm-lto-whitelist.so: afl-llvm-lto-whitelist.so.cc afl-llvm-common.o
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
$(CXX) $(CLANG_CFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL)
|
||||
$(CXX) $(CLANG_CFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
endif
|
||||
|
||||
../afl-llvm-lto-instrumentation.so: afl-llvm-lto-instrumentation.so.cc
|
||||
../afl-llvm-lto-instrumentation.so: afl-llvm-lto-instrumentation.so.cc afl-llvm-common.o
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
$(CXX) $(CLANG_CFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL)
|
||||
$(CC) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto.o
|
||||
@$(CC) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m64 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-64.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
|
||||
@$(CC) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
|
||||
$(CXX) $(CLANG_CFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
$(CLANG_BIN) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto.o
|
||||
@$(CLANG_BIN) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m64 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-64.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
|
||||
@$(CLANG_BIN) $(CFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
|
||||
endif
|
||||
|
||||
../afl-llvm-lto-instrim.so: afl-llvm-lto-instrim.so.cc afl-llvm-common.o
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
endif
|
||||
|
||||
# laf
|
||||
../split-switches-pass.so: split-switches-pass.so.cc | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL)
|
||||
../compare-transform-pass.so: compare-transform-pass.so.cc | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL)
|
||||
../split-compares-pass.so: split-compares-pass.so.cc | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL)
|
||||
../split-switches-pass.so: split-switches-pass.so.cc afl-llvm-common.o | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
../compare-transform-pass.so: compare-transform-pass.so.cc afl-llvm-common.o | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
../split-compares-pass.so: split-compares-pass.so.cc afl-llvm-common.o | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
# /laf
|
||||
|
||||
../cmplog-routines-pass.so: cmplog-routines-pass.cc | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL)
|
||||
../cmplog-routines-pass.so: cmplog-routines-pass.cc afl-llvm-common.o | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
|
||||
../cmplog-instructions-pass.so: cmplog-instructions-pass.cc | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL)
|
||||
../cmplog-instructions-pass.so: cmplog-instructions-pass.cc afl-llvm-common.o | test_deps
|
||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
|
||||
../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps
|
||||
$(CC) $(CFLAGS) -Wno-unused-result -fPIC -c $< -o $@
|
||||
@ -320,7 +367,7 @@ endif
|
||||
|
||||
test_build: $(PROGS)
|
||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=$(CC) AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
||||
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
||||
ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
||||
echo 1 | ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||
@rm -f test-instr
|
||||
@ -335,7 +382,7 @@ all_done: test_build
|
||||
install: all
|
||||
install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
|
||||
if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../afl-llvm-rt-32.o ]; then set -e; install -m 755 ../afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../afl-llvm-rt-64.o ]; then set -e; install -m 755 ../afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../compare-transform-pass.so ]; then set -e; install -m 755 ../compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
|
@ -12,13 +12,13 @@ typedef long double max_align_t;
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/CFG.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#else
|
||||
#include "llvm/Support/CFG.h"
|
||||
#include "llvm/Analysis/Dominators.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#include "llvm/Support/CFG.h"
|
||||
#include "llvm/Analysis/Dominators.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#endif
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
@ -36,11 +36,13 @@ typedef long double max_align_t;
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include "MarkNodes.h"
|
||||
#include "afl-llvm-common.h"
|
||||
#include "llvm-ngram-coverage.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "MarkNodes.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static cl::opt<bool> MarkSetOpt("markset", cl::desc("MarkSet"),
|
||||
@ -53,9 +55,9 @@ namespace {
|
||||
struct InsTrim : public ModulePass {
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
uint32_t function_minimum_size = 1;
|
||||
uint32_t debug = 0;
|
||||
uint32_t function_minimum_size = 1;
|
||||
uint32_t debug = 0;
|
||||
char * skip_nozero = NULL;
|
||||
|
||||
private:
|
||||
std::mt19937 generator;
|
||||
@ -69,24 +71,10 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
InsTrim() : ModulePass(ID), generator(0) {
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
initWhitelist();
|
||||
|
||||
}
|
||||
|
||||
@ -107,27 +95,10 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
// ripped from aflgo
|
||||
static bool isBlacklisted(const Function *F) {
|
||||
|
||||
static const char *Blacklist[] = {
|
||||
|
||||
"asan.",
|
||||
"llvm.",
|
||||
"sancov.",
|
||||
"__ubsan_handle_",
|
||||
|
||||
};
|
||||
|
||||
for (auto const &BlacklistFunc : Blacklist) {
|
||||
|
||||
if (F->getName().startswith(BlacklistFunc)) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1)
|
||||
#define AFL_HAVE_VECTOR_INTRINSICS 1
|
||||
#endif
|
||||
|
||||
bool runOnModule(Module &M) override {
|
||||
|
||||
@ -143,11 +114,17 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
if (getenv("AFL_DEBUG") != NULL) debug = 1;
|
||||
|
||||
LLVMContext &C = M.getContext();
|
||||
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
char *neverZero_counters_str;
|
||||
if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL)
|
||||
if (!be_quiet) OKF("LLVM neverZero activated (by hexcoder)\n");
|
||||
#endif
|
||||
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL ||
|
||||
getenv("LOOPHEAD") != NULL) {
|
||||
@ -156,27 +133,124 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") != NULL)
|
||||
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
|
||||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
|
||||
function_minimum_size = 2;
|
||||
|
||||
unsigned int PrevLocSize = 0;
|
||||
char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE");
|
||||
if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE");
|
||||
char *ctx_str = getenv("AFL_LLVM_CTX");
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
unsigned int ngram_size = 0;
|
||||
/* Decide previous location vector size (must be a power of two) */
|
||||
VectorType *PrevLocTy;
|
||||
|
||||
if (ngram_size_str)
|
||||
if (sscanf(ngram_size_str, "%u", &ngram_size) != 1 || ngram_size < 2 ||
|
||||
ngram_size > NGRAM_SIZE_MAX)
|
||||
FATAL(
|
||||
"Bad value of AFL_NGRAM_SIZE (must be between 2 and NGRAM_SIZE_MAX "
|
||||
"(%u))",
|
||||
NGRAM_SIZE_MAX);
|
||||
|
||||
if (ngram_size)
|
||||
PrevLocSize = ngram_size - 1;
|
||||
else
|
||||
#else
|
||||
if (ngram_size_str)
|
||||
#ifdef LLVM_VERSION_STRING
|
||||
FATAL(
|
||||
"Sorry, NGRAM branch coverage is not supported with llvm version %s!",
|
||||
LLVM_VERSION_STRING);
|
||||
#else
|
||||
#ifndef LLVM_VERSION_PATCH
|
||||
FATAL(
|
||||
"Sorry, NGRAM branch coverage is not supported with llvm version %d.%d.%d!",
|
||||
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0);
|
||||
#else
|
||||
FATAL(
|
||||
"Sorry, NGRAM branch coverage is not supported with llvm version %d.%d.%d!",
|
||||
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERISON_PATCH);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
PrevLocSize = 1;
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
// IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
|
||||
uint64_t PrevLocVecSize = PowerOf2Ceil(PrevLocSize);
|
||||
IntegerType *IntLocTy =
|
||||
IntegerType::getIntNTy(C, sizeof(PREV_LOC_T) * CHAR_BIT);
|
||||
if (ngram_size) PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize);
|
||||
#endif
|
||||
|
||||
/* Get globals for the SHM region and the previous location. Note that
|
||||
__afl_prev_loc is thread-local. */
|
||||
|
||||
GlobalVariable *AFLMapPtr =
|
||||
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
GlobalVariable *AFLPrevLoc;
|
||||
GlobalVariable *AFLContext;
|
||||
LoadInst * PrevCtx = NULL; // for CTX sensitive coverage
|
||||
|
||||
if (ctx_str)
|
||||
#ifdef __ANDROID__
|
||||
AFLContext = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
|
||||
#else
|
||||
AFLContext = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx",
|
||||
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
#endif
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ngram_size)
|
||||
#ifdef __ANDROID__
|
||||
AFLPrevLoc = new GlobalVariable(
|
||||
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
|
||||
/* Initializer */ nullptr, "__afl_prev_loc");
|
||||
#else
|
||||
AFLPrevLoc = new GlobalVariable(
|
||||
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
|
||||
/* Initializer */ nullptr, "__afl_prev_loc",
|
||||
/* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel,
|
||||
/* AddressSpace */ 0, /* IsExternallyInitialized */ false);
|
||||
#endif
|
||||
else
|
||||
#endif
|
||||
#ifdef __ANDROID__
|
||||
AFLPrevLoc = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
|
||||
#else
|
||||
AFLPrevLoc = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0,
|
||||
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
#endif
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
/* Create the vector shuffle mask for updating the previous block history.
|
||||
Note that the first element of the vector will store cur_loc, so just set
|
||||
it to undef to allow the optimizer to do its thing. */
|
||||
|
||||
SmallVector<Constant *, 32> PrevLocShuffle = {UndefValue::get(Int32Ty)};
|
||||
|
||||
for (unsigned I = 0; I < PrevLocSize - 1; ++I)
|
||||
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I));
|
||||
|
||||
for (unsigned I = PrevLocSize; I < PrevLocVecSize; ++I)
|
||||
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize));
|
||||
|
||||
Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle);
|
||||
#endif
|
||||
|
||||
// this is our default
|
||||
MarkSetOpt = true;
|
||||
|
||||
LLVMContext &C = M.getContext();
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
|
||||
GlobalVariable *CovMapPtr = new GlobalVariable(
|
||||
M, PointerType::getUnqual(Int8Ty), false, GlobalValue::ExternalLinkage,
|
||||
nullptr, "__afl_area_ptr");
|
||||
|
||||
GlobalVariable *OldPrev = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0,
|
||||
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
|
||||
ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
|
||||
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
|
||||
ConstantInt *One32 = ConstantInt::get(Int32Ty, 1);
|
||||
|
||||
u64 total_rs = 0;
|
||||
u64 total_hs = 0;
|
||||
@ -194,138 +268,11 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
if (!isInWhitelist(&F)) continue;
|
||||
|
||||
// if the function below our minimum size skip it (1 or 2)
|
||||
if (F.size() < function_minimum_size) { continue; }
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
bool instrumentBlock = false;
|
||||
DebugLoc Loc;
|
||||
StringRef instFilename;
|
||||
unsigned int instLine = 0;
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
for (auto &BB : F) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
if (!Loc) Loc = IP->getDebugLoc();
|
||||
|
||||
}
|
||||
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
instLine = cDILoc->getLine();
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
for (auto &BB : F) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
if (Loc.isUnknown()) Loc = IP->getDebugLoc();
|
||||
|
||||
}
|
||||
|
||||
if (!Loc.isUnknown()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(C));
|
||||
|
||||
instLine = cDILoc.getLineNumber();
|
||||
instFilename = cDILoc.getFilename();
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) {
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
if (!instFilename.str().empty())
|
||||
SAYF(cYEL "[!] " cBRI
|
||||
"Not in whitelist, skipping %s line %u...\n",
|
||||
instFilename.str().c_str(), instLine);
|
||||
else
|
||||
SAYF(cYEL "[!] " cBRI
|
||||
"No filename information found, skipping it");
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isBlacklisted(&F)) continue;
|
||||
|
||||
std::unordered_set<BasicBlock *> MS;
|
||||
if (!MarkSetOpt) {
|
||||
|
||||
@ -401,13 +348,34 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
if (function_minimum_size < 2) {
|
||||
for (BasicBlock &BB : F) {
|
||||
|
||||
for (BasicBlock &BB : F) {
|
||||
if (MS.find(&BB) == MS.end()) { continue; }
|
||||
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||
|
||||
if (MS.find(&BB) == MS.end()) { continue; }
|
||||
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||
IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), OldPrev);
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ngram_size) {
|
||||
|
||||
LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc);
|
||||
PrevLoc->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
Value *ShuffledPrevLoc = IRB.CreateShuffleVector(
|
||||
PrevLoc, UndefValue::get(PrevLocTy), PrevLocShuffleMask);
|
||||
Value *UpdatedPrevLoc = IRB.CreateInsertElement(
|
||||
ShuffledPrevLoc, ConstantInt::get(Int32Ty, genLabel()),
|
||||
(uint64_t)0);
|
||||
|
||||
IRB.CreateStore(UpdatedPrevLoc, AFLPrevLoc)
|
||||
->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
} else
|
||||
|
||||
#endif
|
||||
{
|
||||
|
||||
IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), AFLPrevLoc);
|
||||
|
||||
}
|
||||
|
||||
@ -415,18 +383,67 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
int has_calls = 0;
|
||||
for (BasicBlock &BB : F) {
|
||||
|
||||
auto PI = pred_begin(&BB);
|
||||
auto PE = pred_end(&BB);
|
||||
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||
Value * L = NULL;
|
||||
unsigned int cur_loc;
|
||||
|
||||
// Context sensitive coverage
|
||||
if (ctx_str && &BB == &F.getEntryBlock()) {
|
||||
|
||||
PrevCtx = IRB.CreateLoad(AFLContext);
|
||||
PrevCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
// does the function have calls? and is any of the calls larger than
|
||||
// one basic block?
|
||||
has_calls = 0;
|
||||
for (auto &BB : F) {
|
||||
|
||||
if (has_calls) break;
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CallInst *callInst = nullptr;
|
||||
if ((callInst = dyn_cast<CallInst>(&IN))) {
|
||||
|
||||
Function *Callee = callInst->getCalledFunction();
|
||||
if (!Callee || Callee->size() < function_minimum_size)
|
||||
continue;
|
||||
else {
|
||||
|
||||
has_calls = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if yes we store a context ID for this function in the global var
|
||||
if (has_calls) {
|
||||
|
||||
ConstantInt *NewCtx = ConstantInt::get(Int32Ty, genLabel());
|
||||
StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
|
||||
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
} // END of ctx_str
|
||||
|
||||
if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; }
|
||||
|
||||
auto PI = pred_begin(&BB);
|
||||
auto PE = pred_end(&BB);
|
||||
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||
Value * L = NULL;
|
||||
if (PI == PE) {
|
||||
|
||||
if (function_minimum_size < 2 && PI == PE) {
|
||||
|
||||
L = ConstantInt::get(Int32Ty, genLabel());
|
||||
cur_loc = genLabel();
|
||||
L = ConstantInt::get(Int32Ty, cur_loc);
|
||||
|
||||
} else {
|
||||
|
||||
@ -437,6 +454,7 @@ struct InsTrim : public ModulePass {
|
||||
BasicBlock *PBB = *PI;
|
||||
auto It = PredMap.insert({PBB, genLabel()});
|
||||
unsigned Label = It.first->second;
|
||||
cur_loc = Label;
|
||||
PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
|
||||
|
||||
}
|
||||
@ -446,15 +464,37 @@ struct InsTrim : public ModulePass {
|
||||
}
|
||||
|
||||
/* Load prev_loc */
|
||||
LoadInst *PrevLoc = IRB.CreateLoad(OldPrev);
|
||||
LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc);
|
||||
PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
|
||||
Value *PrevLocTrans;
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
/* "For efficiency, we propose to hash the tuple as a key into the
|
||||
hit_count map as (prev_block_trans << 1) ^ curr_block_trans, where
|
||||
prev_block_trans = (block_trans_1 ^ ... ^ block_trans_(n-1)" */
|
||||
|
||||
if (ngram_size)
|
||||
PrevLocTrans =
|
||||
IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty());
|
||||
else
|
||||
#endif
|
||||
PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
|
||||
|
||||
if (ctx_str)
|
||||
PrevLocTrans =
|
||||
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty);
|
||||
|
||||
/* Load SHM pointer */
|
||||
LoadInst *MapPtr = IRB.CreateLoad(CovMapPtr);
|
||||
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
Value *MapPtrIdx =
|
||||
IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, L));
|
||||
Value *MapPtrIdx;
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ngram_size)
|
||||
MapPtrIdx = IRB.CreateGEP(
|
||||
MapPtr, IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, L), Int32Ty));
|
||||
else
|
||||
#endif
|
||||
MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocTrans, L));
|
||||
|
||||
/* Update bitmap */
|
||||
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
|
||||
@ -467,8 +507,7 @@ struct InsTrim : public ModulePass {
|
||||
NULL) // with llvm 9 we make this the default as the bug in llvm is
|
||||
// then fixed
|
||||
#else
|
||||
if (1) // with llvm 9 we make this the default as the bug in llvm is
|
||||
// then fixed
|
||||
if (!skip_nozero)
|
||||
#endif
|
||||
{
|
||||
|
||||
@ -491,12 +530,20 @@ struct InsTrim : public ModulePass {
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
// save the actually location ID to OldPrev if function_minimum_size > 1
|
||||
if (function_minimum_size > 1) {
|
||||
if (ctx_str && has_calls) {
|
||||
|
||||
Value *Shr = IRB.CreateLShr(L, One32);
|
||||
IRB.CreateStore(Shr, OldPrev)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
// in CTX mode we have to restore the original context for the
|
||||
// caller - she might be calling other functions which need the
|
||||
// correct CTX
|
||||
Instruction *Inst = BB.getTerminator();
|
||||
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
|
||||
|
||||
IRBuilder<> Post_IRB(Inst);
|
||||
StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,9 @@ typedef long double max_align_t;
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/CFG.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#else
|
||||
#include "llvm/Support/CFG.h"
|
||||
#include "llvm/Support/CFG.h"
|
||||
#endif
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
|
@ -1,88 +0,0 @@
|
||||
|
||||
markNodes
|
||||
->
|
||||
|
||||
whitelist:
|
||||
set meta information/context to functions? ask llvm-dev
|
||||
setAttribute/hasAttribute?
|
||||
|
||||
afl-ld:
|
||||
handle(=instrument) .a archives on the cmdline
|
||||
|
||||
afl-pass-lto-instrument.so:
|
||||
either a or b:
|
||||
a) use instrim
|
||||
b) start in main() or _init() and first otherwise (warn!)
|
||||
keep list of done functions
|
||||
final: go through function list and instrument those missing
|
||||
|
||||
|
||||
|
||||
---------------------------
|
||||
|
||||
|
||||
|
||||
for (auto &module : Ctx.getModules()) {
|
||||
auto &functionList = module->getModule()->getFunctionList();
|
||||
for (auto &function : functionList) {
|
||||
for (auto &bb : function) {
|
||||
for (auto &instruction : bb) {
|
||||
if (CallInst *callInst = dyn_cast<CallInst>(&instruction)) {
|
||||
if (Function *calledFunction = callInst->getCalledFunction()) {
|
||||
if (calledFunction->getName().startswith("llvm.dbg.declare")) {
|
||||
|
||||
|
||||
for (auto &U : F.getUsers()) { <- unbekannt
|
||||
if (auto CS = CallSite(U)) {
|
||||
if (CS->getCalledFunction() == F)
|
||||
|
||||
getCalledValue()->stripPointerCasts()
|
||||
-> for indirect calls
|
||||
|
||||
|
||||
CallGraph(M)
|
||||
|
||||
|
||||
|
||||
#include "llvm/IR/CallSite.h"
|
||||
|
||||
unsigned int indirect_call_cnt = 0;
|
||||
|
||||
printf("Function: %s\n", F.getName().str().c_str());
|
||||
int cnt=0;
|
||||
for (auto *U : F.users()) {
|
||||
// auto *I = dyn_cast<Instruction>(U);
|
||||
// if (I) {
|
||||
// if (cast<CallInst>(I)->getCalledFunction()->getName() == F.getName()) {
|
||||
// printf("DIRECT CALL %s->%s->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), cast<CallInst>(I)->getCalledFunction()->getName().str().c_str(), F.getName().str().c_str());
|
||||
// }
|
||||
printf("Callsite #%d\n", ++cnt);
|
||||
CallSite CS(U);
|
||||
auto *I = CS.getInstruction();
|
||||
if (I) {
|
||||
Value *called = CS.getCalledValue()->stripPointerCasts();
|
||||
Function* f = dyn_cast<Function>(called);
|
||||
if (f->getName().size() > 0) {
|
||||
printf("test %s->%s->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), f->getName().str().c_str(), F.getName().str().c_str());
|
||||
if (f->getName() == F.getName()) {
|
||||
printf("CALL %s->%s->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), f->getName().str().c_str(), F.getName().str().c_str());
|
||||
}
|
||||
} else
|
||||
printf("FOO %s->...->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), F.getName().str().c_str());
|
||||
if (cast<CallInst>(I)->getCalledFunction()->getName() == F.getName()) {
|
||||
printf("DIRECT %s->%s->%s\n", cast<CallInst>(I)->getParent()->getParent()->getName().str().c_str(), cast<CallInst>(I)->getCalledFunction()->getName().str().c_str(), F.getName().str().c_str());
|
||||
}
|
||||
} else {
|
||||
printf("WE MISSED SOMETHING HERE!!\n");
|
||||
indirect_call_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
oder:
|
||||
for (auto *U : F.users()) {
|
||||
if (auto CS = CallSite(U->getUser())) {
|
||||
if (CS->isCallee(&U)) {
|
||||
// foo
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ This version requires a current llvm 11 compiled from the github master.
|
||||
|
||||
1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better
|
||||
coverage than anything else that is out there in the AFL world
|
||||
1a. Set AFL_LLVM_INSTRUMENT=CFG if you want the InsTrimLTO version
|
||||
(recommended)
|
||||
|
||||
2. You can use it together with llvm_mode: laf-intel and whitelisting
|
||||
features and can be combined with cmplog/Redqueen
|
||||
@ -14,6 +16,11 @@ This version requires a current llvm 11 compiled from the github master.
|
||||
|
||||
4. AUTODICTIONARY feature! see below
|
||||
|
||||
5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib` also
|
||||
note that if that target uses _init functions or early constructors then
|
||||
also set `AFL_LLVM_MAP_DYNAMIC=1` as your target will crash otherwise
|
||||
|
||||
|
||||
## Introduction and problem description
|
||||
|
||||
A big issue with how afl/afl++ works is that the basic block IDs that are
|
||||
@ -41,7 +48,7 @@ and many dead ends until we got to this:
|
||||
-fsanitize=coverage edge coverage mode :)
|
||||
|
||||
The result:
|
||||
* 10-20% speed gain compared to llvm_mode
|
||||
* 10-25% speed gain compared to llvm_mode
|
||||
* guaranteed non-colliding edge coverage :-)
|
||||
* The compile time especially for libraries can be longer
|
||||
|
||||
@ -65,7 +72,7 @@ $ cd build
|
||||
$ cmake -DLLVM_ENABLE_PROJECTS='clang;clang-tools-extra;compiler-rt;libclc;libcxx;libcxxabi;libunwind;lld' -DCMAKE_BUILD_TYPE=Release -DLLVM_BINUTILS_INCDIR=/usr/include/ ../llvm/
|
||||
$ make -j $(nproc)
|
||||
$ export PATH=`pwd`/bin:$PATH
|
||||
$ export LLVM_CONFIG=`pwd`/bin/llcm-config
|
||||
$ export LLVM_CONFIG=`pwd`/bin/llvm-config
|
||||
$ cd /path/to/AFLplusplus/
|
||||
$ make
|
||||
$ cd llvm_mode
|
||||
@ -80,11 +87,13 @@ Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc.
|
||||
|
||||
Also whitelisting (AFL_LLVM_WHITELIST -> [README.whitelist.md](README.whitelist.md)) and
|
||||
laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work.
|
||||
Instrim does not - but we can not really use it anyway for our approach.
|
||||
InsTrim (control flow graph instrumentation) is supported and recommended!
|
||||
(set `AFL_LLVM_INSTRUMENT=CFG`)
|
||||
|
||||
Example:
|
||||
```
|
||||
CC=afl-clang-lto CXX=afl-clang-lto++ ./configure
|
||||
CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure
|
||||
export AFL_LLVM_INSTRUMENT=CFG
|
||||
make
|
||||
```
|
||||
|
||||
@ -95,6 +104,17 @@ target binary based on string compare and memory compare functions.
|
||||
afl-fuzz will automatically get these transmitted when starting to fuzz.
|
||||
This improves coverage on a lot of targets.
|
||||
|
||||
## Fixed memory map
|
||||
|
||||
To speed up fuzzing, the shared memory map is hard set to a specific address,
|
||||
by default 0x10000. In most cases this will work without any problems.
|
||||
On unusual operating systems/processors/kernels or weird libraries this might
|
||||
fail so to change the fixed address at compile time set
|
||||
AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address
|
||||
to be dynamic - the original afl way, which is slower).
|
||||
AFL_LLVM_MAP_DYNAMIC can be set so the shared memory address is dynamic (which
|
||||
is safer but also slower).
|
||||
|
||||
## Potential issues
|
||||
|
||||
### compiling libraries fails
|
||||
@ -110,14 +130,36 @@ Solution:
|
||||
```
|
||||
AR=llvm-ar RANLIB=llvm-ranlib CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --disable-shared
|
||||
```
|
||||
and on some target you have to to AR=/RANLIB= even for make as the configure script does not save it ...
|
||||
and on some target you have to to AR=/RANLIB= even for make as the configure script does not save it.
|
||||
Other targets ignore environment variables and need the parameters set via
|
||||
`./configure --cc=... --cxx= --ranlib= ...` etc. (I am looking at you ffmpeg!).
|
||||
|
||||
### compiling programs still fail
|
||||
|
||||
afl-clang-lto is still work in progress.
|
||||
Please report issues at:
|
||||
|
||||
Known issues:
|
||||
* Anything that llvm 11 cannot compile, afl-clang-lto can not compile either - obviously
|
||||
* Anything that does not compile with LTO, afl-clang-lto can not compile either - obviously
|
||||
|
||||
Hence if building a target with afl-clang-lto fails try to build it with llvm11
|
||||
and LTO enabled (`CC=clang-11` `CXX=clang++-11` `CFLAGS=-flto=full` and
|
||||
`CXXFLAGS=-flto=full`).
|
||||
|
||||
An example that does not build with llvm 11 and LTO is ffmpeg.
|
||||
|
||||
If this succeeeds then there is an issue with afl-clang-lto. Please report at
|
||||
[https://github.com/AFLplusplus/AFLplusplus/issues/226](https://github.com/AFLplusplus/AFLplusplus/issues/226)
|
||||
|
||||
### Target crashes immediately
|
||||
|
||||
If the target is using early constructors (priority values smaller than 6)
|
||||
or have their own _init/.init functions and these are instrumented then the
|
||||
target will likely crash when started. This can be avoided by compiling with
|
||||
`AFL_LLVM_MAP_DYNAMIC=1` .
|
||||
|
||||
This can e.g. happen with OpenSSL.
|
||||
|
||||
## Upcoming Work
|
||||
|
||||
1. Currently the LTO whitelist feature does not allow to instrument main,
|
||||
|
@ -37,7 +37,26 @@ co-exists with the original code.
|
||||
|
||||
The idea and much of the implementation comes from Laszlo Szekeres.
|
||||
|
||||
## 2) How to use this
|
||||
## 2a) How to use this - short
|
||||
|
||||
Set the `LLVM_CONFIG` variable to the clang version you want to use, e.g.
|
||||
```
|
||||
LLVM_CONFIG=llvm-config-9 make
|
||||
```
|
||||
In case you have your own compiled llvm version specify the full path:
|
||||
```
|
||||
LLVM_CONFIG=~/llvm-project/build/bin/llvm-config make
|
||||
```
|
||||
If you try to use a new llvm version on an old Linux this can fail because of
|
||||
old c++ libraries. In this case usually switching to gcc/g++ to compile
|
||||
llvm_mode will work:
|
||||
```
|
||||
LLVM_CONFIG=llvm-config-7 REAL_CC=gcc REAL_CXX=g++ make
|
||||
```
|
||||
It is highly recommended to use the newest clang version you can put your
|
||||
hands on :)
|
||||
|
||||
## 2b) How to use this - long
|
||||
|
||||
In order to leverage this mechanism, you need to have clang installed on your
|
||||
system. You should also make sure that the llvm-config tool is in your path
|
||||
|
@ -20,8 +20,16 @@ This is implemented in afl-gcc, however for llvm_mode this is optional if
|
||||
the llvm version is below 9 - as there is a perfomance bug that is only fixed
|
||||
in version 9 and onwards.
|
||||
|
||||
If you want to enable this for llvm < 9 then set
|
||||
If you want to enable this for llvm versions below 9 then set
|
||||
|
||||
```
|
||||
export AFL_LLVM_NOT_ZERO=1
|
||||
```
|
||||
|
||||
In case you are on llvm 9 or greater and you do not want this behaviour then
|
||||
you can set:
|
||||
```
|
||||
AFL_LLVM_SKIP_NEVERZERO=1
|
||||
```
|
||||
If the target does not have extensive loops or functions that are called
|
||||
a lot then this can give a small performance boost.
|
||||
|
@ -43,13 +43,13 @@ static u8 * obj_path; /* Path to runtime libraries */
|
||||
static u8 **cc_params; /* Parameters passed to the real CC */
|
||||
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
||||
static u8 llvm_fullpath[PATH_MAX];
|
||||
static u8 instrument_mode;
|
||||
static u8 * lto_flag = AFL_CLANG_FLTO;
|
||||
static u8 * march_opt = CFLAGS_OPT;
|
||||
static u8 debug;
|
||||
static u8 cwd[4096];
|
||||
static u8 cmplog_mode;
|
||||
u8 use_stdin = 0; /* dummy */
|
||||
static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode, cpp_mode;
|
||||
static u8 *lto_flag = AFL_CLANG_FLTO;
|
||||
static u8 *march_opt = CFLAGS_OPT;
|
||||
static u8 debug;
|
||||
static u8 cwd[4096];
|
||||
static u8 cmplog_mode;
|
||||
u8 use_stdin = 0; /* dummy */
|
||||
|
||||
enum {
|
||||
|
||||
@ -60,14 +60,15 @@ enum {
|
||||
INSTRUMENT_INSTRIM = 2,
|
||||
INSTRUMENT_CFG = 2,
|
||||
INSTRUMENT_LTO = 3,
|
||||
INSTRUMENT_CTX = 4,
|
||||
INSTRUMENT_NGRAM = 5 // + ngram value of 2-16 = 7 - 21
|
||||
INSTRUMENT_OPT_CTX = 4,
|
||||
INSTRUMENT_OPT_NGRAM = 8
|
||||
|
||||
};
|
||||
|
||||
char instrument_mode_string[6][16] = {
|
||||
char instrument_mode_string[10][16] = {
|
||||
|
||||
"DEFAULT", "PCGUARD", "CFG", "LTO", "CTX",
|
||||
"CLASSIC", "PCGUARD", "CFG", "LTO", "CTX", "",
|
||||
"", "", "NGRAM", ""
|
||||
|
||||
};
|
||||
|
||||
@ -169,7 +170,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
else
|
||||
++name;
|
||||
|
||||
if (instrument_mode == INSTRUMENT_LTO)
|
||||
if (lto_mode)
|
||||
if (lto_flag[0] != '-')
|
||||
FATAL(
|
||||
"Using afl-clang-lto is not possible because Makefile magic did not "
|
||||
@ -183,6 +184,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
else
|
||||
sprintf(llvm_fullpath, CLANGPP_BIN);
|
||||
cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)llvm_fullpath;
|
||||
cpp_mode = 1;
|
||||
|
||||
} else if (!strcmp(name, "afl-clang-fast") ||
|
||||
|
||||
@ -204,12 +206,18 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
/* There are three ways to compile with afl-clang-fast. In the traditional
|
||||
if (lto_mode && cpp_mode)
|
||||
cc_params[cc_par_cnt++] = "-lc++"; // needed by fuzzbench, early
|
||||
|
||||
/* There are several ways to compile with afl-clang-fast. In the traditional
|
||||
mode, we use afl-llvm-pass.so, then there is libLLVMInsTrim.so which is
|
||||
much faster but has less coverage. Finally there is the experimental
|
||||
'trace-pc-guard' mode, we use native LLVM instrumentation callbacks
|
||||
instead. For trace-pc-guard see:
|
||||
faster and creates less map pollution.
|
||||
Then there is the 'trace-pc-guard' mode, we use native LLVM
|
||||
instrumentation callbacks instead. For trace-pc-guard see:
|
||||
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
|
||||
The best instrumentatation is with the LTO modes, the classic and
|
||||
InsTrimLTO, the latter is faster. The LTO modes are activated by using
|
||||
afl-clang-lto(++)
|
||||
*/
|
||||
|
||||
// laf
|
||||
@ -226,8 +234,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
if (getenv("LAF_TRANSFORM_COMPARES") ||
|
||||
getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
|
||||
|
||||
if (!be_quiet && getenv("AFL_LLVM_LTO_AUTODICTIONARY") &&
|
||||
instrument_mode != INSTRUMENT_LTO)
|
||||
if (!be_quiet && getenv("AFL_LLVM_LTO_AUTODICTIONARY") && lto_mode)
|
||||
WARNF(
|
||||
"using AFL_LLVM_LAF_TRANSFORM_COMPARES together with "
|
||||
"AFL_LLVM_LTO_AUTODICTIONARY makes no sense. Use only "
|
||||
@ -280,7 +287,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (instrument_mode == INSTRUMENT_LTO) {
|
||||
if (lto_mode) {
|
||||
|
||||
if (getenv("AFL_LLVM_WHITELIST") != NULL) {
|
||||
|
||||
@ -294,8 +301,12 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD);
|
||||
cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition";
|
||||
cc_params[cc_par_cnt++] = alloc_printf(
|
||||
"-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path);
|
||||
if (instrument_mode == INSTRUMENT_CFG)
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so", obj_path);
|
||||
else
|
||||
cc_params[cc_par_cnt++] = alloc_printf(
|
||||
"-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path);
|
||||
cc_params[cc_par_cnt++] = lto_flag;
|
||||
|
||||
} else {
|
||||
@ -322,6 +333,22 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Qunused-arguments";
|
||||
|
||||
// in case LLVM is installed not via a package manager or "make install"
|
||||
// e.g. compiled download or compiled from github then it's ./lib directory
|
||||
// might not be in the search path. Add it if so.
|
||||
u8 *libdir = strdup(LLVM_LIBDIR);
|
||||
if (cpp_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) &&
|
||||
strncmp(libdir, "/lib", 4)) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-rpath";
|
||||
cc_params[cc_par_cnt++] = libdir;
|
||||
|
||||
} else {
|
||||
|
||||
free(libdir);
|
||||
|
||||
}
|
||||
|
||||
/* Detect stray -v calls from ./configure scripts. */
|
||||
|
||||
while (--argc) {
|
||||
@ -390,7 +417,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
if (getenv("AFL_USE_CFISAN")) {
|
||||
|
||||
if (instrument_mode != INSTRUMENT_LTO) {
|
||||
if (!lto_mode) {
|
||||
|
||||
uint32_t i = 0, found = 0;
|
||||
while (envp[i] != NULL && !found)
|
||||
@ -416,9 +443,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
|
||||
getenv("LAF_TRANSFORM_COMPARES") ||
|
||||
(instrument_mode == INSTRUMENT_LTO &&
|
||||
(getenv("AFL_LLVM_LTO_AUTODICTIONARY") ||
|
||||
getenv("AFL_LLVM_AUTODICTIONARY")))) {
|
||||
(lto_mode && (getenv("AFL_LLVM_LTO_AUTODICTIONARY") ||
|
||||
getenv("AFL_LLVM_AUTODICTIONARY")))) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
|
||||
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
|
||||
@ -499,7 +525,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
case 0:
|
||||
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path);
|
||||
if (instrument_mode == INSTRUMENT_LTO)
|
||||
if (lto_mode)
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/afl-llvm-rt-lto.o", obj_path);
|
||||
break;
|
||||
@ -508,7 +534,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path);
|
||||
if (access(cc_params[cc_par_cnt - 1], R_OK))
|
||||
FATAL("-m32 is not supported by your compiler");
|
||||
if (instrument_mode == INSTRUMENT_LTO) {
|
||||
if (lto_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path);
|
||||
@ -523,7 +549,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path);
|
||||
if (access(cc_params[cc_par_cnt - 1], R_OK))
|
||||
FATAL("-m64 is not supported by your compiler");
|
||||
if (instrument_mode == INSTRUMENT_LTO) {
|
||||
if (lto_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path);
|
||||
@ -547,7 +573,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
|
||||
int i;
|
||||
char *callname = "afl-clang-fast", *ptr;
|
||||
char *callname = "afl-clang-fast", *ptr = NULL;
|
||||
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
|
||||
@ -562,54 +588,6 @@ int main(int argc, char **argv, char **envp) {
|
||||
instrument_mode = INSTRUMENT_PCGUARD;
|
||||
#endif
|
||||
|
||||
if ((ptr = getenv("AFL_LLVM_INSTRUMENT")) != NULL) {
|
||||
|
||||
if (strncasecmp(ptr, "default", strlen("default")) == 0 ||
|
||||
strncasecmp(ptr, "afl", strlen("afl")) == 0 ||
|
||||
strncasecmp(ptr, "classic", strlen("classic")) == 0)
|
||||
instrument_mode = INSTRUMENT_DEFAULT;
|
||||
if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 ||
|
||||
strncasecmp(ptr, "instrim", strlen("instrim")) == 0)
|
||||
instrument_mode = INSTRUMENT_CFG;
|
||||
else if (strncasecmp(ptr, "pc-guard", strlen("pc-guard")) == 0 ||
|
||||
strncasecmp(ptr, "pcguard", strlen("pcgard")) == 0)
|
||||
instrument_mode = INSTRUMENT_PCGUARD;
|
||||
else if (strncasecmp(ptr, "lto", strlen("lto")) == 0)
|
||||
instrument_mode = INSTRUMENT_LTO;
|
||||
else if (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) {
|
||||
|
||||
instrument_mode = INSTRUMENT_CTX;
|
||||
setenv("AFL_LLVM_CTX", "1", 1);
|
||||
|
||||
} else if (strncasecmp(ptr, "ngram", strlen("ngram")) == 0) {
|
||||
|
||||
ptr += strlen("ngram");
|
||||
while (*ptr && (*ptr < '0' || *ptr > '9'))
|
||||
ptr++;
|
||||
if (!*ptr)
|
||||
if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) != NULL)
|
||||
FATAL(
|
||||
"you must set the NGRAM size with (e.g. for value 2) "
|
||||
"AFL_LLVM_INSTRUMENT=ngram-2");
|
||||
instrument_mode = INSTRUMENT_NGRAM + atoi(ptr);
|
||||
if (instrument_mode < INSTRUMENT_NGRAM + 2 ||
|
||||
instrument_mode > INSTRUMENT_NGRAM + NGRAM_SIZE_MAX)
|
||||
FATAL(
|
||||
"NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX "
|
||||
"(%u)",
|
||||
NGRAM_SIZE_MAX);
|
||||
|
||||
ptr = alloc_printf("%u", instrument_mode - INSTRUMENT_NGRAM);
|
||||
setenv("AFL_LLVM_NGRAM_SIZE", ptr, 1);
|
||||
|
||||
} else if (strncasecmp(ptr, "classic", strlen("classic")) != 0 ||
|
||||
|
||||
strncasecmp(ptr, "default", strlen("default")) != 0 ||
|
||||
strncasecmp(ptr, "afl", strlen("afl")) != 0)
|
||||
FATAL("unknown AFL_LLVM_INSTRUMENT value: %s", ptr);
|
||||
|
||||
}
|
||||
|
||||
if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
|
||||
getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) {
|
||||
|
||||
@ -631,66 +609,180 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_CTX")) {
|
||||
|
||||
if (instrument_mode == 0)
|
||||
instrument_mode = INSTRUMENT_CTX;
|
||||
else if (instrument_mode != INSTRUMENT_CTX)
|
||||
FATAL("you can not set AFL_LLVM_INSTRUMENT and AFL_LLVM_CTX together");
|
||||
|
||||
}
|
||||
if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX;
|
||||
|
||||
if (getenv("AFL_LLVM_NGRAM_SIZE")) {
|
||||
|
||||
if (instrument_mode == 0) {
|
||||
|
||||
instrument_mode = INSTRUMENT_NGRAM + atoi(getenv("AFL_LLVM_NGRAM_SIZE"));
|
||||
if (instrument_mode < INSTRUMENT_NGRAM + 2 ||
|
||||
instrument_mode > INSTRUMENT_NGRAM + NGRAM_SIZE_MAX)
|
||||
FATAL(
|
||||
"NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX "
|
||||
"(%u)",
|
||||
NGRAM_SIZE_MAX);
|
||||
|
||||
} else if (instrument_mode != INSTRUMENT_NGRAM)
|
||||
|
||||
instrument_opt_mode |= INSTRUMENT_OPT_NGRAM;
|
||||
ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE"));
|
||||
if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX)
|
||||
FATAL(
|
||||
"you can not set AFL_LLVM_INSTRUMENT and AFL_LLVM_NGRAM_SIZE "
|
||||
"together");
|
||||
"NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX "
|
||||
"(%u)",
|
||||
NGRAM_SIZE_MAX);
|
||||
|
||||
}
|
||||
|
||||
if (instrument_mode < INSTRUMENT_NGRAM)
|
||||
ptr = instrument_mode_string[instrument_mode];
|
||||
else
|
||||
ptr = alloc_printf("NGRAM-%u", instrument_mode - INSTRUMENT_NGRAM);
|
||||
if (getenv("AFL_LLVM_INSTRUMENT")) {
|
||||
|
||||
if (strstr(argv[0], "afl-clang-lto") != NULL) {
|
||||
u8 *ptr = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;");
|
||||
|
||||
if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO) {
|
||||
while (ptr) {
|
||||
|
||||
callname = "afl-clang-lto";
|
||||
instrument_mode = INSTRUMENT_LTO;
|
||||
ptr = instrument_mode_string[instrument_mode];
|
||||
if (strncasecmp(ptr, "default", strlen("default")) == 0 ||
|
||||
strncasecmp(ptr, "afl", strlen("afl")) == 0 ||
|
||||
strncasecmp(ptr, "classic", strlen("classic")) == 0) {
|
||||
|
||||
} else {
|
||||
if (!instrument_mode || instrument_mode == INSTRUMENT_DEFAULT)
|
||||
instrument_mode = INSTRUMENT_DEFAULT;
|
||||
else
|
||||
FATAL("main instrumentation mode already set with %s",
|
||||
instrument_mode_string[instrument_mode]);
|
||||
|
||||
if (!be_quiet)
|
||||
WARNF("afl-clang-lto called with mode %s, using that mode instead",
|
||||
ptr);
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr, "pc-guard", strlen("pc-guard")) == 0 ||
|
||||
strncasecmp(ptr, "pcguard", strlen("pcgard")) == 0) {
|
||||
|
||||
if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD)
|
||||
instrument_mode = INSTRUMENT_PCGUARD;
|
||||
else
|
||||
FATAL("main instrumentation mode already set with %s",
|
||||
instrument_mode_string[instrument_mode]);
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 ||
|
||||
strncasecmp(ptr, "instrim", strlen("instrim")) == 0) {
|
||||
|
||||
if (instrument_mode == INSTRUMENT_LTO) {
|
||||
|
||||
instrument_mode = INSTRUMENT_CFG;
|
||||
lto_mode = 1;
|
||||
|
||||
} else if (!instrument_mode || instrument_mode == INSTRUMENT_CFG)
|
||||
|
||||
instrument_mode = INSTRUMENT_CFG;
|
||||
else
|
||||
FATAL("main instrumentation mode already set with %s",
|
||||
instrument_mode_string[instrument_mode]);
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr, "lto", strlen("lto")) == 0) {
|
||||
|
||||
lto_mode = 1;
|
||||
if (!instrument_mode || instrument_mode == INSTRUMENT_LTO)
|
||||
instrument_mode = INSTRUMENT_LTO;
|
||||
else if (instrument_mode != INSTRUMENT_CFG)
|
||||
FATAL("main instrumentation mode already set with %s",
|
||||
instrument_mode_string[instrument_mode]);
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) {
|
||||
|
||||
instrument_opt_mode |= INSTRUMENT_OPT_CTX;
|
||||
setenv("AFL_LLVM_CTX", "1", 1);
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr, "ngram", strlen("ngram")) == 0) {
|
||||
|
||||
ptr += strlen("ngram");
|
||||
while (*ptr && (*ptr < '0' || *ptr > '9'))
|
||||
ptr++;
|
||||
if (!*ptr)
|
||||
if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) != NULL)
|
||||
FATAL(
|
||||
"you must set the NGRAM size with (e.g. for value 2) "
|
||||
"AFL_LLVM_INSTRUMENT=ngram-2");
|
||||
ngram_size = atoi(ptr);
|
||||
if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX)
|
||||
FATAL(
|
||||
"NGRAM instrumentation option must be between 2 and "
|
||||
"NGRAM_SIZE_MAX "
|
||||
"(%u)",
|
||||
NGRAM_SIZE_MAX);
|
||||
instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM);
|
||||
ptr = alloc_printf("%u", ngram_size);
|
||||
setenv("AFL_LLVM_NGRAM_SIZE", ptr, 1);
|
||||
|
||||
}
|
||||
|
||||
ptr = strtok(NULL, ":,;");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (strstr(argv[0], "afl-clang-lto") != NULL) {
|
||||
|
||||
if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO ||
|
||||
instrument_mode == INSTRUMENT_CFG) {
|
||||
|
||||
lto_mode = 1;
|
||||
callname = "afl-clang-lto";
|
||||
if (!instrument_mode) {
|
||||
|
||||
instrument_mode = INSTRUMENT_LTO;
|
||||
ptr = instrument_mode_string[instrument_mode];
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!be_quiet)
|
||||
WARNF("afl-clang-lto called with mode %s, using that mode instead",
|
||||
instrument_mode_string[instrument_mode]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (instrument_opt_mode && lto_mode)
|
||||
FATAL(
|
||||
"CTX and NGRAM can not be used in LTO mode (and would make LTO "
|
||||
"useless)");
|
||||
|
||||
if (!instrument_opt_mode) {
|
||||
|
||||
if (lto_mode && instrument_mode == INSTRUMENT_CFG)
|
||||
ptr = alloc_printf("InsTrimLTO");
|
||||
else
|
||||
ptr = instrument_mode_string[instrument_mode];
|
||||
|
||||
} else if (instrument_opt_mode == INSTRUMENT_OPT_CTX)
|
||||
|
||||
ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]);
|
||||
else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM)
|
||||
ptr = alloc_printf("%s + NGRAM-%u", instrument_mode_string[instrument_mode],
|
||||
ngram_size);
|
||||
else
|
||||
ptr = alloc_printf("%s + CTX + NGRAM-%u",
|
||||
instrument_mode_string[instrument_mode], ngram_size);
|
||||
|
||||
#ifndef AFL_CLANG_FLTO
|
||||
if (instrument_mode == INSTRUMENT_LTO)
|
||||
FATAL("instrumentation mode LTO specified but LLVM support not available");
|
||||
if (lto_mode)
|
||||
FATAL(
|
||||
"instrumentation mode LTO specified but LLVM support not available "
|
||||
"(requires LLVM 11 or higher)");
|
||||
#endif
|
||||
|
||||
if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC &&
|
||||
instrument_mode != INSTRUMENT_CFG)
|
||||
FATAL(
|
||||
"CTX and NGRAM instrumentation options can only be used with CFG "
|
||||
"(recommended) and CLASSIC instrumentation modes!");
|
||||
|
||||
if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO"))
|
||||
FATAL(
|
||||
"AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set "
|
||||
"together");
|
||||
|
||||
if (argc < 2 || strcmp(argv[1], "-h") == 0) {
|
||||
|
||||
if (instrument_mode != INSTRUMENT_LTO)
|
||||
if (!lto_mode)
|
||||
printf("afl-clang-fast" VERSION " by <lszekeres@google.com> in %s mode\n",
|
||||
ptr);
|
||||
else
|
||||
@ -726,6 +818,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
"AFL_HARDEN: adds code hardening to catch memory bugs\n"
|
||||
"AFL_INST_RATIO: percentage of branches to instrument\n"
|
||||
"AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n"
|
||||
"AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n"
|
||||
"AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n"
|
||||
"AFL_LLVM_LAF_SPLIT_FLOATS: transform floating point comp. to "
|
||||
"cascaded "
|
||||
@ -751,14 +844,14 @@ int main(int argc, char **argv, char **envp) {
|
||||
"\nafl-clang-fast specific environment variables:\n"
|
||||
"AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n"
|
||||
"AFL_LLVM_INSTRUMENT: set instrumentation mode: DEFAULT, CFG "
|
||||
"(INSTRIM), LTO, CTX, NGRAM-2 ... NGRAM-16\n"
|
||||
"(INSTRIM), PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n"
|
||||
" You can also use the old environment variables instead:"
|
||||
" AFL_LLVM_CTX: use context sensitive coverage\n"
|
||||
" AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n"
|
||||
" AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage\n"
|
||||
" AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n"
|
||||
" AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed (sub "
|
||||
"option to INSTRIM)\n");
|
||||
"option to INSTRIM)\n"
|
||||
" AFL_LLVM_CTX: use context sensitive coverage\n"
|
||||
" AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage\n");
|
||||
|
||||
#ifdef AFL_CLANG_FLTO
|
||||
SAYF(
|
||||
@ -787,7 +880,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
getenv("AFL_DEBUG") != NULL) {
|
||||
|
||||
if (instrument_mode != INSTRUMENT_LTO)
|
||||
if (!lto_mode)
|
||||
|
||||
SAYF(cCYA "afl-clang-fast" VERSION cRST
|
||||
" by <lszekeres@google.com> in %s mode\n",
|
||||
@ -802,7 +895,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
u8 *ptr2;
|
||||
if (!be_quiet && instrument_mode != INSTRUMENT_LTO &&
|
||||
if (!be_quiet && !lto_mode &&
|
||||
((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) {
|
||||
|
||||
u32 map_size = atoi(ptr2);
|
||||
|
242
llvm_mode/afl-llvm-common.cc
Normal file
242
llvm_mode/afl-llvm-common.cc
Normal file
@ -0,0 +1,242 @@
|
||||
#define AFL_LLVM_PASS
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static std::list<std::string> myWhitelist;
|
||||
|
||||
char *getBBName(const llvm::BasicBlock *BB) {
|
||||
|
||||
static char *name;
|
||||
|
||||
if (!BB->getName().empty()) {
|
||||
|
||||
name = strdup(BB->getName().str().c_str());
|
||||
return name;
|
||||
|
||||
}
|
||||
|
||||
std::string Str;
|
||||
raw_string_ostream OS(Str);
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
BB->printAsOperand(OS, false);
|
||||
#endif
|
||||
name = strdup(OS.str().c_str());
|
||||
return name;
|
||||
|
||||
}
|
||||
|
||||
/* Function that we never instrument or analyze */
|
||||
/* Note: this blacklist check is also called in isInWhitelist() */
|
||||
bool isBlacklisted(const llvm::Function *F) {
|
||||
|
||||
// Starting from "LLVMFuzzer" these are functions used in libfuzzer based
|
||||
// fuzzing campaign installations, e.g. oss-fuzz
|
||||
|
||||
static const char *Blacklist[] = {
|
||||
|
||||
"asan.",
|
||||
"llvm.",
|
||||
"sancov.",
|
||||
"__ubsan_handle_",
|
||||
"ign.",
|
||||
"__afl_",
|
||||
"_fini",
|
||||
"__libc_csu",
|
||||
"__asan",
|
||||
"__msan",
|
||||
"msan.",
|
||||
"LLVMFuzzer",
|
||||
"maybe_duplicate_stderr",
|
||||
"discard_output",
|
||||
"close_stdout",
|
||||
"dup_and_close_stderr",
|
||||
"maybe_close_fd_mask",
|
||||
"ExecuteFilesOnyByOne"
|
||||
|
||||
};
|
||||
|
||||
for (auto const &BlacklistFunc : Blacklist) {
|
||||
|
||||
if (F->getName().startswith(BlacklistFunc)) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void initWhitelist() {
|
||||
|
||||
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 isInWhitelist(llvm::Function *F) {
|
||||
|
||||
// is this a function with code? If it is external we dont instrument it
|
||||
// anyway and cant be in the whitelist. Or if it is blacklisted.
|
||||
if (!F->size() || isBlacklisted(F)) return false;
|
||||
|
||||
// if we do not have a whitelist return true
|
||||
if (myWhitelist.empty()) return true;
|
||||
|
||||
// let's try to get the filename for the function
|
||||
auto bb = &F->getEntryBlock();
|
||||
BasicBlock::iterator IP = bb->getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(), it->length(),
|
||||
*it) == 0) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(F->getContext()));
|
||||
|
||||
unsigned int instLine = cDILoc.getLineNumber();
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
|
||||
(void)instLine;
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(), it->length(),
|
||||
*it) == 0) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
else {
|
||||
|
||||
// we could not find out the location. in this case we say it is not
|
||||
// in the whitelist
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// Calculate the number of average collisions that would occur if all
|
||||
// location IDs would be assigned randomly (like normal afl/afl++).
|
||||
// This uses the "balls in bins" algorithm.
|
||||
unsigned long long int calculateCollisions(uint32_t edges) {
|
||||
|
||||
double bins = MAP_SIZE;
|
||||
double balls = edges;
|
||||
double step1 = 1 - (1 / bins);
|
||||
double step2 = pow(step1, balls);
|
||||
double step3 = bins * step2;
|
||||
double step4 = round(step3);
|
||||
unsigned long long int empty = step4;
|
||||
unsigned long long int collisions = edges - (MAP_SIZE - empty);
|
||||
return collisions;
|
||||
|
||||
}
|
||||
|
42
llvm_mode/afl-llvm-common.h
Normal file
42
llvm_mode/afl-llvm-common.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef __AFLLLVMCOMMON_H
|
||||
#define __AFLLLVMCOMMON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5
|
||||
typedef long double max_align_t;
|
||||
#endif
|
||||
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#else
|
||||
#include "llvm/DebugInfo.h"
|
||||
#include "llvm/Support/CFG.h"
|
||||
#endif
|
||||
|
||||
char * getBBName(const llvm::BasicBlock *BB);
|
||||
bool isBlacklisted(const llvm::Function *F);
|
||||
void initWhitelist();
|
||||
bool isInWhitelist(llvm::Function *F);
|
||||
unsigned long long int calculateCollisions(uint32_t edges);
|
||||
|
||||
#endif
|
||||
|
935
llvm_mode/afl-llvm-lto-instrim.so.cc
Normal file
935
llvm_mode/afl-llvm-lto-instrim.so.cc
Normal file
@ -0,0 +1,935 @@
|
||||
/*
|
||||
american fuzzy lop++ - LLVM-mode instrumentation pass
|
||||
---------------------------------------------------
|
||||
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
This library is plugged into LLVM when invoking clang through afl-clang-fast
|
||||
or afl-clang-lto with AFL_LLVM_INSTRUMENT=CFG or =INSTRIM
|
||||
|
||||
*/
|
||||
|
||||
#define AFL_LLVM_PASS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/MemorySSAUpdater.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
|
||||
#include "MarkNodes.h"
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static cl::opt<bool> MarkSetOpt("markset", cl::desc("MarkSet"),
|
||||
cl::init(false));
|
||||
static cl::opt<bool> LoopHeadOpt("loophead", cl::desc("LoopHead"),
|
||||
cl::init(false));
|
||||
|
||||
namespace {
|
||||
|
||||
struct InsTrimLTO : public ModulePass {
|
||||
|
||||
protected:
|
||||
uint32_t function_minimum_size = 1;
|
||||
char * skip_nozero = NULL;
|
||||
int afl_global_id = 1, debug = 0, autodictionary = 0;
|
||||
uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0;
|
||||
uint64_t map_addr = 0x10000;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
InsTrimLTO() : ModulePass(ID) {
|
||||
|
||||
char *ptr;
|
||||
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL)
|
||||
if ((afl_global_id = atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE)
|
||||
FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n",
|
||||
ptr, MAP_SIZE - 1);
|
||||
|
||||
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
|
||||
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
|
||||
ModulePass::getAnalysisUsage(AU);
|
||||
AU.addRequired<DominatorTreeWrapperPass>();
|
||||
AU.addRequired<LoopInfoWrapperPass>();
|
||||
|
||||
}
|
||||
|
||||
StringRef getPassName() const override {
|
||||
|
||||
return "InstTrim LTO Instrumentation";
|
||||
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override {
|
||||
|
||||
char be_quiet = 0;
|
||||
char *ptr;
|
||||
|
||||
if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
|
||||
|
||||
SAYF(cCYA "InsTrimLTO" VERSION cRST
|
||||
" by csienslab and Marc \"vanHauser\" Heuse\n");
|
||||
|
||||
} else
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
/* Process environment variables */
|
||||
|
||||
if (getenv("AFL_LLVM_AUTODICTIONARY") ||
|
||||
getenv("AFL_LLVM_LTO_AUTODICTIONARY"))
|
||||
autodictionary = 1;
|
||||
|
||||
if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0;
|
||||
|
||||
if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) {
|
||||
|
||||
uint64_t val;
|
||||
if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) {
|
||||
|
||||
map_addr = 0;
|
||||
|
||||
} else if (map_addr == 0) {
|
||||
|
||||
FATAL(
|
||||
"AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used "
|
||||
"together");
|
||||
|
||||
} else if (strncmp(ptr, "0x", 2) != 0) {
|
||||
|
||||
map_addr = 0x10000; // the default
|
||||
|
||||
} else {
|
||||
|
||||
val = strtoull(ptr, NULL, 16);
|
||||
if (val < 0x100 || val > 0xffffffff00000000) {
|
||||
|
||||
FATAL(
|
||||
"AFL_LLVM_MAP_ADDR must be a value between 0x100 and "
|
||||
"0xffffffff00000000");
|
||||
|
||||
}
|
||||
|
||||
map_addr = val;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); }
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL ||
|
||||
getenv("LOOPHEAD") != NULL) {
|
||||
|
||||
LoopHeadOpt = true;
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
|
||||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
|
||||
function_minimum_size = 2;
|
||||
|
||||
// this is our default
|
||||
MarkSetOpt = true;
|
||||
|
||||
/* Initialize LLVM instrumentation */
|
||||
|
||||
LLVMContext & C = M.getContext();
|
||||
std::vector<std::string> dictionary;
|
||||
std::vector<CallInst *> calls;
|
||||
DenseMap<Value *, std::string *> valueMap;
|
||||
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
|
||||
|
||||
ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
|
||||
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
|
||||
|
||||
/* Get/set globals for the SHM region. */
|
||||
|
||||
GlobalVariable *AFLMapPtr = NULL;
|
||||
Value * MapPtrFixed = NULL;
|
||||
|
||||
if (!map_addr) {
|
||||
|
||||
AFLMapPtr =
|
||||
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
|
||||
} else {
|
||||
|
||||
ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
|
||||
MapPtrFixed =
|
||||
ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty));
|
||||
|
||||
}
|
||||
|
||||
if (autodictionary) {
|
||||
|
||||
/* Some implementation notes.
|
||||
*
|
||||
* We try to handle 3 cases:
|
||||
* - memcmp("foo", arg, 3) <- literal string
|
||||
* - static char globalvar[] = "foo";
|
||||
* memcmp(globalvar, arg, 3) <- global variable
|
||||
* - char localvar[] = "foo";
|
||||
* memcmp(locallvar, arg, 3) <- local variable
|
||||
*
|
||||
* The local variable case is the hardest. We can only detect that
|
||||
* case if there is no reassignment or change in the variable.
|
||||
* And it might not work across llvm version.
|
||||
* What we do is hooking the initializer function for local variables
|
||||
* (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned
|
||||
* variable. And if that variable is then used in a compare function
|
||||
* we use that noted string.
|
||||
* This seems not to work for tokens that have a size <= 4 :-(
|
||||
*
|
||||
* - if the compared length is smaller than the string length we
|
||||
* save the full string. This is likely better for fuzzing but
|
||||
* might be wrong in a few cases depending on optimizers
|
||||
*
|
||||
* - not using StringRef because there is a bug in the llvm 11
|
||||
* checkout I am using which sometimes points to wrong strings
|
||||
*
|
||||
* Over and out. Took me a full day. damn. mh/vh
|
||||
*/
|
||||
|
||||
for (Function &F : M) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CallInst *callInst = nullptr;
|
||||
|
||||
if ((callInst = dyn_cast<CallInst>(&IN))) {
|
||||
|
||||
bool isStrcmp = true;
|
||||
bool isMemcmp = true;
|
||||
bool isStrncmp = true;
|
||||
bool isStrcasecmp = true;
|
||||
bool isStrncasecmp = true;
|
||||
bool isIntMemcpy = true;
|
||||
bool addedNull = false;
|
||||
uint8_t optLen = 0;
|
||||
|
||||
Function *Callee = callInst->getCalledFunction();
|
||||
if (!Callee) continue;
|
||||
if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
|
||||
std::string FuncName = Callee->getName().str();
|
||||
isStrcmp &= !FuncName.compare("strcmp");
|
||||
isMemcmp &= !FuncName.compare("memcmp");
|
||||
isStrncmp &= !FuncName.compare("strncmp");
|
||||
isStrcasecmp &= !FuncName.compare("strcasecmp");
|
||||
isStrncasecmp &= !FuncName.compare("strncasecmp");
|
||||
isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64");
|
||||
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||
!isStrncasecmp && !isIntMemcpy)
|
||||
continue;
|
||||
|
||||
/* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp
|
||||
* function prototype */
|
||||
FunctionType *FT = Callee->getFunctionType();
|
||||
|
||||
isStrcmp &= FT->getNumParams() == 2 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) ==
|
||||
IntegerType::getInt8PtrTy(M.getContext());
|
||||
isStrcasecmp &= FT->getNumParams() == 2 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) ==
|
||||
IntegerType::getInt8PtrTy(M.getContext());
|
||||
isMemcmp &= FT->getNumParams() == 3 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0)->isPointerTy() &&
|
||||
FT->getParamType(1)->isPointerTy() &&
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
isStrncmp &= FT->getNumParams() == 3 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) ==
|
||||
IntegerType::getInt8PtrTy(M.getContext()) &&
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
isStrncasecmp &= FT->getNumParams() == 3 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) ==
|
||||
IntegerType::getInt8PtrTy(M.getContext()) &&
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||
!isStrncasecmp && !isIntMemcpy)
|
||||
continue;
|
||||
|
||||
/* is a str{n,}{case,}cmp/memcmp, check if we have
|
||||
* str{case,}cmp(x, "const") or str{case,}cmp("const", x)
|
||||
* strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x,
|
||||
* ..) memcmp(x, "const", ..) or memcmp("const", x, ..) */
|
||||
Value *Str1P = callInst->getArgOperand(0),
|
||||
*Str2P = callInst->getArgOperand(1);
|
||||
std::string Str1, Str2;
|
||||
StringRef TmpStr;
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, TmpStr);
|
||||
if (TmpStr.empty())
|
||||
HasStr1 = false;
|
||||
else
|
||||
Str1 = TmpStr.str();
|
||||
bool HasStr2 = getConstantStringInfo(Str2P, TmpStr);
|
||||
if (TmpStr.empty())
|
||||
HasStr2 = false;
|
||||
else
|
||||
Str2 = TmpStr.str();
|
||||
|
||||
if (debug)
|
||||
fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n",
|
||||
FuncName.c_str(), Str1P, Str1P->getName().str().c_str(),
|
||||
Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P,
|
||||
Str2P->getName().str().c_str(), Str2.c_str(),
|
||||
HasStr2 == true ? "true" : "false");
|
||||
|
||||
// we handle the 2nd parameter first because of llvm memcpy
|
||||
if (!HasStr2) {
|
||||
|
||||
auto *Ptr = dyn_cast<ConstantExpr>(Str2P);
|
||||
if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
|
||||
|
||||
if (auto *Var =
|
||||
dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
|
||||
|
||||
if (Var->hasInitializer()) {
|
||||
|
||||
if (auto *Array = dyn_cast<ConstantDataArray>(
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString().str();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for the internal memcpy routine we only care for the second
|
||||
// parameter and are not reporting anything.
|
||||
if (isIntMemcpy == true) {
|
||||
|
||||
if (HasStr2 == true) {
|
||||
|
||||
Value * op2 = callInst->getArgOperand(2);
|
||||
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
|
||||
if (ilen) {
|
||||
|
||||
uint64_t literalLength = Str2.size();
|
||||
uint64_t optLength = ilen->getZExtValue();
|
||||
if (literalLength + 1 == optLength) {
|
||||
|
||||
Str2.append("\0", 1); // add null byte
|
||||
addedNull = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
valueMap[Str1P] = new std::string(Str2);
|
||||
|
||||
if (debug)
|
||||
fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
// Neither a literal nor a global variable?
|
||||
// maybe it is a local variable that we saved
|
||||
if (!HasStr2) {
|
||||
|
||||
std::string *strng = valueMap[Str2P];
|
||||
if (strng && !strng->empty()) {
|
||||
|
||||
Str2 = *strng;
|
||||
HasStr2 = true;
|
||||
if (debug)
|
||||
fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(),
|
||||
Str2P);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!HasStr1) {
|
||||
|
||||
auto Ptr = dyn_cast<ConstantExpr>(Str1P);
|
||||
|
||||
if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
|
||||
|
||||
if (auto *Var =
|
||||
dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
|
||||
|
||||
if (Var->hasInitializer()) {
|
||||
|
||||
if (auto *Array = dyn_cast<ConstantDataArray>(
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString().str();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Neither a literal nor a global variable?
|
||||
// maybe it is a local variable that we saved
|
||||
if (!HasStr1) {
|
||||
|
||||
std::string *strng = valueMap[Str1P];
|
||||
if (strng && !strng->empty()) {
|
||||
|
||||
Str1 = *strng;
|
||||
HasStr1 = true;
|
||||
if (debug)
|
||||
fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(),
|
||||
Str1P);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* handle cases of one string is const, one string is variable */
|
||||
if (!(HasStr1 ^ HasStr2)) continue;
|
||||
|
||||
std::string thestring;
|
||||
|
||||
if (HasStr1)
|
||||
thestring = Str1;
|
||||
else
|
||||
thestring = Str2;
|
||||
|
||||
optLen = thestring.length();
|
||||
|
||||
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
||||
|
||||
Value * op2 = callInst->getArgOperand(2);
|
||||
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
|
||||
if (ilen) {
|
||||
|
||||
uint64_t literalLength = optLen;
|
||||
optLen = ilen->getZExtValue();
|
||||
if (literalLength + 1 == optLen) { // add null byte
|
||||
thestring.append("\0", 1);
|
||||
addedNull = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// add null byte if this is a string compare function and a null
|
||||
// was not already added
|
||||
if (addedNull == false && !isMemcmp) {
|
||||
|
||||
thestring.append("\0", 1); // add null byte
|
||||
optLen++;
|
||||
|
||||
}
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
std::string outstring;
|
||||
fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen,
|
||||
(unsigned int)thestring.length());
|
||||
for (uint8_t i = 0; i < thestring.length(); i++) {
|
||||
|
||||
uint8_t c = thestring[i];
|
||||
if (c <= 32 || c >= 127)
|
||||
fprintf(stderr, "\\x%02x", c);
|
||||
else
|
||||
fprintf(stderr, "%c", c);
|
||||
|
||||
}
|
||||
|
||||
fprintf(stderr, "\"\n");
|
||||
|
||||
}
|
||||
|
||||
// we take the longer string, even if the compare was to a
|
||||
// shorter part. Note that depending on the optimizer of the
|
||||
// compiler this can be wrong, but it is more likely that this
|
||||
// is helping the fuzzer
|
||||
if (optLen != thestring.length()) optLen = thestring.length();
|
||||
if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA;
|
||||
if (optLen < MIN_AUTO_EXTRA) // too short? skip
|
||||
continue;
|
||||
|
||||
dictionary.push_back(thestring.substr(0, optLen));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* InsTrim instrumentation starts here */
|
||||
|
||||
u64 total_rs = 0;
|
||||
u64 total_hs = 0;
|
||||
|
||||
for (Function &F : M) {
|
||||
|
||||
if (debug) {
|
||||
|
||||
uint32_t bb_cnt = 0;
|
||||
|
||||
for (auto &BB : F)
|
||||
if (BB.size() > 0) ++bb_cnt;
|
||||
SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n",
|
||||
F.getName().str().c_str(), F.size(), bb_cnt);
|
||||
|
||||
}
|
||||
|
||||
// if the function below our minimum size skip it (1 or 2)
|
||||
if (F.size() < function_minimum_size) continue;
|
||||
if (isBlacklisted(&F)) continue;
|
||||
|
||||
std::unordered_set<BasicBlock *> MS;
|
||||
if (!MarkSetOpt) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
MS.insert(&BB);
|
||||
|
||||
}
|
||||
|
||||
total_rs += F.size();
|
||||
|
||||
} else {
|
||||
|
||||
auto Result = markNodes(&F);
|
||||
auto RS = Result.first;
|
||||
auto HS = Result.second;
|
||||
|
||||
MS.insert(RS.begin(), RS.end());
|
||||
if (!LoopHeadOpt) {
|
||||
|
||||
MS.insert(HS.begin(), HS.end());
|
||||
total_rs += MS.size();
|
||||
|
||||
} else {
|
||||
|
||||
DenseSet<std::pair<BasicBlock *, BasicBlock *>> EdgeSet;
|
||||
DominatorTreeWrapperPass * DTWP =
|
||||
&getAnalysis<DominatorTreeWrapperPass>(F);
|
||||
auto DT = &DTWP->getDomTree();
|
||||
|
||||
total_rs += RS.size();
|
||||
total_hs += HS.size();
|
||||
|
||||
for (BasicBlock *BB : HS) {
|
||||
|
||||
bool Inserted = false;
|
||||
for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) {
|
||||
|
||||
auto Edge = BasicBlockEdge(*BI, BB);
|
||||
if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) {
|
||||
|
||||
EdgeSet.insert({*BI, BB});
|
||||
Inserted = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!Inserted) {
|
||||
|
||||
MS.insert(BB);
|
||||
total_rs += 1;
|
||||
total_hs -= 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) {
|
||||
|
||||
auto PredBB = I->first;
|
||||
auto SuccBB = I->second;
|
||||
auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT,
|
||||
nullptr, nullptr, false);
|
||||
MS.insert(NewBB);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (BasicBlock &BB : F) {
|
||||
|
||||
auto PI = pred_begin(&BB);
|
||||
auto PE = pred_end(&BB);
|
||||
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||
Value * L = NULL;
|
||||
|
||||
if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; }
|
||||
|
||||
if (PI == PE) {
|
||||
|
||||
L = ConstantInt::get(Int32Ty, afl_global_id++);
|
||||
|
||||
} else {
|
||||
|
||||
auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin());
|
||||
DenseMap<BasicBlock *, unsigned> PredMap;
|
||||
for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) {
|
||||
|
||||
BasicBlock *PBB = *PI;
|
||||
auto It = PredMap.insert({PBB, afl_global_id++});
|
||||
unsigned Label = It.first->second;
|
||||
PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
|
||||
|
||||
}
|
||||
|
||||
L = PN;
|
||||
|
||||
}
|
||||
|
||||
/* Load SHM pointer */
|
||||
Value *MapPtrIdx;
|
||||
|
||||
if (map_addr) {
|
||||
|
||||
MapPtrIdx = IRB.CreateGEP(MapPtrFixed, L);
|
||||
|
||||
} else {
|
||||
|
||||
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
MapPtrIdx = IRB.CreateGEP(MapPtr, L);
|
||||
|
||||
}
|
||||
|
||||
/* Update bitmap */
|
||||
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
|
||||
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
Value *Incr = IRB.CreateAdd(Counter, One);
|
||||
|
||||
if (skip_nozero) {
|
||||
|
||||
auto cf = IRB.CreateICmpEQ(Incr, Zero);
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
|
||||
}
|
||||
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
// done :)
|
||||
|
||||
inst_blocks++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// save highest location ID to global variable
|
||||
// do this after each function to fail faster
|
||||
if (!be_quiet && afl_global_id > MAP_SIZE &&
|
||||
afl_global_id > FS_OPT_MAX_MAPSIZE) {
|
||||
|
||||
uint32_t pow2map = 1, map = afl_global_id;
|
||||
while ((map = map >> 1))
|
||||
pow2map++;
|
||||
WARNF(
|
||||
"We have %u blocks to instrument but the map size is only %u. Either "
|
||||
"edit config.h and set MAP_SIZE_POW2 from %u to %u, then recompile "
|
||||
"afl-fuzz and llvm_mode and then make this target - or set "
|
||||
"AFL_MAP_SIZE with at least size %u when running afl-fuzz with this "
|
||||
"target.",
|
||||
afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id);
|
||||
|
||||
}
|
||||
|
||||
if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) {
|
||||
|
||||
// yes we could create our own function, insert it into ctors ...
|
||||
// but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o
|
||||
|
||||
Function *f = M.getFunction("__afl_auto_init_globals");
|
||||
|
||||
if (!f) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Error: init function could not be found (this should not "
|
||||
"happen)\n");
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
BasicBlock *bb = &f->getEntryBlock();
|
||||
if (!bb) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Error: init function does not have an EntryBlock (this should "
|
||||
"not happen)\n");
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
BasicBlock::iterator IP = bb->getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
|
||||
if (map_addr) {
|
||||
|
||||
GlobalVariable *AFLMapAddrFixed =
|
||||
new GlobalVariable(M, Int64Ty, true, GlobalValue::ExternalLinkage,
|
||||
0, "__afl_map_addr");
|
||||
ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
|
||||
StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed);
|
||||
StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) {
|
||||
|
||||
uint32_t write_loc = afl_global_id;
|
||||
|
||||
if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3);
|
||||
|
||||
GlobalVariable *AFLFinalLoc =
|
||||
new GlobalVariable(M, Int32Ty, true, GlobalValue::ExternalLinkage,
|
||||
0, "__afl_final_loc");
|
||||
ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc);
|
||||
StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
|
||||
StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
if (dictionary.size()) {
|
||||
|
||||
size_t memlen = 0, count = 0, offset = 0;
|
||||
char * ptr;
|
||||
|
||||
for (auto token : dictionary) {
|
||||
|
||||
memlen += token.length();
|
||||
count++;
|
||||
|
||||
}
|
||||
|
||||
if (!be_quiet)
|
||||
printf("AUTODICTIONARY: %lu string%s found\n", count,
|
||||
count == 1 ? "" : "s");
|
||||
|
||||
if (count) {
|
||||
|
||||
if ((ptr = (char *)malloc(memlen + count)) == NULL) {
|
||||
|
||||
fprintf(stderr, "Error: malloc for %lu bytes failed!\n",
|
||||
memlen + count);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
for (auto token : dictionary) {
|
||||
|
||||
if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) {
|
||||
|
||||
ptr[offset++] = (uint8_t)token.length();
|
||||
memcpy(ptr + offset, token.c_str(), token.length());
|
||||
offset += token.length();
|
||||
count++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GlobalVariable *AFLDictionaryLen = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
|
||||
"__afl_dictionary_len");
|
||||
ConstantInt *const_len = ConstantInt::get(Int32Ty, offset);
|
||||
StoreInst * StoreDictLen =
|
||||
IRB.CreateStore(const_len, AFLDictionaryLen);
|
||||
StoreDictLen->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset);
|
||||
GlobalVariable *AFLInternalDictionary = new GlobalVariable(
|
||||
M, ArrayTy, true, GlobalValue::ExternalLinkage,
|
||||
ConstantDataArray::get(
|
||||
C, *(new ArrayRef<char>((char *)ptr, offset))),
|
||||
"__afl_internal_dictionary");
|
||||
AFLInternalDictionary->setInitializer(ConstantDataArray::get(
|
||||
C, *(new ArrayRef<char>((char *)ptr, offset))));
|
||||
AFLInternalDictionary->setConstant(true);
|
||||
|
||||
GlobalVariable *AFLDictionary = new GlobalVariable(
|
||||
M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_dictionary");
|
||||
|
||||
Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero);
|
||||
Value *AFLDictPtr =
|
||||
IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0));
|
||||
StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary);
|
||||
StoreDict->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// count basic blocks for comparison with classic instrumentation
|
||||
|
||||
u32 edges = 0;
|
||||
for (auto &F : M) {
|
||||
|
||||
if (F.size() < function_minimum_size) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
bool would_instrument = false;
|
||||
|
||||
for (BasicBlock *Pred : predecessors(&BB)) {
|
||||
|
||||
int count = 0;
|
||||
for (BasicBlock *Succ : successors(Pred))
|
||||
if (Succ != NULL) count++;
|
||||
|
||||
if (count > 1) return true;
|
||||
|
||||
}
|
||||
|
||||
if (would_instrument == true) edges++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Say something nice. */
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
if (!inst_blocks)
|
||||
WARNF("No instrumentation targets found.");
|
||||
else {
|
||||
|
||||
char modeline[100];
|
||||
snprintf(modeline, sizeof(modeline), "%s%s%s%s%s",
|
||||
getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
|
||||
getenv("AFL_USE_ASAN") ? ", ASAN" : "",
|
||||
getenv("AFL_USE_MSAN") ? ", MSAN" : "",
|
||||
getenv("AFL_USE_CFISAN") ? ", CFISAN" : "",
|
||||
getenv("AFL_USE_UBSAN") ? ", UBSAN" : "");
|
||||
OKF("Instrumented %u locations (%llu, %llu) with no collisions (on "
|
||||
"average %llu collisions would be in afl-gcc/afl-clang-fast for %u "
|
||||
"edges) (%s mode).",
|
||||
inst_blocks, total_rs, total_hs, calculateCollisions(edges), edges,
|
||||
modeline);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}; // end of struct InsTrim
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
char InsTrimLTO::ID = 0;
|
||||
|
||||
static void registerInsTrimLTO(const PassManagerBuilder &,
|
||||
legacy::PassManagerBase &PM) {
|
||||
|
||||
PM.add(new InsTrimLTO());
|
||||
|
||||
}
|
||||
|
||||
static RegisterPass<InsTrimLTO> X("afl-lto-instrim",
|
||||
"afl++ InsTrim LTO instrumentation pass",
|
||||
false, false);
|
||||
|
||||
static RegisterStandardPasses RegisterInsTrimLTO(
|
||||
PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerInsTrimLTO);
|
||||
|
@ -1,14 +1,9 @@
|
||||
/*
|
||||
american fuzzy lop++ - LLVM-mode instrumentation pass
|
||||
---------------------------------------------------
|
||||
american fuzzy lop++ - LLVM LTO instrumentation pass
|
||||
----------------------------------------------------
|
||||
|
||||
Written by Laszlo Szekeres <lszekeres@google.com> and
|
||||
Michal Zalewski
|
||||
Written by Marc Heuse <mh@mh-sec.de>
|
||||
|
||||
LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted
|
||||
from afl-as.c are Michal's fault.
|
||||
|
||||
Copyright 2015, 2016 Google Inc. All rights reserved.
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -17,9 +12,7 @@
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
This library is plugged into LLVM when invoking clang through afl-clang-fast.
|
||||
It tells the compiler to add code roughly equivalent to the bits discussed
|
||||
in ../afl-as.h.
|
||||
This library is plugged into LLVM when invoking clang through afl-clang-lto.
|
||||
|
||||
*/
|
||||
|
||||
@ -31,11 +24,13 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sys/time.h>
|
||||
#include <set>
|
||||
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
@ -55,7 +50,7 @@
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/Pass.h"
|
||||
|
||||
#include <set>
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -76,6 +71,8 @@ class AFLLTOPass : public ModulePass {
|
||||
FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n",
|
||||
ptr, MAP_SIZE - 1);
|
||||
|
||||
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
|
||||
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
@ -86,74 +83,14 @@ class AFLLTOPass : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
// Calculate the number of average collisions that would occur if all
|
||||
// location IDs would be assigned randomly (like normal afl/afl++).
|
||||
// This uses the "balls in bins" algorithm.
|
||||
unsigned long long int calculateCollisions(uint32_t edges) {
|
||||
|
||||
double bins = MAP_SIZE;
|
||||
double balls = edges;
|
||||
double step1 = 1 - (1 / bins);
|
||||
double step2 = pow(step1, balls);
|
||||
double step3 = bins * step2;
|
||||
double step4 = round(step3);
|
||||
unsigned long long int empty = step4;
|
||||
unsigned long long int collisions = edges - (MAP_SIZE - empty);
|
||||
return collisions;
|
||||
|
||||
}
|
||||
|
||||
// Get the internal llvm name of a basic block
|
||||
// This is an ugly debug support so it is commented out :-)
|
||||
/*
|
||||
static char *getBBName(const BasicBlock *BB) {
|
||||
|
||||
static char *name;
|
||||
|
||||
if (!BB->getName().empty()) {
|
||||
|
||||
name = strdup(BB->getName().str().c_str());
|
||||
return name;
|
||||
|
||||
}
|
||||
|
||||
std::string Str;
|
||||
raw_string_ostream OS(Str);
|
||||
|
||||
BB->printAsOperand(OS, false);
|
||||
|
||||
name = strdup(OS.str().c_str());
|
||||
|
||||
return name;
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
static bool isBlacklisted(const Function *F) {
|
||||
|
||||
static const char *Blacklist[] = {
|
||||
|
||||
"asan.", "llvm.", "sancov.", "__ubsan_handle_", "ign.",
|
||||
"__afl_", "_fini", "__libc_csu"
|
||||
|
||||
};
|
||||
|
||||
for (auto const &BlacklistFunc : Blacklist) {
|
||||
|
||||
if (F->getName().startswith(BlacklistFunc)) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
|
||||
protected:
|
||||
int afl_global_id = 1, debug = 0, autodictionary = 0;
|
||||
uint32_t function_minimum_size = 1;
|
||||
uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0;
|
||||
uint64_t map_addr = 0x10000;
|
||||
char * skip_nozero = NULL;
|
||||
|
||||
};
|
||||
|
||||
@ -165,11 +102,11 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
std::vector<std::string> dictionary;
|
||||
std::vector<CallInst *> calls;
|
||||
DenseMap<Value *, std::string *> valueMap;
|
||||
char * ptr;
|
||||
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
|
||||
|
||||
/* Show a banner */
|
||||
|
||||
@ -186,12 +123,65 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
getenv("AFL_LLVM_LTO_AUTODICTIONARY"))
|
||||
autodictionary = 1;
|
||||
|
||||
/* Get globals for the SHM region and the previous location. Note that
|
||||
__afl_prev_loc is thread-local. */
|
||||
if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0;
|
||||
|
||||
GlobalVariable *AFLMapPtr =
|
||||
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
|
||||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
|
||||
function_minimum_size = 2;
|
||||
|
||||
if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) {
|
||||
|
||||
uint64_t val;
|
||||
if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) {
|
||||
|
||||
map_addr = 0;
|
||||
|
||||
} else if (map_addr == 0) {
|
||||
|
||||
FATAL(
|
||||
"AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used together");
|
||||
|
||||
} else if (strncmp(ptr, "0x", 2) != 0) {
|
||||
|
||||
map_addr = 0x10000; // the default
|
||||
|
||||
} else {
|
||||
|
||||
val = strtoull(ptr, NULL, 16);
|
||||
if (val < 0x100 || val > 0xffffffff00000000) {
|
||||
|
||||
FATAL(
|
||||
"AFL_LLVM_MAP_ADDR must be a value between 0x100 and "
|
||||
"0xffffffff00000000");
|
||||
|
||||
}
|
||||
|
||||
map_addr = val;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); }
|
||||
|
||||
/* Get/set the globals for the SHM region. */
|
||||
|
||||
GlobalVariable *AFLMapPtr = NULL;
|
||||
Value * MapPtrFixed = NULL;
|
||||
|
||||
if (!map_addr) {
|
||||
|
||||
AFLMapPtr =
|
||||
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
|
||||
} else {
|
||||
|
||||
ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
|
||||
MapPtrFixed =
|
||||
ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty));
|
||||
|
||||
}
|
||||
|
||||
ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
|
||||
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
|
||||
@ -202,7 +192,9 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
for (auto &F : M) {
|
||||
|
||||
if (F.size() < 2) continue;
|
||||
// fprintf(stderr, "DEBUG: Function %s\n", F.getName().str().c_str());
|
||||
|
||||
if (F.size() < function_minimum_size) continue;
|
||||
if (isBlacklisted(&F)) continue;
|
||||
|
||||
std::vector<BasicBlock *> InsBlocks;
|
||||
@ -339,11 +331,15 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
|
||||
|
||||
if (auto *Array =
|
||||
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
|
||||
if (Var->hasInitializer()) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString().str();
|
||||
if (auto *Array = dyn_cast<ConstantDataArray>(
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString().str();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -411,11 +407,15 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
|
||||
|
||||
if (auto *Array =
|
||||
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
|
||||
if (Var->hasInitializer()) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString().str();
|
||||
if (auto *Array = dyn_cast<ConstantDataArray>(
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString().str();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -579,10 +579,20 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
/* Load SHM pointer */
|
||||
|
||||
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
Value *MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc);
|
||||
Value *MapPtrIdx;
|
||||
|
||||
if (map_addr) {
|
||||
|
||||
MapPtrIdx = IRB.CreateGEP(MapPtrFixed, CurLoc);
|
||||
|
||||
} else {
|
||||
|
||||
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc);
|
||||
|
||||
}
|
||||
|
||||
/* Update bitmap */
|
||||
|
||||
@ -592,9 +602,14 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
Value *Incr = IRB.CreateAdd(Counter, One);
|
||||
|
||||
auto cf = IRB.CreateICmpEQ(Incr, Zero);
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
if (skip_nozero) {
|
||||
|
||||
auto cf = IRB.CreateICmpEQ(Incr, Zero);
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
|
||||
}
|
||||
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
@ -612,7 +627,8 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
// save highest location ID to global variable
|
||||
// do this after each function to fail faster
|
||||
if (!be_quiet && afl_global_id > MAP_SIZE) {
|
||||
if (!be_quiet && afl_global_id > MAP_SIZE &&
|
||||
afl_global_id > FS_OPT_MAX_MAPSIZE) {
|
||||
|
||||
uint32_t pow2map = 1, map = afl_global_id;
|
||||
while ((map = map >> 1))
|
||||
@ -627,7 +643,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL || dictionary.size()) {
|
||||
if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) {
|
||||
|
||||
// yes we could create our own function, insert it into ctors ...
|
||||
// but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o
|
||||
@ -656,24 +672,29 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
BasicBlock::iterator IP = bb->getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
|
||||
if (map_addr) {
|
||||
|
||||
GlobalVariable *AFLMapAddrFixed = new GlobalVariable(
|
||||
M, Int64Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_map_addr");
|
||||
ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr);
|
||||
StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed);
|
||||
StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) {
|
||||
|
||||
uint32_t write_loc = afl_global_id;
|
||||
|
||||
if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3);
|
||||
|
||||
if (write_loc <= MAP_SIZE && write_loc <= 0x800000) {
|
||||
|
||||
GlobalVariable *AFLFinalLoc = new GlobalVariable(
|
||||
M, Int32Ty, true, GlobalValue::ExternalLinkage, 0,
|
||||
"__afl_final_loc", 0, GlobalVariable::GeneralDynamicTLSModel, 0,
|
||||
false);
|
||||
ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc);
|
||||
StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
|
||||
StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
GlobalVariable *AFLFinalLoc = new GlobalVariable(
|
||||
M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc");
|
||||
ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc);
|
||||
StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
|
||||
StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
@ -718,10 +739,9 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
GlobalVariable *AFLDictionaryLen = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
|
||||
"__afl_dictionary_len", 0, GlobalVariable::GeneralDynamicTLSModel,
|
||||
0, false);
|
||||
GlobalVariable *AFLDictionaryLen =
|
||||
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage,
|
||||
0, "__afl_dictionary_len");
|
||||
ConstantInt *const_len = ConstantInt::get(Int32Ty, offset);
|
||||
StoreInst *StoreDictLen = IRB.CreateStore(const_len, AFLDictionaryLen);
|
||||
StoreDictLen->setMetadata(M.getMDKindID("nosanitize"),
|
||||
@ -732,8 +752,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
M, ArrayTy, true, GlobalValue::ExternalLinkage,
|
||||
ConstantDataArray::get(C,
|
||||
*(new ArrayRef<char>((char *)ptr, offset))),
|
||||
"__afl_internal_dictionary", 0,
|
||||
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
"__afl_internal_dictionary");
|
||||
AFLInternalDictionary->setInitializer(ConstantDataArray::get(
|
||||
C, *(new ArrayRef<char>((char *)ptr, offset))));
|
||||
AFLInternalDictionary->setConstant(true);
|
||||
|
@ -46,6 +46,8 @@
|
||||
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
@ -86,25 +88,6 @@ class AFLwhitelist : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
// ripped from aflgo
|
||||
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;
|
||||
|
||||
// StringRef getPassName() const override {
|
||||
@ -128,7 +111,7 @@ bool AFLwhitelist::runOnModule(Module &M) {
|
||||
|
||||
char be_quiet = 0;
|
||||
|
||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||
if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
|
||||
|
||||
SAYF(cCYA "afl-llvm-lto-whitelist" VERSION cRST
|
||||
" by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n");
|
||||
|
@ -54,13 +54,14 @@ typedef long double max_align_t;
|
||||
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#else
|
||||
#include "llvm/DebugInfo.h"
|
||||
#include "llvm/Support/CFG.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#include "llvm/Support/CFG.h"
|
||||
#endif
|
||||
|
||||
#include "afl-llvm-common.h"
|
||||
#include "llvm-ngram-coverage.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -73,60 +74,18 @@ class AFLCoverage : public ModulePass {
|
||||
static char ID;
|
||||
AFLCoverage() : 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ripped from aflgo
|
||||
static bool isBlacklisted(const Function *F) {
|
||||
|
||||
static const char *Blacklist[] = {
|
||||
|
||||
"asan.",
|
||||
"llvm.",
|
||||
"sancov.",
|
||||
"__ubsan_handle_",
|
||||
|
||||
};
|
||||
|
||||
for (auto const &BlacklistFunc : Blacklist) {
|
||||
|
||||
if (F->getName().startswith(BlacklistFunc)) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
initWhitelist();
|
||||
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
|
||||
// StringRef getPassName() const override {
|
||||
|
||||
// return "American Fuzzy Lop Instrumentation";
|
||||
// }
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
uint32_t ngram_size = 0;
|
||||
uint32_t debug = 0;
|
||||
uint32_t map_size = MAP_SIZE;
|
||||
char * ctx_str = NULL;
|
||||
uint32_t ngram_size = 0;
|
||||
uint32_t debug = 0;
|
||||
uint32_t map_size = MAP_SIZE;
|
||||
uint32_t function_minimum_size = 1;
|
||||
char * ctx_str = NULL, *skip_nozero = NULL;
|
||||
|
||||
};
|
||||
|
||||
@ -156,7 +115,7 @@ uint64_t PowerOf2Ceil(unsigned in) {
|
||||
/* #if LLVM_VERSION_STRING >= "4.0.1" */
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1)
|
||||
#define AFL_HAVE_VECTOR_INTRINSICS 1
|
||||
#define AFL_HAVE_VECTOR_INTRINSICS 1
|
||||
#endif
|
||||
bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
@ -222,8 +181,13 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
char *neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO");
|
||||
#endif
|
||||
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
|
||||
|
||||
unsigned PrevLocSize;
|
||||
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
|
||||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
|
||||
function_minimum_size = 2;
|
||||
|
||||
unsigned PrevLocSize = 0;
|
||||
|
||||
char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE");
|
||||
if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE");
|
||||
@ -247,8 +211,15 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
else
|
||||
#else
|
||||
if (ngram_size_str)
|
||||
FATAL("Sorry, NGRAM branch coverage is not supported with llvm version %s!",
|
||||
LLVM_VERSION_STRING);
|
||||
#ifndef LLVM_VERSION_PATCH
|
||||
FATAL("Sorry, NGRAM branch coverage is not supported with llvm version %d.%d.%d!",
|
||||
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR,
|
||||
0);
|
||||
#else
|
||||
FATAL("Sorry, NGRAM branch coverage is not supported with llvm version %d.%d.%d!",
|
||||
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR,
|
||||
LLVM_VERSION_PATCH);
|
||||
#endif
|
||||
#endif
|
||||
PrevLocSize = 1;
|
||||
|
||||
@ -257,9 +228,6 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
if (ngram_size) PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize);
|
||||
#endif
|
||||
|
||||
if (ctx_str && ngram_size_str)
|
||||
FATAL("you must decide between NGRAM and CTX instrumentation");
|
||||
|
||||
/* Get globals for the SHM region and the previous location. Note that
|
||||
__afl_prev_loc is thread-local. */
|
||||
|
||||
@ -281,17 +249,17 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ngram_size)
|
||||
#ifdef __ANDROID__
|
||||
#ifdef __ANDROID__
|
||||
AFLPrevLoc = new GlobalVariable(
|
||||
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
|
||||
/* Initializer */ nullptr, "__afl_prev_loc");
|
||||
#else
|
||||
#else
|
||||
AFLPrevLoc = new GlobalVariable(
|
||||
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
|
||||
/* Initializer */ nullptr, "__afl_prev_loc",
|
||||
/* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel,
|
||||
/* AddressSpace */ 0, /* IsExternallyInitialized */ false);
|
||||
#endif
|
||||
#endif
|
||||
else
|
||||
#endif
|
||||
#ifdef __ANDROID__
|
||||
@ -336,116 +304,40 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
fprintf(stderr, "FUNCTION: %s (%zu)\n", F.getName().str().c_str(),
|
||||
F.size());
|
||||
|
||||
if (isBlacklisted(&F)) continue;
|
||||
if (!isInWhitelist(&F)) continue;
|
||||
|
||||
// AllocaInst *CallingContext = nullptr;
|
||||
|
||||
if (ctx_str && F.size() > 1) { // Context sensitive coverage
|
||||
// load the context ID of the previous function and write to to a local
|
||||
// variable on the stack
|
||||
auto bb = &F.getEntryBlock();
|
||||
BasicBlock::iterator IP = bb->getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
PrevCtx = IRB.CreateLoad(AFLContext);
|
||||
PrevCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
// does the function have calls? and is any of the calls larger than one
|
||||
// basic block?
|
||||
has_calls = 0;
|
||||
for (auto &BB : F) {
|
||||
|
||||
if (has_calls) break;
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CallInst *callInst = nullptr;
|
||||
if ((callInst = dyn_cast<CallInst>(&IN))) {
|
||||
|
||||
Function *Callee = callInst->getCalledFunction();
|
||||
if (!Callee || Callee->size() < 2)
|
||||
continue;
|
||||
else {
|
||||
|
||||
has_calls = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if yes we store a context ID for this function in the global var
|
||||
if (has_calls) {
|
||||
|
||||
ConstantInt *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size));
|
||||
StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
|
||||
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (F.size() < function_minimum_size) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
// Context sensitive coverage
|
||||
if (ctx_str && &BB == &F.getEntryBlock()) {
|
||||
|
||||
bool instrumentBlock = false;
|
||||
// load the context ID of the previous function and write to to a local
|
||||
// variable on the stack
|
||||
PrevCtx = IRB.CreateLoad(AFLContext);
|
||||
PrevCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
if (Loc) {
|
||||
// does the function have calls? and is any of the calls larger than one
|
||||
// basic block?
|
||||
for (auto &BB : F) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
if (has_calls) break;
|
||||
for (auto &IN : BB) {
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
CallInst *callInst = nullptr;
|
||||
if ((callInst = dyn_cast<CallInst>(&IN))) {
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
Function *Callee = callInst->getCalledFunction();
|
||||
if (!Callee || Callee->size() < function_minimum_size)
|
||||
continue;
|
||||
else {
|
||||
|
||||
/* 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;
|
||||
|
||||
}
|
||||
has_calls = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
@ -455,64 +347,13 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
// if yes we store a context ID for this function in the global var
|
||||
if (has_calls) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(C));
|
||||
|
||||
unsigned int instLine = cDILoc.getLineNumber();
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) continue;
|
||||
|
||||
}
|
||||
|
||||
// in CTX mode we have to restore the original context for the caller -
|
||||
// she might be calling other functions which need the correct CTX
|
||||
if (ctx_str && has_calls) {
|
||||
|
||||
Instruction *Inst = BB.getTerminator();
|
||||
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
|
||||
|
||||
IRBuilder<> Post_IRB(Inst);
|
||||
StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
ConstantInt *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size));
|
||||
StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
|
||||
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
@ -562,7 +403,28 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
}
|
||||
|
||||
// fprintf(stderr, " == %d\n", more_than_one);
|
||||
if (more_than_one != 1) continue;
|
||||
if (F.size() > 1 && more_than_one != 1) {
|
||||
|
||||
// in CTX mode we have to restore the original context for the caller -
|
||||
// she might be calling other functions which need the correct CTX
|
||||
if (ctx_str && has_calls) {
|
||||
|
||||
Instruction *Inst = BB.getTerminator();
|
||||
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
|
||||
|
||||
IRBuilder<> Post_IRB(Inst);
|
||||
StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ConstantInt *CurLoc;
|
||||
@ -586,13 +448,17 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
prev_block_trans = (block_trans_1 ^ ... ^ block_trans_(n-1)" */
|
||||
|
||||
if (ngram_size)
|
||||
PrevLocTrans = IRB.CreateXorReduce(PrevLoc);
|
||||
PrevLocTrans =
|
||||
IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty());
|
||||
else
|
||||
#endif
|
||||
if (ctx_str)
|
||||
PrevLocTrans = IRB.CreateZExt(IRB.CreateXor(PrevLoc, PrevCtx), Int32Ty);
|
||||
PrevLocTrans = PrevLoc;
|
||||
|
||||
if (ctx_str)
|
||||
PrevLocTrans =
|
||||
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty);
|
||||
else
|
||||
PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
|
||||
PrevLocTrans = IRB.CreateZExt(PrevLocTrans, IRB.getInt32Ty());
|
||||
|
||||
/* Load SHM pointer */
|
||||
|
||||
@ -604,7 +470,9 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
if (ngram_size)
|
||||
MapPtrIdx = IRB.CreateGEP(
|
||||
MapPtr,
|
||||
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, CurLoc), Int32Ty));
|
||||
IRB.CreateZExt(
|
||||
IRB.CreateXor(PrevLocTrans, IRB.CreateZExt(CurLoc, Int32Ty)),
|
||||
Int32Ty));
|
||||
else
|
||||
#endif
|
||||
MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocTrans, CurLoc));
|
||||
@ -620,6 +488,9 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
if (neverZero_counters_str !=
|
||||
NULL) { // with llvm 9 we make this the default as the bug in llvm is
|
||||
// then fixed
|
||||
#else
|
||||
if (!skip_nozero) {
|
||||
|
||||
#endif
|
||||
/* hexcoder: Realize a counter that skips zero during overflow.
|
||||
* Once this counter reaches its maximum value, it next increments to 1
|
||||
@ -630,60 +501,13 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
* Counter + 1 -> {Counter, OverflowFlag}
|
||||
* Counter + OverflowFlag -> Counter
|
||||
*/
|
||||
/* // we keep the old solutions just in case
|
||||
// Solution #1
|
||||
if (neverZero_counters_str[0] == '1') {
|
||||
|
||||
CallInst *AddOv =
|
||||
IRB.CreateBinaryIntrinsic(Intrinsic::uadd_with_overflow, Counter,
|
||||
ConstantInt::get(Int8Ty, 1));
|
||||
AddOv->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None)); Value *SumWithOverflowBit = AddOv; Incr =
|
||||
IRB.CreateAdd(IRB.CreateExtractValue(SumWithOverflowBit, 0), // sum
|
||||
IRB.CreateZExt( // convert from one bit
|
||||
type to 8 bits type IRB.CreateExtractValue(SumWithOverflowBit, 1), //
|
||||
overflow Int8Ty));
|
||||
// Solution #2
|
||||
|
||||
} else if (neverZero_counters_str[0] == '2') {
|
||||
|
||||
auto cf = IRB.CreateICmpEQ(Counter,
|
||||
ConstantInt::get(Int8Ty, 255)); Value *HowMuch =
|
||||
IRB.CreateAdd(ConstantInt::get(Int8Ty, 1), cf); Incr =
|
||||
IRB.CreateAdd(Counter, HowMuch);
|
||||
// Solution #3
|
||||
|
||||
} else if (neverZero_counters_str[0] == '3') {
|
||||
|
||||
*/
|
||||
// this is the solution we choose because llvm9 should do the right
|
||||
// thing here
|
||||
auto cf = IRB.CreateICmpEQ(Incr, Zero);
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
/*
|
||||
// Solution #4
|
||||
|
||||
} else if (neverZero_counters_str[0] == '4') {
|
||||
|
||||
auto cf = IRB.CreateICmpULT(Incr, ConstantInt::get(Int8Ty, 1));
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
|
||||
} else {
|
||||
|
||||
fprintf(stderr, "Error: unknown value for AFL_NZERO_COUNTS: %s
|
||||
(valid is 1-4)\n", neverZero_counters_str); exit(-1);
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
@ -713,6 +537,23 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
// in CTX mode we have to restore the original context for the caller -
|
||||
// she might be calling other functions which need the correct CTX.
|
||||
// Currently this is only needed for the Ubuntu clang-6.0 bug
|
||||
if (ctx_str && has_calls) {
|
||||
|
||||
Instruction *Inst = BB.getTerminator();
|
||||
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
|
||||
|
||||
IRBuilder<> Post_IRB(Inst);
|
||||
StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inst_blocks++;
|
||||
|
||||
}
|
||||
@ -756,8 +597,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
GlobalVariable *AFLFinalLoc = new GlobalVariable(
|
||||
M, Int32Ty, true, GlobalValue::ExternalLinkage, 0,
|
||||
"__afl_final_loc", 0, GlobalVariable::GeneralDynamicTLSModel, 0,
|
||||
false);
|
||||
"__afl_final_loc");
|
||||
ConstantInt *const_loc = ConstantInt::get(Int32Ty, map_size);
|
||||
StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
|
||||
StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"),
|
||||
|
@ -10,6 +10,9 @@
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// to prevent the function from being removed
|
||||
unsigned char __afl_lto_mode = 0;
|
||||
|
||||
@ -17,6 +20,7 @@ unsigned char __afl_lto_mode = 0;
|
||||
|
||||
__attribute__((constructor(0))) void __afl_auto_init_globals(void) {
|
||||
|
||||
if (getenv("AFL_DEBUG")) fprintf(stderr, "[__afl_auto_init_globals]\n");
|
||||
__afl_lto_mode = 1;
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
@ -43,7 +43,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include "snapshot-inl.h"
|
||||
#include "snapshot-inl.h"
|
||||
#endif
|
||||
|
||||
/* This is a somewhat ugly hack for the experimental 'trace-pc-guard' mode.
|
||||
@ -52,6 +52,10 @@
|
||||
|
||||
#define CONST_PRIO 5
|
||||
|
||||
#ifndef MAP_FIXED_NOREPLACE
|
||||
#define MAP_FIXED_NOREPLACE MAP_FIXED
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
@ -59,22 +63,27 @@
|
||||
is used for instrumentation output before __afl_map_shm() has a chance to
|
||||
run. It will end up as .comm, so it shouldn't be too wasteful. */
|
||||
|
||||
u8 __afl_area_initial[MAP_SIZE];
|
||||
#ifdef AFL_REAL_LD
|
||||
u8 __afl_area_initial[256000];
|
||||
#else
|
||||
u8 __afl_area_initial[MAP_SIZE];
|
||||
#endif
|
||||
u8 *__afl_area_ptr = __afl_area_initial;
|
||||
u8 *__afl_dictionary;
|
||||
|
||||
u32 __afl_final_loc;
|
||||
u32 __afl_map_size = MAP_SIZE;
|
||||
u32 __afl_dictionary_len;
|
||||
u64 __afl_map_addr;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX];
|
||||
u32 __afl_final_loc;
|
||||
u32 __afl_prev_ctx;
|
||||
u32 __afl_cmp_counter;
|
||||
u32 __afl_dictionary_len;
|
||||
#else
|
||||
__thread PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX];
|
||||
__thread u32 __afl_final_loc;
|
||||
__thread u32 __afl_prev_ctx;
|
||||
__thread u32 __afl_cmp_counter;
|
||||
__thread u32 __afl_dictionary_len;
|
||||
#endif
|
||||
|
||||
struct cmp_map *__afl_cmp_map;
|
||||
@ -83,44 +92,113 @@ struct cmp_map *__afl_cmp_map;
|
||||
|
||||
static u8 is_persistent;
|
||||
|
||||
/* Error reporting to forkserver controller */
|
||||
|
||||
void send_forkserver_error(int error) {
|
||||
|
||||
u32 status;
|
||||
if (!error || error > 0xffff) return;
|
||||
status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error));
|
||||
if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return;
|
||||
|
||||
}
|
||||
|
||||
/* SHM setup. */
|
||||
|
||||
static void __afl_map_shm(void) {
|
||||
|
||||
u8 *id_str = getenv(SHM_ENV_VAR);
|
||||
char *id_str = getenv(SHM_ENV_VAR);
|
||||
|
||||
if (__afl_final_loc) {
|
||||
|
||||
__afl_map_size = __afl_final_loc;
|
||||
if (__afl_final_loc > MAP_SIZE) {
|
||||
|
||||
char *ptr;
|
||||
u32 val = 0;
|
||||
if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) val = atoi(ptr);
|
||||
if (val < __afl_final_loc) {
|
||||
|
||||
if (__afl_final_loc > FS_OPT_MAX_MAPSIZE) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to "
|
||||
"be able to run this instrumented program!\n",
|
||||
__afl_final_loc);
|
||||
if (id_str) {
|
||||
|
||||
send_forkserver_error(FS_ERROR_MAP_SIZE);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fprintf(stderr,
|
||||
"Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to "
|
||||
"be able to run this instrumented program!\n",
|
||||
__afl_final_loc);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* If we're running under AFL, attach to the appropriate region, replacing the
|
||||
early-stage __afl_area_initial region that is needed to allow some really
|
||||
hacky .init code to work correctly in projects such as OpenSSL. */
|
||||
|
||||
if (getenv("AFL_DEBUG"))
|
||||
fprintf(stderr,
|
||||
"DEBUG: id_str %s, __afl_map_addr 0x%llx, MAP_SIZE %u, "
|
||||
"__afl_final_loc %u, max_size_forkserver %u/0x%x\n",
|
||||
id_str == NULL ? "<null>" : id_str, __afl_map_addr, MAP_SIZE,
|
||||
__afl_final_loc, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE);
|
||||
|
||||
if (id_str) {
|
||||
|
||||
#ifdef USEMMAP
|
||||
const char * shm_file_path = id_str;
|
||||
int shm_fd = -1;
|
||||
unsigned char *shm_base = NULL;
|
||||
unsigned int map_size = MAP_SIZE
|
||||
|
||||
if (__afl_final_loc > 1 && __afl_final_loc < MAP_SIZE) map_size =
|
||||
__afl_final_loc;
|
||||
|
||||
/* create the shared memory segment as if it was a file */
|
||||
shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
|
||||
if (shm_fd == -1) {
|
||||
|
||||
fprintf(stderr, "shm_open() failed\n");
|
||||
send_forkserver_error(FS_ERROR_SHM_OPEN);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* map the shared memory segment to the address space of the process */
|
||||
shm_base = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
||||
if (__afl_map_addr) {
|
||||
|
||||
shm_base =
|
||||
mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED_NOREPLACE | MAP_SHARED, shm_fd, 0);
|
||||
|
||||
} else {
|
||||
|
||||
shm_base = mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
shm_fd, 0);
|
||||
|
||||
}
|
||||
|
||||
if (shm_base == MAP_FAILED) {
|
||||
|
||||
close(shm_fd);
|
||||
shm_fd = -1;
|
||||
|
||||
fprintf(stderr, "mmap() failed\n");
|
||||
if (__afl_map_addr)
|
||||
send_forkserver_error(FS_ERROR_MAP_ADDR);
|
||||
else
|
||||
send_forkserver_error(FS_ERROR_MMAP);
|
||||
exit(2);
|
||||
|
||||
}
|
||||
@ -129,18 +207,40 @@ static void __afl_map_shm(void) {
|
||||
#else
|
||||
u32 shm_id = atoi(id_str);
|
||||
|
||||
__afl_area_ptr = shmat(shm_id, NULL, 0);
|
||||
__afl_area_ptr = shmat(shm_id, (void *)__afl_map_addr, 0);
|
||||
|
||||
#endif
|
||||
|
||||
/* Whooooops. */
|
||||
|
||||
if (__afl_area_ptr == (void *)-1) _exit(1);
|
||||
if (__afl_area_ptr == (void *)-1) {
|
||||
|
||||
if (__afl_map_addr)
|
||||
send_forkserver_error(FS_ERROR_MAP_ADDR);
|
||||
else
|
||||
send_forkserver_error(FS_ERROR_SHMAT);
|
||||
_exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* Write something into the bitmap so that even with low AFL_INST_RATIO,
|
||||
our parent doesn't give up on us. */
|
||||
|
||||
__afl_area_ptr[0] = 1;
|
||||
|
||||
} else if (__afl_map_addr) {
|
||||
|
||||
__afl_area_ptr =
|
||||
mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
if (__afl_area_ptr == MAP_FAILED) {
|
||||
|
||||
fprintf(stderr, "can not aquire mmap for address %p\n",
|
||||
(void *)__afl_map_addr);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
id_str = getenv(CMPLOG_SHM_ENV_VAR);
|
||||
@ -193,13 +293,9 @@ static void __afl_start_snapshots(void) {
|
||||
static u8 tmp[4] = {0, 0, 0, 0};
|
||||
s32 child_pid;
|
||||
u32 status = 0;
|
||||
u32 map_size = MAP_SIZE;
|
||||
u32 already_read_first = 0;
|
||||
u32 was_killed;
|
||||
|
||||
if (__afl_final_loc > 1 && __afl_final_loc < MAP_SIZE)
|
||||
map_size = __afl_final_loc;
|
||||
|
||||
u8 child_stopped = 0;
|
||||
|
||||
void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL);
|
||||
@ -208,8 +304,8 @@ static void __afl_start_snapshots(void) {
|
||||
assume we're not running in forkserver mode and just execute program. */
|
||||
|
||||
status |= (FS_OPT_ENABLED | FS_OPT_SNAPSHOT);
|
||||
if (map_size <= 0x800000)
|
||||
status |= (FS_OPT_SET_MAPSIZE(map_size) | FS_OPT_MAPSIZE);
|
||||
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
|
||||
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
|
||||
if (__afl_dictionary_len > 0 && __afl_dictionary) status |= FS_OPT_AUTODICT;
|
||||
memcpy(tmp, &status, 4);
|
||||
|
||||
@ -362,19 +458,15 @@ static void __afl_start_forkserver(void) {
|
||||
u8 tmp[4] = {0, 0, 0, 0};
|
||||
s32 child_pid;
|
||||
u32 status = 0;
|
||||
u32 map_size = MAP_SIZE;
|
||||
u32 already_read_first = 0;
|
||||
u32 was_killed;
|
||||
|
||||
if (__afl_final_loc > 1 && __afl_final_loc < MAP_SIZE)
|
||||
map_size = __afl_final_loc;
|
||||
|
||||
u8 child_stopped = 0;
|
||||
|
||||
void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
if (map_size <= 0x800000)
|
||||
status |= (FS_OPT_SET_MAPSIZE(map_size) | FS_OPT_MAPSIZE);
|
||||
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
|
||||
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
|
||||
if (__afl_dictionary_len > 0 && __afl_dictionary) status |= FS_OPT_AUTODICT;
|
||||
if (status) status |= (FS_OPT_ENABLED);
|
||||
memcpy(tmp, &status, 4);
|
||||
@ -512,12 +604,8 @@ static void __afl_start_forkserver(void) {
|
||||
|
||||
int __afl_persistent_loop(unsigned int max_cnt) {
|
||||
|
||||
static u8 first_pass = 1;
|
||||
static u32 cycle_cnt;
|
||||
unsigned int map_size = MAP_SIZE;
|
||||
|
||||
if (__afl_final_loc > 1 && __afl_final_loc < MAP_SIZE)
|
||||
map_size = __afl_final_loc;
|
||||
static u8 first_pass = 1;
|
||||
static u32 cycle_cnt;
|
||||
|
||||
if (first_pass) {
|
||||
|
||||
@ -528,7 +616,7 @@ int __afl_persistent_loop(unsigned int max_cnt) {
|
||||
|
||||
if (is_persistent) {
|
||||
|
||||
memset(__afl_area_ptr, 0, map_size);
|
||||
memset(__afl_area_ptr, 0, __afl_map_size);
|
||||
__afl_area_ptr[0] = 1;
|
||||
memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T));
|
||||
|
||||
@ -738,15 +826,15 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) {
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#pragma weak __sanitizer_cov_trace_const_cmp1 = __cmplog_ins_hook1
|
||||
#pragma weak __sanitizer_cov_trace_const_cmp2 = __cmplog_ins_hook2
|
||||
#pragma weak __sanitizer_cov_trace_const_cmp4 = __cmplog_ins_hook4
|
||||
#pragma weak __sanitizer_cov_trace_const_cmp8 = __cmplog_ins_hook8
|
||||
#pragma weak __sanitizer_cov_trace_const_cmp1 = __cmplog_ins_hook1
|
||||
#pragma weak __sanitizer_cov_trace_const_cmp2 = __cmplog_ins_hook2
|
||||
#pragma weak __sanitizer_cov_trace_const_cmp4 = __cmplog_ins_hook4
|
||||
#pragma weak __sanitizer_cov_trace_const_cmp8 = __cmplog_ins_hook8
|
||||
|
||||
#pragma weak __sanitizer_cov_trace_cmp1 = __cmplog_ins_hook1
|
||||
#pragma weak __sanitizer_cov_trace_cmp2 = __cmplog_ins_hook2
|
||||
#pragma weak __sanitizer_cov_trace_cmp4 = __cmplog_ins_hook4
|
||||
#pragma weak __sanitizer_cov_trace_cmp8 = __cmplog_ins_hook8
|
||||
#pragma weak __sanitizer_cov_trace_cmp1 = __cmplog_ins_hook1
|
||||
#pragma weak __sanitizer_cov_trace_cmp2 = __cmplog_ins_hook2
|
||||
#pragma weak __sanitizer_cov_trace_cmp4 = __cmplog_ins_hook4
|
||||
#pragma weak __sanitizer_cov_trace_cmp8 = __cmplog_ins_hook8
|
||||
#else
|
||||
void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook1")));
|
||||
|
@ -38,15 +38,16 @@
|
||||
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#else
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -58,22 +59,7 @@ class CmpLogInstructions : public ModulePass {
|
||||
static char ID;
|
||||
CmpLogInstructions() : 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
initWhitelist();
|
||||
|
||||
}
|
||||
|
||||
@ -91,8 +77,7 @@ class CmpLogInstructions : public ModulePass {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
int be_quiet = 0;
|
||||
int be_quiet = 0;
|
||||
|
||||
private:
|
||||
bool hookInstrs(Module &M);
|
||||
@ -185,119 +170,10 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
/* iterate over all functions, bbs and instruction and add suitable calls */
|
||||
for (auto &F : M) {
|
||||
|
||||
if (!isInWhitelist(&F)) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
|
||||
bool instrumentBlock = false;
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(C));
|
||||
|
||||
unsigned int instLine = cDILoc.getLineNumber();
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) continue;
|
||||
|
||||
}
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CmpInst *selectcmpInst = nullptr;
|
||||
@ -360,11 +236,20 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
|
||||
switch (max_size) {
|
||||
|
||||
case 8: IRB.CreateCall(cmplogHookIns1, args, "tmp"); break;
|
||||
case 16: IRB.CreateCall(cmplogHookIns2, args, "tmp"); break;
|
||||
case 32: IRB.CreateCall(cmplogHookIns4, args, "tmp"); break;
|
||||
case 64: IRB.CreateCall(cmplogHookIns8, args, "tmp"); break;
|
||||
default: break;
|
||||
case 8:
|
||||
IRB.CreateCall(cmplogHookIns1, args, "tmp");
|
||||
break;
|
||||
case 16:
|
||||
IRB.CreateCall(cmplogHookIns2, args, "tmp");
|
||||
break;
|
||||
case 32:
|
||||
IRB.CreateCall(cmplogHookIns4, args, "tmp");
|
||||
break;
|
||||
case 64:
|
||||
IRB.CreateCall(cmplogHookIns8, args, "tmp");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
@ -38,15 +38,16 @@
|
||||
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#else
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -58,22 +59,7 @@ class CmpLogRoutines : public ModulePass {
|
||||
static char ID;
|
||||
CmpLogRoutines() : 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
initWhitelist();
|
||||
|
||||
}
|
||||
|
||||
@ -91,8 +77,7 @@ class CmpLogRoutines : public ModulePass {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
int be_quiet = 0;
|
||||
int be_quiet = 0;
|
||||
|
||||
private:
|
||||
bool hookRtns(Module &M);
|
||||
@ -132,119 +117,10 @@ bool CmpLogRoutines::hookRtns(Module &M) {
|
||||
/* iterate over all functions, bbs and instruction and add suitable calls */
|
||||
for (auto &F : M) {
|
||||
|
||||
if (!isInWhitelist(&F)) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
|
||||
bool instrumentBlock = false;
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(C));
|
||||
|
||||
unsigned int instLine = cDILoc.getLineNumber();
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) continue;
|
||||
|
||||
}
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CallInst *callInst = nullptr;
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "llvm/Config/llvm-config.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"
|
||||
@ -38,15 +37,16 @@
|
||||
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#else
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -58,22 +58,7 @@ 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
initWhitelist();
|
||||
|
||||
}
|
||||
|
||||
@ -91,8 +76,7 @@ class CompareTransform : public ModulePass {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
int be_quiet = 0;
|
||||
int be_quiet = 0;
|
||||
|
||||
private:
|
||||
bool transformCmps(Module &M, const bool processStrcmp,
|
||||
@ -140,119 +124,10 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
* strcmp/memcmp/strncmp/strcasecmp/strncasecmp */
|
||||
for (auto &F : M) {
|
||||
|
||||
if (!isInWhitelist(&F)) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
|
||||
bool instrumentBlock = false;
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(C));
|
||||
|
||||
unsigned int instLine = cDILoc.getLineNumber();
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) continue;
|
||||
|
||||
}
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CallInst *callInst = nullptr;
|
||||
@ -335,20 +210,24 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
}
|
||||
|
||||
// not literal? maybe global or local variable
|
||||
if (!(HasStr1 ^ HasStr2)) {
|
||||
if (!(HasStr1 || HasStr2)) {
|
||||
|
||||
auto *Ptr = dyn_cast<ConstantExpr>(Str2P);
|
||||
if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) {
|
||||
|
||||
if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
|
||||
|
||||
if (auto *Array =
|
||||
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
|
||||
if (Var->hasInitializer()) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString();
|
||||
valueMap[Str2P] = new std::string(Str2.str());
|
||||
// fprintf(stderr, "glo2 %s\n", Str2.str().c_str());
|
||||
if (auto *Array =
|
||||
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString();
|
||||
valueMap[Str2P] = new std::string(Str2.str());
|
||||
fprintf(stderr, "glo2 %s\n", Str2.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -363,13 +242,17 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
|
||||
if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) {
|
||||
|
||||
if (auto *Array =
|
||||
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
|
||||
if (Var->hasInitializer()) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString();
|
||||
valueMap[Str1P] = new std::string(Str1.str());
|
||||
// fprintf(stderr, "glo1 %s\n", Str1.str().c_str());
|
||||
if (auto *Array = dyn_cast<ConstantDataArray>(
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString();
|
||||
valueMap[Str1P] = new std::string(Str1.str());
|
||||
// fprintf(stderr, "glo1 %s\n", Str1.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -384,13 +267,13 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
|
||||
}
|
||||
|
||||
if ((HasStr1 ^ HasStr2)) indirect = true;
|
||||
if ((HasStr1 || HasStr2)) indirect = true;
|
||||
|
||||
}
|
||||
|
||||
if (isIntMemcpy) continue;
|
||||
|
||||
if (!(HasStr1 ^ HasStr2)) {
|
||||
if (!(HasStr1 || HasStr2)) {
|
||||
|
||||
// do we have a saved local variable initialization?
|
||||
std::string *val = valueMap[Str1P];
|
||||
@ -418,7 +301,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
}
|
||||
|
||||
/* handle cases of one string is const, one string is variable */
|
||||
if (!(HasStr1 ^ HasStr2)) continue;
|
||||
if (!(HasStr1 || HasStr2)) continue;
|
||||
|
||||
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
||||
|
||||
@ -483,7 +366,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
|
||||
}
|
||||
|
||||
if (!(HasStr1 ^ HasStr2)) {
|
||||
if (!(HasStr1 || HasStr2)) {
|
||||
|
||||
// do we have a saved local or global variable initialization?
|
||||
std::string *val = valueMap[Str1P];
|
||||
@ -510,13 +393,13 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
|
||||
TmpConstStr = Str1.str();
|
||||
VarStr = Str2P;
|
||||
constLen = isMemcmp ? sizedLen : GetStringLength(Str1P);
|
||||
constLen = isMemcmp ? sizedLen : TmpConstStr.length();
|
||||
|
||||
} else {
|
||||
|
||||
TmpConstStr = Str2.str();
|
||||
VarStr = Str1P;
|
||||
constLen = isMemcmp ? sizedLen : GetStringLength(Str2P);
|
||||
constLen = isMemcmp ? sizedLen : TmpConstStr.length();
|
||||
|
||||
}
|
||||
|
||||
@ -524,9 +407,14 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
* the StringRef (in comparison to std::string a StringRef has built-in
|
||||
* runtime bounds checking, which makes debugging easier) */
|
||||
TmpConstStr.append("\0", 1);
|
||||
if (!sizedLen) constLen++;
|
||||
ConstStr = StringRef(TmpConstStr);
|
||||
|
||||
if (isSizedcmp && constLen > sizedLen) { constLen = sizedLen; }
|
||||
// fprintf(stderr, "issized: %d, const > sized ? %u > %u\n", isSizedcmp,
|
||||
// constLen, sizedLen);
|
||||
if (isSizedcmp && constLen > sizedLen && sizedLen) constLen = sizedLen;
|
||||
if (constLen > TmpConstStr.length()) constLen = TmpConstStr.length();
|
||||
if (!constLen) constLen = TmpConstStr.length();
|
||||
if (!constLen) continue;
|
||||
|
||||
if (!be_quiet)
|
||||
errs() << callInst->getCalledFunction()->getName() << ": len " << constLen
|
||||
@ -617,7 +505,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
|
||||
bool CompareTransform::runOnModule(Module &M) {
|
||||
|
||||
if (isatty(2) && getenv("AFL_QUIET") == NULL)
|
||||
if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL)
|
||||
llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, "
|
||||
"extended by heiko@hexco.de\n";
|
||||
else
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include "llvm/Config/llvm-config.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"
|
||||
@ -37,15 +36,16 @@
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#else
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -55,40 +55,7 @@ class SplitComparesTransform : public ModulePass {
|
||||
static char ID;
|
||||
SplitComparesTransform() : ModulePass(ID) {
|
||||
|
||||
char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool isBlacklisted(const Function *F) {
|
||||
|
||||
static const char *Blacklist[] = {
|
||||
|
||||
"asan.", "llvm.", "sancov.", "__ubsan_handle_", "ign."
|
||||
|
||||
};
|
||||
|
||||
for (auto const &BlacklistFunc : Blacklist) {
|
||||
|
||||
if (F->getName().startswith(BlacklistFunc)) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
initWhitelist();
|
||||
|
||||
}
|
||||
|
||||
@ -105,8 +72,7 @@ class SplitComparesTransform : public ModulePass {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
int be_quiet = 0;
|
||||
int be_quiet = 0;
|
||||
|
||||
private:
|
||||
int enableFPSplit;
|
||||
@ -136,121 +102,10 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||
* all integer comparisons with >= and <= predicates to the icomps vector */
|
||||
for (auto &F : M) {
|
||||
|
||||
if (isBlacklisted(&F)) continue;
|
||||
if (!isInWhitelist(&F)) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
bool instrumentBlock = false;
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(C));
|
||||
|
||||
unsigned int instLine = cDILoc.getLineNumber();
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) continue;
|
||||
|
||||
}
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CmpInst *selectcmpInst = nullptr;
|
||||
@ -318,10 +173,18 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||
CmpInst::Predicate new_pred;
|
||||
switch (pred) {
|
||||
|
||||
case CmpInst::ICMP_UGE: new_pred = CmpInst::ICMP_UGT; break;
|
||||
case CmpInst::ICMP_SGE: new_pred = CmpInst::ICMP_SGT; break;
|
||||
case CmpInst::ICMP_ULE: new_pred = CmpInst::ICMP_ULT; break;
|
||||
case CmpInst::ICMP_SLE: new_pred = CmpInst::ICMP_SLT; break;
|
||||
case CmpInst::ICMP_UGE:
|
||||
new_pred = CmpInst::ICMP_UGT;
|
||||
break;
|
||||
case CmpInst::ICMP_SGE:
|
||||
new_pred = CmpInst::ICMP_SGT;
|
||||
break;
|
||||
case CmpInst::ICMP_ULE:
|
||||
new_pred = CmpInst::ICMP_ULT;
|
||||
break;
|
||||
case CmpInst::ICMP_SLE:
|
||||
new_pred = CmpInst::ICMP_SLT;
|
||||
break;
|
||||
default: // keep the compiler happy
|
||||
continue;
|
||||
|
||||
@ -384,10 +247,18 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||
CmpInst::Predicate new_pred;
|
||||
switch (pred) {
|
||||
|
||||
case CmpInst::FCMP_UGE: new_pred = CmpInst::FCMP_UGT; break;
|
||||
case CmpInst::FCMP_OGE: new_pred = CmpInst::FCMP_OGT; break;
|
||||
case CmpInst::FCMP_ULE: new_pred = CmpInst::FCMP_ULT; break;
|
||||
case CmpInst::FCMP_OLE: new_pred = CmpInst::FCMP_OLT; break;
|
||||
case CmpInst::FCMP_UGE:
|
||||
new_pred = CmpInst::FCMP_UGT;
|
||||
break;
|
||||
case CmpInst::FCMP_OGE:
|
||||
new_pred = CmpInst::FCMP_OGT;
|
||||
break;
|
||||
case CmpInst::FCMP_ULE:
|
||||
new_pred = CmpInst::FCMP_ULT;
|
||||
break;
|
||||
case CmpInst::FCMP_OLE:
|
||||
new_pred = CmpInst::FCMP_OLT;
|
||||
break;
|
||||
default: // keep the compiler happy
|
||||
continue;
|
||||
|
||||
@ -855,7 +726,8 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
|
||||
icmp_exponent_result =
|
||||
BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0);
|
||||
break;
|
||||
default: continue;
|
||||
default:
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
@ -958,7 +830,8 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
|
||||
icmp_fraction_result =
|
||||
BinaryOperator::Create(Instruction::Xor, icmp_fraction, t_s0);
|
||||
break;
|
||||
default: continue;
|
||||
default:
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
@ -1004,7 +877,8 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
|
||||
PN->addIncoming(icmp_exponent_result, signequal_bb);
|
||||
PN->addIncoming(icmp_fraction_result, middle_bb);
|
||||
break;
|
||||
default: continue;
|
||||
default:
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "llvm/Config/llvm-config.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"
|
||||
@ -40,15 +39,16 @@
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#else
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#define nullptr 0
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -60,40 +60,7 @@ class SplitSwitchesTransform : public ModulePass {
|
||||
static char ID;
|
||||
SplitSwitchesTransform() : ModulePass(ID) {
|
||||
|
||||
char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool isBlacklisted(const Function *F) {
|
||||
|
||||
static const char *Blacklist[] = {
|
||||
|
||||
"asan.", "llvm.", "sancov.", "__ubsan_handle_", "ign."
|
||||
|
||||
};
|
||||
|
||||
for (auto const &BlacklistFunc : Blacklist) {
|
||||
|
||||
if (F->getName().startswith(BlacklistFunc)) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
initWhitelist();
|
||||
|
||||
}
|
||||
|
||||
@ -125,8 +92,7 @@ class SplitSwitchesTransform : public ModulePass {
|
||||
typedef std::vector<CaseExpr> CaseVector;
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
int be_quiet = 0;
|
||||
int be_quiet = 0;
|
||||
|
||||
private:
|
||||
bool splitSwitches(Module &M);
|
||||
@ -346,122 +312,12 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
||||
* all switches to switches vector for later processing */
|
||||
for (auto &F : M) {
|
||||
|
||||
if (isBlacklisted(&F)) continue;
|
||||
if (!isInWhitelist(&F)) continue;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
SwitchInst *switchInst = nullptr;
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
bool instrumentBlock = false;
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7)
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(C));
|
||||
|
||||
unsigned int instLine = cDILoc.getLineNumber();
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
|
||||
(void)instLine;
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
/* We don't check for filename equality here because
|
||||
* filenames might actually be full paths. Instead we
|
||||
* check that the actual filename ends in the filename
|
||||
* specified in the list. */
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) continue;
|
||||
|
||||
}
|
||||
|
||||
if ((switchInst = dyn_cast<SwitchInst>(BB.getTerminator()))) {
|
||||
|
||||
if (switchInst->getNumCases() < 1) continue;
|
||||
@ -561,7 +417,7 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
||||
|
||||
bool SplitSwitchesTransform::runOnModule(Module &M) {
|
||||
|
||||
if (isatty(2) && getenv("AFL_QUIET") == NULL)
|
||||
if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL)
|
||||
llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n";
|
||||
else
|
||||
be_quiet = 1;
|
||||
|
@ -29,7 +29,9 @@ int target_func(char *buf, int size) {
|
||||
}
|
||||
|
||||
break;
|
||||
default: puts("default action"); break;
|
||||
default:
|
||||
puts("default action");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "../include/android-ashmem.h"
|
||||
#include "../include/android-ashmem.h"
|
||||
#endif
|
||||
|
||||
#include <sys/ipc.h>
|
||||
@ -23,15 +23,15 @@
|
||||
/* NeverZero */
|
||||
|
||||
#if (defined(__x86_64__) || defined(__i386__)) && defined(AFL_QEMU_NOT_ZERO)
|
||||
#define INC_AFL_AREA(loc) \
|
||||
asm volatile( \
|
||||
"incb (%0, %1, 1)\n" \
|
||||
"adcb $0, (%0, %1, 1)\n" \
|
||||
: /* no out */ \
|
||||
: "r"(afl_area_ptr), "r"(loc) \
|
||||
: "memory", "eax")
|
||||
#define INC_AFL_AREA(loc) \
|
||||
asm volatile( \
|
||||
"incb (%0, %1, 1)\n" \
|
||||
"adcb $0, (%0, %1, 1)\n" \
|
||||
: /* no out */ \
|
||||
: "r"(afl_area_ptr), "r"(loc) \
|
||||
: "memory", "eax")
|
||||
#else
|
||||
#define INC_AFL_AREA(loc) afl_area_ptr[loc]++
|
||||
#define INC_AFL_AREA(loc) afl_area_ptr[loc]++
|
||||
#endif
|
||||
|
||||
using namespace QBDI;
|
||||
|
@ -46,8 +46,8 @@ You can also compile statically-linked binaries by setting STATIC=1. This
|
||||
can be useful when compiling QEMU on a different system than the one you're
|
||||
planning to run the fuzzer on and is most often used with the HOST variable.
|
||||
|
||||
Note: when targetting the i386 architecture, on some bianries the forkserver
|
||||
handshake may fail due to the lack of reversed memory. Fix it with
|
||||
Note: when targetting the i386 architecture, on some binaries the forkserver
|
||||
handshake may fail due to the lack of reserved memory. Fix it with
|
||||
|
||||
export QEMU_RESERVED_VA=0x1000000
|
||||
|
||||
|
@ -23,8 +23,10 @@ in 2.2 and 2.3) have to be set.
|
||||
This address (as well as the RET address, see below) has to be defined in
|
||||
hexadecimal with the 0x prefix or as a decimal value.
|
||||
|
||||
If the target is compiled with position independant code (PIE/PIC), you must
|
||||
add 0x4000000000 to that address, because qemu loads to this base address.
|
||||
*Note:* If the target is compiled with position independant code (PIE/PIC)
|
||||
qemu loads these to a specific base address.
|
||||
For 64 bit you have to add 0x4000000000 (9 zeroes) and for 32 bit 0x40000000
|
||||
(7 zeroes) to the address.
|
||||
On strange setups the base address set by QEMU for PIE executable may change,
|
||||
you can check it printing the process map using
|
||||
`AFL_QEMU_DEBUG_MAPS=1 afl-qemu-trace TARGET-BINARY`
|
||||
@ -32,7 +34,7 @@ you can check it printing the process map using
|
||||
If this address is not valid, afl-fuzz will error during startup with the
|
||||
message that the forkserver was not found.
|
||||
|
||||
### 2.2) the RET address
|
||||
### 2.2) The RET address
|
||||
|
||||
The RET address is the last instruction of the persistent loop.
|
||||
The emulator will emit a jump to START when translating the instruction at RET.
|
||||
@ -46,7 +48,7 @@ patch the return address (on stack or in the link register) to return to START
|
||||
It is defined by setting AFL_QEMU_PERSISTENT_RET, and too 0x4000000000 has to
|
||||
be set if the target is position independant.
|
||||
|
||||
### 2.3) the OFFSET
|
||||
### 2.3) The OFFSET
|
||||
|
||||
This option is valid only for x86/x86_64 only, arm/aarch64 do not save the
|
||||
return address on stack.
|
||||
@ -72,7 +74,7 @@ Now to get this value right here some help:
|
||||
8. again print the ESP value
|
||||
9. calculate the difference between the two values - and this is the offset
|
||||
|
||||
### 2.4) resetting the register state
|
||||
### 2.4) Resetting the register state
|
||||
|
||||
It is very, very likely you need to restore the general purpose registers state
|
||||
when starting a new loop. Because of this you 99% of the time should set
|
||||
|
@ -63,7 +63,7 @@ if [ ! -f "../afl-showmap" ]; then
|
||||
fi
|
||||
|
||||
PREREQ_NOTFOUND=
|
||||
for i in libtool wget python automake autoconf sha384sum bison flex iconv patch pkg-config; do
|
||||
for i in libtool wget automake autoconf sha384sum bison flex iconv patch pkg-config; do
|
||||
|
||||
T=`command -v "$i" 2>/dev/null`
|
||||
|
||||
@ -76,6 +76,14 @@ for i in libtool wget python automake autoconf sha384sum bison flex iconv patch
|
||||
|
||||
done
|
||||
|
||||
PYTHONBIN=`command -v python3 || command -v python || command -v python2`
|
||||
|
||||
if [ "$PYTHONBIN" = "" ]; then
|
||||
echo "[-] Error: 'python' not found, please install using 'sudo apt install python3'."
|
||||
PREREQ_NOTFOUND=1
|
||||
fi
|
||||
|
||||
|
||||
if [ ! -d "/usr/include/glib-2.0/" -a ! -d "/usr/local/include/glib-2.0/" ]; then
|
||||
|
||||
echo "[-] Error: devel version of 'glib2' not found, please install first."
|
||||
@ -83,6 +91,13 @@ if [ ! -d "/usr/include/glib-2.0/" -a ! -d "/usr/local/include/glib-2.0/" ]; the
|
||||
|
||||
fi
|
||||
|
||||
if [ ! -d "/usr/include/pixman-1/" -a ! -d "/usr/local/include/pixman-1/" ]; then
|
||||
|
||||
echo "[-] Error: devel version of 'pixman-1' not found, please install first."
|
||||
PREREQ_NOTFOUND=1
|
||||
|
||||
fi
|
||||
|
||||
if echo "$CC" | grep -qF /afl-; then
|
||||
|
||||
echo "[-] Error: do not use afl-gcc or afl-clang to compile this tool."
|
||||
@ -156,6 +171,8 @@ fi
|
||||
|
||||
cd qemu-$VERSION || exit 1
|
||||
|
||||
echo Building for CPU target $CPU_TARGET
|
||||
|
||||
echo "[*] Applying patches..."
|
||||
|
||||
patch -p1 <../patches/elfload.diff || exit 1
|
||||
@ -181,6 +198,7 @@ echo "[+] Patching done."
|
||||
|
||||
if [ "$STATIC" = "1" ]; then
|
||||
|
||||
echo Building STATIC binary
|
||||
./configure --extra-cflags="-O3 -ggdb -DAFL_QEMU_STATIC_BUILD=1" \
|
||||
--disable-bsd-user --disable-guest-agent --disable-strip --disable-werror \
|
||||
--disable-gcrypt --disable-debug-info --disable-debug-tcg --disable-tcg-interpreter \
|
||||
@ -192,16 +210,17 @@ if [ "$STATIC" = "1" ]; then
|
||||
--disable-libusb --disable-usb-redir --disable-vde --disable-vhost-net --disable-virglrenderer \
|
||||
--disable-virtfs --disable-vnc --disable-vte --disable-xen --disable-xen-pci-passthrough --disable-xfsctl \
|
||||
--enable-linux-user --disable-system --disable-blobs --disable-tools --enable-capstone=internal \
|
||||
--target-list="${CPU_TARGET}-linux-user" --static --disable-pie --cross-prefix=$CROSS_PREFIX || exit 1
|
||||
--target-list="${CPU_TARGET}-linux-user" --static --disable-pie --cross-prefix=$CROSS_PREFIX --python="$PYTHONBIN" \
|
||||
|| exit 1
|
||||
|
||||
else
|
||||
|
||||
# --enable-pie seems to give a couple of exec's a second performance
|
||||
# improvement, much to my surprise. Not sure how universal this is..
|
||||
|
||||
|
||||
./configure --disable-system \
|
||||
--enable-linux-user --disable-gtk --disable-sdl --disable-vnc --enable-capstone=internal \
|
||||
--target-list="${CPU_TARGET}-linux-user" --enable-pie $CROSS_PREFIX || exit 1
|
||||
--target-list="${CPU_TARGET}-linux-user" --enable-pie $CROSS_PREFIX --python="$PYTHONBIN" || exit 1
|
||||
|
||||
fi
|
||||
|
||||
@ -230,7 +249,7 @@ if [ "$ORIG_CPU_TARGET" = "" ]; then
|
||||
|
||||
make >/dev/null || exit 1
|
||||
|
||||
gcc test-instr.c -o test-instr || exit 1
|
||||
cc test-instr.c -o test-instr || exit 1
|
||||
|
||||
unset AFL_INST_RATIO
|
||||
export ASAN_OPTIONS=detect_leaks=0
|
||||
@ -269,6 +288,7 @@ echo "[+] Building libcompcov ..."
|
||||
make -C libcompcov && echo "[+] libcompcov ready"
|
||||
echo "[+] Building unsigaction ..."
|
||||
make -C unsigaction && echo "[+] unsigaction ready"
|
||||
|
||||
echo "[+] All done for qemu_mode, enjoy!"
|
||||
|
||||
exit 0
|
||||
|
@ -51,9 +51,11 @@ int main() {
|
||||
|
||||
switch (z) {
|
||||
|
||||
case 0xBEEF: break;
|
||||
case 0xBEEF:
|
||||
break;
|
||||
|
||||
default: return 4;
|
||||
default:
|
||||
return 4;
|
||||
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
@ -36,7 +36,7 @@
|
||||
#include "pmparser.h"
|
||||
|
||||
#ifndef __linux__
|
||||
#error "Sorry, this library is Linux-specific for now!"
|
||||
#error "Sorry, this library is Linux-specific for now!"
|
||||
#endif /* !__linux__ */
|
||||
|
||||
/* Change this value to tune the compare coverage */
|
||||
@ -166,7 +166,7 @@ static void __compcov_trace(u64 cur_loc, const u8 *v0, const u8 *v1, size_t n) {
|
||||
if (debug_fd != 1) {
|
||||
|
||||
char debugbuf[4096];
|
||||
snprintf(debugbuf, sizeof(debugbuf), "0x%llx %s %s %lu\n", cur_loc,
|
||||
snprintf(debugbuf, sizeof(debugbuf), "0x%llx %s %s %zu\n", cur_loc,
|
||||
v0 == NULL ? "(null)" : (char *)v0,
|
||||
v1 == NULL ? "(null)" : (char *)v1, n);
|
||||
write(debug_fd, debugbuf, strlen(debugbuf));
|
||||
|
@ -40,27 +40,27 @@
|
||||
#define PERSISTENT_DEFAULT_MAX_CNT 1000
|
||||
|
||||
#ifdef CPU_NB_REGS
|
||||
#define AFL_REGS_NUM CPU_NB_REGS
|
||||
#define AFL_REGS_NUM CPU_NB_REGS
|
||||
#elif TARGET_ARM
|
||||
#define AFL_REGS_NUM 32
|
||||
#define AFL_REGS_NUM 32
|
||||
#elif TARGET_AARCH64
|
||||
#define AFL_REGS_NUM 32
|
||||
#define AFL_REGS_NUM 32
|
||||
#else
|
||||
#define AFL_REGS_NUM 100
|
||||
#define AFL_REGS_NUM 100
|
||||
#endif
|
||||
|
||||
/* NeverZero */
|
||||
|
||||
#if (defined(__x86_64__) || defined(__i386__)) && defined(AFL_QEMU_NOT_ZERO)
|
||||
#define INC_AFL_AREA(loc) \
|
||||
asm volatile( \
|
||||
"incb (%0, %1, 1)\n" \
|
||||
"adcb $0, (%0, %1, 1)\n" \
|
||||
: /* no out */ \
|
||||
: "r"(afl_area_ptr), "r"(loc) \
|
||||
: "memory", "eax")
|
||||
#define INC_AFL_AREA(loc) \
|
||||
asm volatile( \
|
||||
"incb (%0, %1, 1)\n" \
|
||||
"adcb $0, (%0, %1, 1)\n" \
|
||||
: /* no out */ \
|
||||
: "r"(afl_area_ptr), "r"(loc) \
|
||||
: "memory", "eax")
|
||||
#else
|
||||
#define INC_AFL_AREA(loc) afl_area_ptr[loc]++
|
||||
#define INC_AFL_AREA(loc) afl_area_ptr[loc]++
|
||||
#endif
|
||||
|
||||
typedef void (*afl_persistent_hook_fn)(uint64_t *regs, uint64_t guest_base);
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include "afl-qemu-common.h"
|
||||
|
||||
#ifndef AFL_QEMU_STATIC_BUILD
|
||||
#include <dlfcn.h>
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
/***************************
|
||||
@ -123,12 +123,11 @@ struct afl_chain {
|
||||
|
||||
/* Some forward decls: */
|
||||
|
||||
TranslationBlock *tb_htable_lookup(CPUState *, target_ulong, target_ulong,
|
||||
uint32_t, uint32_t);
|
||||
static inline TranslationBlock *tb_find(CPUState *, TranslationBlock *, int,
|
||||
uint32_t);
|
||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||
TranslationBlock *tb_next);
|
||||
int open_self_maps(void *cpu_env, int fd);
|
||||
|
||||
/*************************
|
||||
* ACTUAL IMPLEMENTATION *
|
||||
@ -275,20 +274,6 @@ void afl_setup(void) {
|
||||
|
||||
}
|
||||
|
||||
static void print_mappings(void) {
|
||||
|
||||
u8 buf[MAX_LINE];
|
||||
FILE *f = fopen("/proc/self/maps", "r");
|
||||
|
||||
if (!f) return;
|
||||
|
||||
while (fgets(buf, MAX_LINE, f))
|
||||
printf("%s", buf);
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
/* Fork server logic, invoked once we hit _start. */
|
||||
|
||||
void afl_forkserver(CPUState *cpu) {
|
||||
@ -299,7 +284,7 @@ void afl_forkserver(CPUState *cpu) {
|
||||
if (forkserver_installed == 1) return;
|
||||
forkserver_installed = 1;
|
||||
|
||||
if (getenv("AFL_QEMU_DEBUG_MAPS")) print_mappings();
|
||||
if (getenv("AFL_QEMU_DEBUG_MAPS")) open_self_maps(cpu->env_ptr, 0);
|
||||
|
||||
// if (!afl_area_ptr) return; // not necessary because of fixed dummy buffer
|
||||
|
||||
|
@ -35,10 +35,10 @@
|
||||
#include "tcg.h"
|
||||
#include "tcg-op.h"
|
||||
|
||||
#if TCG_TARGET_LONG_BITS == 64
|
||||
#define _DEFAULT_MO MO_64
|
||||
#if TCG_TARGET_REG_BITS == 64
|
||||
#define _DEFAULT_MO MO_64
|
||||
#else
|
||||
#define _DEFAULT_MO MO_32
|
||||
#define _DEFAULT_MO MO_32
|
||||
#endif
|
||||
|
||||
static void afl_gen_compcov(target_ulong cur_loc, TCGv arg1, TCGv arg2,
|
||||
@ -55,11 +55,20 @@ static void afl_gen_compcov(target_ulong cur_loc, TCGv arg1, TCGv arg2,
|
||||
|
||||
switch (ot & MO_SIZE) {
|
||||
|
||||
case MO_64: gen_helper_afl_cmplog_64(cur_loc_v, arg1, arg2); break;
|
||||
case MO_32: gen_helper_afl_cmplog_32(cur_loc_v, arg1, arg2); break;
|
||||
case MO_16: gen_helper_afl_cmplog_16(cur_loc_v, arg1, arg2); break;
|
||||
case MO_8: gen_helper_afl_cmplog_8(cur_loc_v, arg1, arg2); break;
|
||||
default: break;
|
||||
case MO_64:
|
||||
gen_helper_afl_cmplog_64(cur_loc_v, arg1, arg2);
|
||||
break;
|
||||
case MO_32:
|
||||
gen_helper_afl_cmplog_32(cur_loc_v, arg1, arg2);
|
||||
break;
|
||||
case MO_16:
|
||||
gen_helper_afl_cmplog_16(cur_loc_v, arg1, arg2);
|
||||
break;
|
||||
case MO_8:
|
||||
gen_helper_afl_cmplog_8(cur_loc_v, arg1, arg2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
@ -78,10 +87,17 @@ static void afl_gen_compcov(target_ulong cur_loc, TCGv arg1, TCGv arg2,
|
||||
|
||||
switch (ot & MO_SIZE) {
|
||||
|
||||
case MO_64: gen_helper_afl_compcov_64(cur_loc_v, arg1, arg2); break;
|
||||
case MO_32: gen_helper_afl_compcov_32(cur_loc_v, arg1, arg2); break;
|
||||
case MO_16: gen_helper_afl_compcov_16(cur_loc_v, arg1, arg2); break;
|
||||
default: break;
|
||||
case MO_64:
|
||||
gen_helper_afl_compcov_64(cur_loc_v, arg1, arg2);
|
||||
break;
|
||||
case MO_32:
|
||||
gen_helper_afl_compcov_32(cur_loc_v, arg1, arg2);
|
||||
break;
|
||||
case MO_16:
|
||||
gen_helper_afl_compcov_16(cur_loc_v, arg1, arg2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
|
||||
index b13a170e..4af79175 100644
|
||||
index b13a170e..3f5cc902 100644
|
||||
--- a/linux-user/syscall.c
|
||||
+++ b/linux-user/syscall.c
|
||||
@@ -111,6 +111,9 @@
|
||||
@ -43,7 +43,17 @@ index b13a170e..4af79175 100644
|
||||
ts = (TaskState *)cpu->opaque;
|
||||
if (flags & CLONE_SETTLS)
|
||||
cpu_set_tls (env, newtls);
|
||||
@@ -7324,10 +7328,12 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
|
||||
@@ -6554,7 +6558,8 @@ static int open_self_cmdline(void *cpu_env, int fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int open_self_maps(void *cpu_env, int fd)
|
||||
+int open_self_maps(void *cpu_env, int fd);
|
||||
+int open_self_maps(void *cpu_env, int fd)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
|
||||
TaskState *ts = cpu->opaque;
|
||||
@@ -7324,10 +7329,12 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
|
||||
#ifdef TARGET_NR_stime /* not on alpha */
|
||||
case TARGET_NR_stime:
|
||||
{
|
||||
@ -59,7 +69,7 @@ index b13a170e..4af79175 100644
|
||||
}
|
||||
#endif
|
||||
#ifdef TARGET_NR_alarm /* not on alpha */
|
||||
@@ -10529,7 +10535,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
|
||||
@@ -10529,7 +10536,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
|
||||
return TARGET_PAGE_SIZE;
|
||||
#endif
|
||||
case TARGET_NR_gettid:
|
||||
@ -68,7 +78,7 @@ index b13a170e..4af79175 100644
|
||||
#ifdef TARGET_NR_readahead
|
||||
case TARGET_NR_readahead:
|
||||
#if TARGET_ABI_BITS == 32
|
||||
@@ -10813,8 +10819,19 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
|
||||
@@ -10813,8 +10820,19 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
|
||||
return get_errno(safe_tkill((int)arg1, target_to_host_signal(arg2)));
|
||||
|
||||
case TARGET_NR_tgkill:
|
||||
|
@ -25,10 +25,10 @@ all: $(TARGETS)
|
||||
@if [ "$(AFL_NO_X86)" != "" ]; then echo "[!] Note: skipping compilation of unsigaction (AFL_NO_X86 set)."; fi
|
||||
|
||||
unsigaction32.so:
|
||||
@$(CC) -m32 -fPIC -shared unsigaction.c -o unsigaction32.so 2>/dev/null ; if [ "$$?" = "0" ]; then echo "unsigaction32 build success"; else echo "unsigaction32 build failure (that's fine)"; fi
|
||||
@if $(CC) -m32 -fPIC -shared unsigaction.c -o unsigaction32.so 2>/dev/null ; then echo "unsigaction32 build success"; else echo "unsigaction32 build failure (that's fine)"; fi
|
||||
|
||||
unsigaction64.so:
|
||||
$(CC) -m64 -fPIC -shared unsigaction.c -o unsigaction64.so
|
||||
@if $(CC) -m64 -fPIC -shared unsigaction.c -o unsigaction64.so 2>/dev/null ; then echo "unsigaction64 build success"; else echo "unsigaction64 build failure (that's fine)"; fi
|
||||
|
||||
clean:
|
||||
rm -f unsigaction32.so unsigaction64.so
|
||||
|
@ -27,7 +27,7 @@
|
||||
#define AFL_MAIN
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
@ -122,7 +122,7 @@ static void classify_counts(u8 *mem) {
|
||||
|
||||
while (i--) {
|
||||
|
||||
if (*mem) *mem = 1;
|
||||
if (*mem) { *mem = 1; }
|
||||
mem++;
|
||||
|
||||
}
|
||||
@ -147,8 +147,11 @@ static inline u8 anything_set(void) {
|
||||
u32 *ptr = (u32 *)trace_bits;
|
||||
u32 i = (map_size >> 2);
|
||||
|
||||
while (i--)
|
||||
if (*(ptr++)) return 1;
|
||||
while (i--) {
|
||||
|
||||
if (*(ptr++)) { return 1; }
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -169,13 +172,16 @@ static void read_initial_file(void) {
|
||||
struct stat st;
|
||||
s32 fd = open(in_file, O_RDONLY);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to open '%s'", in_file);
|
||||
if (fd < 0) { PFATAL("Unable to open '%s'", in_file); }
|
||||
|
||||
if (fstat(fd, &st) || !st.st_size) FATAL("Zero-sized input file.");
|
||||
if (fstat(fd, &st) || !st.st_size) { FATAL("Zero-sized input file."); }
|
||||
|
||||
if (st.st_size >= TMIN_MAX_FILE) {
|
||||
|
||||
if (st.st_size >= TMIN_MAX_FILE)
|
||||
FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024);
|
||||
|
||||
}
|
||||
|
||||
in_len = st.st_size;
|
||||
in_data = ck_alloc_nozero(in_len);
|
||||
|
||||
@ -197,7 +203,7 @@ static s32 write_to_file(u8 *path, u8 *mem, u32 len) {
|
||||
|
||||
ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
if (ret < 0) PFATAL("Unable to create '%s'", path);
|
||||
if (ret < 0) { PFATAL("Unable to create '%s'", path); }
|
||||
|
||||
ck_write(ret, mem, len, path);
|
||||
|
||||
@ -225,7 +231,7 @@ static u32 analyze_run_target(char **argv, u8 *mem, u32 len, u8 first_run) {
|
||||
|
||||
child_pid = fork();
|
||||
|
||||
if (child_pid < 0) PFATAL("fork() failed");
|
||||
if (child_pid < 0) { PFATAL("fork() failed"); }
|
||||
|
||||
if (!child_pid) {
|
||||
|
||||
@ -278,7 +284,7 @@ static u32 analyze_run_target(char **argv, u8 *mem, u32 len, u8 first_run) {
|
||||
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
|
||||
if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed");
|
||||
if (waitpid(child_pid, &status, 0) <= 0) { FATAL("waitpid() failed"); }
|
||||
|
||||
child_pid = 0;
|
||||
it.it_value.tv_sec = 0;
|
||||
@ -290,9 +296,12 @@ static u32 analyze_run_target(char **argv, u8 *mem, u32 len, u8 first_run) {
|
||||
|
||||
/* Clean up bitmap, analyze exit condition, etc. */
|
||||
|
||||
if (*(u32 *)trace_bits == EXEC_FAIL_SIG)
|
||||
if (*(u32 *)trace_bits == EXEC_FAIL_SIG) {
|
||||
|
||||
FATAL("Unable to execute '%s'", argv[0]);
|
||||
|
||||
}
|
||||
|
||||
classify_counts(trace_bits);
|
||||
total_execs++;
|
||||
|
||||
@ -325,7 +334,7 @@ static u32 analyze_run_target(char **argv, u8 *mem, u32 len, u8 first_run) {
|
||||
|
||||
}
|
||||
|
||||
if (first_run) orig_cksum = cksum;
|
||||
if (first_run) { orig_cksum = cksum; }
|
||||
|
||||
return cksum;
|
||||
|
||||
@ -340,9 +349,12 @@ static void show_char(u8 val) {
|
||||
switch (val) {
|
||||
|
||||
case 0 ... 32:
|
||||
case 127 ... 255: SAYF("#%02x", val); break;
|
||||
case 127 ... 255:
|
||||
SAYF("#%02x", val);
|
||||
break;
|
||||
|
||||
default: SAYF(" %c ", val);
|
||||
default:
|
||||
SAYF(" %c ", val);
|
||||
|
||||
}
|
||||
|
||||
@ -387,7 +399,12 @@ static void dump_hex(u8 *buf, u32 len, u8 *b_data) {
|
||||
|
||||
while (i + rlen < len && (b_data[i] >> 7) == (b_data[i + rlen] >> 7)) {
|
||||
|
||||
if (rtype < (b_data[i + rlen] & 0x0f)) rtype = b_data[i + rlen] & 0x0f;
|
||||
if (rtype < (b_data[i + rlen] & 0x0f)) {
|
||||
|
||||
rtype = b_data[i + rlen] & 0x0f;
|
||||
|
||||
}
|
||||
|
||||
rlen++;
|
||||
|
||||
}
|
||||
@ -454,9 +471,11 @@ static void dump_hex(u8 *buf, u32 len, u8 *b_data) {
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
case 5 ... MAX_AUTO_EXTRA - 1: break;
|
||||
case 5 ... MAX_AUTO_EXTRA - 1:
|
||||
break;
|
||||
|
||||
default: rtype = RESP_SUSPECT;
|
||||
default:
|
||||
rtype = RESP_SUSPECT;
|
||||
|
||||
}
|
||||
|
||||
@ -472,34 +491,58 @@ static void dump_hex(u8 *buf, u32 len, u8 *b_data) {
|
||||
|
||||
if (!((i + off) % 16)) {
|
||||
|
||||
if (off) SAYF(cRST cLCY ">");
|
||||
if (off) { SAYF(cRST cLCY ">"); }
|
||||
|
||||
if (use_hex_offsets) {
|
||||
|
||||
if (use_hex_offsets)
|
||||
SAYF(cRST cGRA "%s[%06x] " cRST, (i + off) ? "\n" : "", i + off);
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
SAYF(cRST cGRA "%s[%06u] " cRST, (i + off) ? "\n" : "", i + off);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch (rtype) {
|
||||
|
||||
case RESP_NONE: SAYF(cLGR bgGRA); break;
|
||||
case RESP_MINOR: SAYF(cBRI bgGRA); break;
|
||||
case RESP_VARIABLE: SAYF(cBLK bgCYA); break;
|
||||
case RESP_FIXED: SAYF(cBLK bgMGN); break;
|
||||
case RESP_LEN: SAYF(cBLK bgLGN); break;
|
||||
case RESP_CKSUM: SAYF(cBLK bgYEL); break;
|
||||
case RESP_SUSPECT: SAYF(cBLK bgLRD); break;
|
||||
case RESP_NONE:
|
||||
SAYF(cLGR bgGRA);
|
||||
break;
|
||||
case RESP_MINOR:
|
||||
SAYF(cBRI bgGRA);
|
||||
break;
|
||||
case RESP_VARIABLE:
|
||||
SAYF(cBLK bgCYA);
|
||||
break;
|
||||
case RESP_FIXED:
|
||||
SAYF(cBLK bgMGN);
|
||||
break;
|
||||
case RESP_LEN:
|
||||
SAYF(cBLK bgLGN);
|
||||
break;
|
||||
case RESP_CKSUM:
|
||||
SAYF(cBLK bgYEL);
|
||||
break;
|
||||
case RESP_SUSPECT:
|
||||
SAYF(cBLK bgLRD);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
show_char(in_data[i + off]);
|
||||
|
||||
if (off != rlen - 1 && (i + off + 1) % 16)
|
||||
if (off != rlen - 1 && (i + off + 1) % 16) {
|
||||
|
||||
SAYF(" ");
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
SAYF(cRST " ");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
@ -511,13 +554,27 @@ static void dump_hex(u8 *buf, u32 len, u8 *b_data) {
|
||||
|
||||
switch (rtype) {
|
||||
|
||||
case RESP_NONE: SAYF("no-op block\n"); break;
|
||||
case RESP_MINOR: SAYF("superficial content\n"); break;
|
||||
case RESP_VARIABLE: SAYF("critical stream\n"); break;
|
||||
case RESP_FIXED: SAYF("\"magic value\" section\n"); break;
|
||||
case RESP_LEN: SAYF("suspected length field\n"); break;
|
||||
case RESP_CKSUM: SAYF("suspected cksum or magic int\n"); break;
|
||||
case RESP_SUSPECT: SAYF("suspected checksummed block\n"); break;
|
||||
case RESP_NONE:
|
||||
SAYF("no-op block\n");
|
||||
break;
|
||||
case RESP_MINOR:
|
||||
SAYF("superficial content\n");
|
||||
break;
|
||||
case RESP_VARIABLE:
|
||||
SAYF("critical stream\n");
|
||||
break;
|
||||
case RESP_FIXED:
|
||||
SAYF("\"magic value\" section\n");
|
||||
break;
|
||||
case RESP_LEN:
|
||||
SAYF("suspected length field\n");
|
||||
break;
|
||||
case RESP_CKSUM:
|
||||
SAYF("suspected cksum or magic int\n");
|
||||
break;
|
||||
case RESP_SUSPECT:
|
||||
SAYF("suspected checksummed block\n");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
@ -594,16 +651,21 @@ static void analyze(char **argv) {
|
||||
|
||||
b_data[i] = RESP_FIXED;
|
||||
|
||||
} else
|
||||
} else {
|
||||
|
||||
b_data[i] = RESP_VARIABLE;
|
||||
|
||||
}
|
||||
|
||||
/* When all checksums change, flip most significant bit of b_data. */
|
||||
|
||||
if (prev_xff != xor_ff && prev_x01 != xor_01 && prev_s10 != sub_10 &&
|
||||
prev_a10 != add_10)
|
||||
prev_a10 != add_10) {
|
||||
|
||||
seq_byte ^= 0x80;
|
||||
|
||||
}
|
||||
|
||||
b_data[i] |= seq_byte;
|
||||
|
||||
prev_xff = xor_ff;
|
||||
@ -620,10 +682,13 @@ static void analyze(char **argv) {
|
||||
OKF("Analysis complete. Interesting bits: %0.02f%% of the input file.",
|
||||
100.0 - ((double)boring_len * 100) / in_len);
|
||||
|
||||
if (exec_hangs)
|
||||
if (exec_hangs) {
|
||||
|
||||
WARNF(cLRD "Encountered %u timeouts - results may be skewed." cRST,
|
||||
exec_hangs);
|
||||
|
||||
}
|
||||
|
||||
ck_free(b_data);
|
||||
|
||||
}
|
||||
@ -634,7 +699,7 @@ static void handle_stop_sig(int sig) {
|
||||
|
||||
stop_soon = 1;
|
||||
|
||||
if (child_pid > 0) kill(child_pid, SIGKILL);
|
||||
if (child_pid > 0) { kill(child_pid, SIGKILL); }
|
||||
|
||||
}
|
||||
|
||||
@ -645,7 +710,7 @@ static void set_up_environment(void) {
|
||||
u8 *x;
|
||||
|
||||
dev_null_fd = open("/dev/null", O_RDWR);
|
||||
if (dev_null_fd < 0) PFATAL("Unable to open /dev/null");
|
||||
if (dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
|
||||
|
||||
if (!prog_in) {
|
||||
|
||||
@ -654,11 +719,11 @@ static void set_up_environment(void) {
|
||||
if (access(use_dir, R_OK | W_OK | X_OK)) {
|
||||
|
||||
use_dir = get_afl_env("TMPDIR");
|
||||
if (!use_dir) use_dir = "/tmp";
|
||||
if (!use_dir) { use_dir = "/tmp"; }
|
||||
|
||||
}
|
||||
|
||||
prog_in = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, getpid());
|
||||
prog_in = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, (u32)getpid());
|
||||
|
||||
}
|
||||
|
||||
@ -668,25 +733,37 @@ static void set_up_environment(void) {
|
||||
|
||||
if (x) {
|
||||
|
||||
if (!strstr(x, "abort_on_error=1"))
|
||||
if (!strstr(x, "abort_on_error=1")) {
|
||||
|
||||
FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!");
|
||||
|
||||
if (!strstr(x, "symbolize=0"))
|
||||
}
|
||||
|
||||
if (!strstr(x, "symbolize=0")) {
|
||||
|
||||
FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
x = get_afl_env("MSAN_OPTIONS");
|
||||
|
||||
if (x) {
|
||||
|
||||
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR)))
|
||||
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) {
|
||||
|
||||
FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(
|
||||
MSAN_ERROR) " - please fix!");
|
||||
|
||||
if (!strstr(x, "symbolize=0"))
|
||||
}
|
||||
|
||||
if (!strstr(x, "symbolize=0")) {
|
||||
|
||||
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setenv("ASAN_OPTIONS",
|
||||
@ -713,20 +790,28 @@ static void set_up_environment(void) {
|
||||
s32 i, afl_preload_size = strlen(afl_preload);
|
||||
for (i = 0; i < afl_preload_size; ++i) {
|
||||
|
||||
if (afl_preload[i] == ',')
|
||||
if (afl_preload[i] == ',') {
|
||||
|
||||
PFATAL(
|
||||
"Comma (',') is not allowed in AFL_PRELOAD when -Q is "
|
||||
"specified!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (qemu_preload)
|
||||
if (qemu_preload) {
|
||||
|
||||
buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s",
|
||||
qemu_preload, afl_preload, afl_preload);
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s",
|
||||
afl_preload, afl_preload);
|
||||
|
||||
}
|
||||
|
||||
setenv("QEMU_SET_ENV", buf, 1);
|
||||
|
||||
ck_free(buf);
|
||||
@ -820,26 +905,26 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
SAYF(cCYA "afl-analyze" VERSION cRST " by Michal Zalewski\n");
|
||||
|
||||
while ((opt = getopt(argc, argv, "+i:f:m:t:eQUWh")) > 0)
|
||||
while ((opt = getopt(argc, argv, "+i:f:m:t:eQUWh")) > 0) {
|
||||
|
||||
switch (opt) {
|
||||
|
||||
case 'i':
|
||||
|
||||
if (in_file) FATAL("Multiple -i options not supported");
|
||||
if (in_file) { FATAL("Multiple -i options not supported"); }
|
||||
in_file = optarg;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
|
||||
if (prog_in) FATAL("Multiple -f options not supported");
|
||||
if (prog_in) { FATAL("Multiple -f options not supported"); }
|
||||
use_stdin = 0;
|
||||
prog_in = optarg;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
|
||||
if (edges_only) FATAL("Multiple -e options not supported");
|
||||
if (edges_only) { FATAL("Multiple -e options not supported"); }
|
||||
edges_only = 1;
|
||||
break;
|
||||
|
||||
@ -847,7 +932,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
u8 suffix = 'M';
|
||||
|
||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||
if (mem_limit_given) { FATAL("Multiple -m options not supported"); }
|
||||
mem_limit_given = 1;
|
||||
|
||||
if (!optarg) { FATAL("Wrong usage of -m"); }
|
||||
@ -860,66 +945,83 @@ int main(int argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
|
||||
optarg[0] == '-')
|
||||
optarg[0] == '-') {
|
||||
|
||||
FATAL("Bad syntax used for -m");
|
||||
|
||||
switch (suffix) {
|
||||
|
||||
case 'T': mem_limit *= 1024 * 1024; break;
|
||||
case 'G': mem_limit *= 1024; break;
|
||||
case 'k': mem_limit /= 1024; break;
|
||||
case 'M': break;
|
||||
|
||||
default: FATAL("Unsupported suffix or bad syntax for -m");
|
||||
|
||||
}
|
||||
|
||||
if (mem_limit < 5) FATAL("Dangerously low value of -m");
|
||||
switch (suffix) {
|
||||
|
||||
case 'T':
|
||||
mem_limit *= 1024 * 1024;
|
||||
break;
|
||||
case 'G':
|
||||
mem_limit *= 1024;
|
||||
break;
|
||||
case 'k':
|
||||
mem_limit /= 1024;
|
||||
break;
|
||||
case 'M':
|
||||
break;
|
||||
|
||||
default:
|
||||
FATAL("Unsupported suffix or bad syntax for -m");
|
||||
|
||||
}
|
||||
|
||||
if (mem_limit < 5) { FATAL("Dangerously low value of -m"); }
|
||||
|
||||
if (sizeof(rlim_t) == 4 && mem_limit > 2000) {
|
||||
|
||||
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
|
||||
FATAL("Value of -m out of range on 32-bit systems");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 't':
|
||||
|
||||
if (timeout_given) FATAL("Multiple -t options not supported");
|
||||
if (timeout_given) { FATAL("Multiple -t options not supported"); }
|
||||
timeout_given = 1;
|
||||
|
||||
if (!optarg) FATAL("Wrong usage of -t");
|
||||
if (!optarg) { FATAL("Wrong usage of -t"); }
|
||||
|
||||
exec_tmout = atoi(optarg);
|
||||
|
||||
if (exec_tmout < 10 || optarg[0] == '-')
|
||||
if (exec_tmout < 10 || optarg[0] == '-') {
|
||||
|
||||
FATAL("Dangerously low value of -t");
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'Q':
|
||||
|
||||
if (qemu_mode) FATAL("Multiple -Q options not supported");
|
||||
if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
|
||||
if (qemu_mode) { FATAL("Multiple -Q options not supported"); }
|
||||
if (!mem_limit_given) { mem_limit = MEM_LIMIT_QEMU; }
|
||||
|
||||
qemu_mode = 1;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
|
||||
if (unicorn_mode) FATAL("Multiple -U options not supported");
|
||||
if (!mem_limit_given) mem_limit = MEM_LIMIT_UNICORN;
|
||||
if (unicorn_mode) { FATAL("Multiple -U options not supported"); }
|
||||
if (!mem_limit_given) { mem_limit = MEM_LIMIT_UNICORN; }
|
||||
|
||||
unicorn_mode = 1;
|
||||
break;
|
||||
|
||||
case 'W': /* Wine+QEMU mode */
|
||||
|
||||
if (use_wine) FATAL("Multiple -W options not supported");
|
||||
if (use_wine) { FATAL("Multiple -W options not supported"); }
|
||||
qemu_mode = 1;
|
||||
use_wine = 1;
|
||||
|
||||
if (!mem_limit_given) mem_limit = 0;
|
||||
if (!mem_limit_given) { mem_limit = 0; }
|
||||
|
||||
break;
|
||||
|
||||
@ -928,11 +1030,14 @@ int main(int argc, char **argv, char **envp) {
|
||||
return -1;
|
||||
break;
|
||||
|
||||
default: usage(argv[0]);
|
||||
default:
|
||||
usage(argv[0]);
|
||||
|
||||
}
|
||||
|
||||
if (optind == argc || !in_file) usage(argv[0]);
|
||||
}
|
||||
|
||||
if (optind == argc || !in_file) { usage(argv[0]); }
|
||||
|
||||
map_size = get_map_size();
|
||||
|
||||
@ -952,17 +1057,24 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
if (qemu_mode) {
|
||||
|
||||
if (use_wine)
|
||||
if (use_wine) {
|
||||
|
||||
use_argv =
|
||||
get_wine_argv(argv[0], &target_path, argc - optind, argv + optind);
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
use_argv =
|
||||
get_qemu_argv(argv[0], &target_path, argc - optind, argv + optind);
|
||||
|
||||
} else
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
use_argv = argv + optind;
|
||||
|
||||
}
|
||||
|
||||
SAYF("\n");
|
||||
|
||||
read_initial_file();
|
||||
@ -972,17 +1084,23 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
analyze_run_target(use_argv, in_data, in_len, 1);
|
||||
|
||||
if (child_timed_out)
|
||||
if (child_timed_out) {
|
||||
|
||||
FATAL("Target binary times out (adjusting -t may help).");
|
||||
|
||||
if (get_afl_env("AFL_SKIP_BIN_CHECK") == NULL && !anything_set())
|
||||
}
|
||||
|
||||
if (get_afl_env("AFL_SKIP_BIN_CHECK") == NULL && !anything_set()) {
|
||||
|
||||
FATAL("No instrumentation detected.");
|
||||
|
||||
}
|
||||
|
||||
analyze(use_argv);
|
||||
|
||||
OKF("We're done here. Have a nice day!\n");
|
||||
|
||||
if (target_path) ck_free(target_path);
|
||||
if (target_path) { ck_free(target_path); }
|
||||
|
||||
afl_shm_deinit(&shm);
|
||||
|
||||
|
118
src/afl-as.c
118
src/afl-as.c
@ -79,9 +79,9 @@ static u8 use_64bit = 1;
|
||||
|
||||
static u8 use_64bit = 0;
|
||||
|
||||
#ifdef __APPLE__
|
||||
#error "Sorry, 32-bit Apple platforms are not supported."
|
||||
#endif /* __APPLE__ */
|
||||
#ifdef __APPLE__
|
||||
#error "Sorry, 32-bit Apple platforms are not supported."
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#endif /* ^WORD_SIZE_64 */
|
||||
|
||||
@ -126,9 +126,9 @@ static void edit_params(int argc, char **argv) {
|
||||
is not set. We need to check these non-standard variables to properly
|
||||
handle the pass_thru logic later on. */
|
||||
|
||||
if (!tmp_dir) tmp_dir = getenv("TEMP");
|
||||
if (!tmp_dir) tmp_dir = getenv("TMP");
|
||||
if (!tmp_dir) tmp_dir = "/tmp";
|
||||
if (!tmp_dir) { tmp_dir = getenv("TEMP"); }
|
||||
if (!tmp_dir) { tmp_dir = getenv("TMP"); }
|
||||
if (!tmp_dir) { tmp_dir = "/tmp"; }
|
||||
|
||||
as_params = ck_alloc((argc + 32) * sizeof(u8 *));
|
||||
|
||||
@ -138,11 +138,16 @@ static void edit_params(int argc, char **argv) {
|
||||
|
||||
for (i = 1; i < argc - 1; i++) {
|
||||
|
||||
if (!strcmp(argv[i], "--64"))
|
||||
if (!strcmp(argv[i], "--64")) {
|
||||
|
||||
use_64bit = 1;
|
||||
else if (!strcmp(argv[i], "--32"))
|
||||
|
||||
} else if (!strcmp(argv[i], "--32")) {
|
||||
|
||||
use_64bit = 0;
|
||||
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
/* The Apple case is a bit different... */
|
||||
@ -195,11 +200,16 @@ static void edit_params(int argc, char **argv) {
|
||||
|
||||
}
|
||||
|
||||
if (input_file[1])
|
||||
if (input_file[1]) {
|
||||
|
||||
FATAL("Incorrect use (not called through afl-gcc?)");
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
input_file = NULL;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* Check if this looks like a standard invocation as a part of an attempt
|
||||
@ -210,15 +220,20 @@ static void edit_params(int argc, char **argv) {
|
||||
if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
|
||||
strncmp(input_file, "/var/tmp/", 9) &&
|
||||
strncmp(input_file, "/tmp/", 5) &&
|
||||
getenv("AFL_AS_FORCE_INSTRUMENT") == NULL)
|
||||
getenv("AFL_AS_FORCE_INSTRUMENT") == NULL) {
|
||||
|
||||
pass_thru = 1;
|
||||
else if (getenv("AFL_AS_FORCE_INSTRUMENT"))
|
||||
|
||||
} else if (getenv("AFL_AS_FORCE_INSTRUMENT")) {
|
||||
|
||||
unsetenv("AFL_AS_FORCE_INSTRUMENT");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
modified_file =
|
||||
alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(), (u32)time(NULL));
|
||||
alloc_printf("%s/.afl-%u-%u.s", tmp_dir, (u32)getpid(), (u32)time(NULL));
|
||||
|
||||
wrap_things_up:
|
||||
|
||||
@ -251,19 +266,21 @@ static void add_instrumentation(void) {
|
||||
if (input_file) {
|
||||
|
||||
inf = fopen(input_file, "r");
|
||||
if (!inf) PFATAL("Unable to read '%s'", input_file);
|
||||
if (!inf) { PFATAL("Unable to read '%s'", input_file); }
|
||||
|
||||
} else
|
||||
} else {
|
||||
|
||||
inf = stdin;
|
||||
|
||||
}
|
||||
|
||||
outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600);
|
||||
|
||||
if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file);
|
||||
if (outfd < 0) { PFATAL("Unable to write to '%s'", modified_file); }
|
||||
|
||||
outf = fdopen(outfd, "w");
|
||||
|
||||
if (!outf) PFATAL("fdopen() failed");
|
||||
if (!outf) { PFATAL("fdopen() failed"); }
|
||||
|
||||
while (fgets(line, MAX_LINE, inf)) {
|
||||
|
||||
@ -287,7 +304,7 @@ static void add_instrumentation(void) {
|
||||
|
||||
fputs(line, outf);
|
||||
|
||||
if (pass_thru) continue;
|
||||
if (pass_thru) { continue; }
|
||||
|
||||
/* All right, this is where the actual fun begins. For one, we only want to
|
||||
instrument the .text section. So, let's keep track of that in processed
|
||||
@ -300,9 +317,12 @@ static void add_instrumentation(void) {
|
||||
around them, so we use that as a signal. */
|
||||
|
||||
if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
|
||||
isdigit(line[10]) && line[11] == '\n')
|
||||
isdigit(line[10]) && line[11] == '\n') {
|
||||
|
||||
skip_next_label = 1;
|
||||
|
||||
}
|
||||
|
||||
if (!strncmp(line + 2, "text\n", 5) ||
|
||||
!strncmp(line + 2, "section\t.text", 13) ||
|
||||
!strncmp(line + 2, "section\t__TEXT,__text", 21) ||
|
||||
@ -330,23 +350,23 @@ static void add_instrumentation(void) {
|
||||
|
||||
if (strstr(line, ".code")) {
|
||||
|
||||
if (strstr(line, ".code32")) skip_csect = use_64bit;
|
||||
if (strstr(line, ".code64")) skip_csect = !use_64bit;
|
||||
if (strstr(line, ".code32")) { skip_csect = use_64bit; }
|
||||
if (strstr(line, ".code64")) { skip_csect = !use_64bit; }
|
||||
|
||||
}
|
||||
|
||||
/* Detect syntax changes, as could happen with hand-written assembly.
|
||||
Skip Intel blocks, resume instrumentation when back to AT&T. */
|
||||
|
||||
if (strstr(line, ".intel_syntax")) skip_intel = 1;
|
||||
if (strstr(line, ".att_syntax")) skip_intel = 0;
|
||||
if (strstr(line, ".intel_syntax")) { skip_intel = 1; }
|
||||
if (strstr(line, ".att_syntax")) { skip_intel = 0; }
|
||||
|
||||
/* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */
|
||||
|
||||
if (line[0] == '#' || line[1] == '#') {
|
||||
|
||||
if (strstr(line, "#APP")) skip_app = 1;
|
||||
if (strstr(line, "#NO_APP")) skip_app = 0;
|
||||
if (strstr(line, "#APP")) { skip_app = 1; }
|
||||
if (strstr(line, "#NO_APP")) { skip_app = 0; }
|
||||
|
||||
}
|
||||
|
||||
@ -375,9 +395,12 @@ static void add_instrumentation(void) {
|
||||
*/
|
||||
|
||||
if (skip_intel || skip_app || skip_csect || !instr_ok || line[0] == '#' ||
|
||||
line[0] == ' ')
|
||||
line[0] == ' ') {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Conditional branch instruction (jnz, etc). We append the instrumentation
|
||||
right after the branch (to instrument the not-taken path) and at the
|
||||
branch destination label (handled later on). */
|
||||
@ -449,11 +472,16 @@ static void add_instrumentation(void) {
|
||||
.Lfunc_begin0-style exception handling calculations (a problem on
|
||||
MacOS X). */
|
||||
|
||||
if (!skip_next_label)
|
||||
if (!skip_next_label) {
|
||||
|
||||
instrument_next = 1;
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
skip_next_label = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -468,17 +496,19 @@ static void add_instrumentation(void) {
|
||||
|
||||
}
|
||||
|
||||
if (ins_lines) fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
|
||||
if (ins_lines) { fputs(use_64bit ? main_payload_64 : main_payload_32, outf); }
|
||||
|
||||
if (input_file) fclose(inf);
|
||||
if (input_file) { fclose(inf); }
|
||||
fclose(outf);
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
if (!ins_lines)
|
||||
if (!ins_lines) {
|
||||
|
||||
WARNF("No instrumentation targets found%s.",
|
||||
pass_thru ? " (pass-thru mode)" : "");
|
||||
else {
|
||||
|
||||
} else {
|
||||
|
||||
char modeline[100];
|
||||
snprintf(modeline, sizeof(modeline), "%s%s%s%s",
|
||||
@ -514,10 +544,12 @@ int main(int argc, char **argv) {
|
||||
|
||||
SAYF(cCYA "afl-as" VERSION cRST " by Michal Zalewski\n");
|
||||
|
||||
} else
|
||||
} else {
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
}
|
||||
|
||||
if (argc < 2 || (argc == 2 && strcmp(argv[1], "-h") == 0)) {
|
||||
|
||||
fprintf(
|
||||
@ -565,14 +597,20 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (inst_ratio_str) {
|
||||
|
||||
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100)
|
||||
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100) {
|
||||
|
||||
FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (getenv(AS_LOOP_ENV_VAR))
|
||||
if (getenv(AS_LOOP_ENV_VAR)) {
|
||||
|
||||
FATAL("Endless loop when calling 'as' (remove '.' from your PATH)");
|
||||
|
||||
}
|
||||
|
||||
setenv(AS_LOOP_ENV_VAR, "1", 1);
|
||||
|
||||
/* When compiling with ASAN, we don't have a particularly elegant way to skip
|
||||
@ -582,11 +620,11 @@ int main(int argc, char **argv) {
|
||||
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
|
||||
|
||||
sanitizer = 1;
|
||||
if (!getenv("AFL_INST_RATIO")) inst_ratio /= 3;
|
||||
if (!getenv("AFL_INST_RATIO")) { inst_ratio /= 3; }
|
||||
|
||||
}
|
||||
|
||||
if (!just_version) add_instrumentation();
|
||||
if (!just_version) { add_instrumentation(); }
|
||||
|
||||
if (!(pid = fork())) {
|
||||
|
||||
@ -595,11 +633,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
}
|
||||
|
||||
if (pid < 0) PFATAL("fork() failed");
|
||||
if (pid < 0) { PFATAL("fork() failed"); }
|
||||
|
||||
if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
|
||||
if (waitpid(pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); }
|
||||
|
||||
if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file);
|
||||
if (!getenv("AFL_KEEP_ASSEMBLY")) { unlink(modified_file); }
|
||||
|
||||
exit(WEXITSTATUS(status));
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
/* Detect @@ in args. */
|
||||
#ifndef __glibc__
|
||||
#include <unistd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
@ -64,17 +64,19 @@ char *afl_environment_variables[] = {
|
||||
"AFL_LD_PRELOAD", "AFL_LD_VERBOSE", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM",
|
||||
"AFL_LLVM_CTX", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD",
|
||||
"AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY",
|
||||
"AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", "AFL_LLVM_LAF_SPLIT_COMPARES",
|
||||
"AFL_LLVM_LAF_SPLIT_COMPARES_BITW", "AFL_LLVM_LAF_SPLIT_FLOATS",
|
||||
"AFL_LLVM_LAF_SPLIT_SWITCHES", "AFL_LLVM_LAF_TRANSFORM_COMPARES",
|
||||
"AFL_LLVM_NGRAM_SIZE", "AFL_NGRAM_SIZE", "AFL_LLVM_NOT_ZERO",
|
||||
"AFL_LLVM_WHITELIST", "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID",
|
||||
"AFL_LLVM_LTO_DONTWRITEID", "AFL_NO_ARITH", "AFL_NO_BUILTIN",
|
||||
"AFL_NO_CPU_RED", "AFL_NO_FORKSRV", "AFL_NO_UI", "AFL_NO_PYTHON",
|
||||
"AFL_LLVM_SKIPSINGLEBLOCK", "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK",
|
||||
"AFL_LLVM_LAF_SPLIT_COMPARES", "AFL_LLVM_LAF_SPLIT_COMPARES_BITW",
|
||||
"AFL_LLVM_LAF_SPLIT_FLOATS", "AFL_LLVM_LAF_SPLIT_SWITCHES",
|
||||
"AFL_LLVM_LAF_TRANSFORM_COMPARES", "AFL_LLVM_MAP_ADDR",
|
||||
"AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", "AFL_NGRAM_SIZE",
|
||||
"AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", "AFL_LLVM_SKIP_NEVERZERO",
|
||||
"AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID",
|
||||
"AFL_NO_ARITH", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV",
|
||||
"AFL_NO_UI", "AFL_NO_PYTHON", "AFL_UNTRACER_FILE", "AFL_LLVM_USE_TRACE_PC",
|
||||
"AFL_NO_X86", // not really an env but we dont want to warn on it
|
||||
"AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_PATH", "AFL_PERFORMANCE_FILE",
|
||||
//"AFL_PERSISTENT", // not implemented anymore, so warn additionally
|
||||
"AFL_POST_LIBRARY", "AFL_PRELOAD", "AFL_PYTHON_MODULE", "AFL_QEMU_COMPCOV",
|
||||
"AFL_PRELOAD", "AFL_PYTHON_MODULE", "AFL_QEMU_COMPCOV",
|
||||
"AFL_QEMU_COMPCOV_DEBUG", "AFL_QEMU_DEBUG_MAPS", "AFL_QEMU_DISABLE_CACHE",
|
||||
"AFL_QEMU_PERSISTENT_ADDR", "AFL_QEMU_PERSISTENT_CNT",
|
||||
"AFL_QEMU_PERSISTENT_GPR", "AFL_QEMU_PERSISTENT_HOOK",
|
||||
@ -101,7 +103,7 @@ void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) {
|
||||
|
||||
if (aa_loc) {
|
||||
|
||||
if (!prog_in) FATAL("@@ syntax is not supported by this tool.");
|
||||
if (!prog_in) { FATAL("@@ syntax is not supported by this tool."); }
|
||||
|
||||
*use_stdin = 0;
|
||||
|
||||
@ -198,7 +200,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
|
||||
|
||||
cp = alloc_printf("%s/afl-qemu-trace", tmp);
|
||||
|
||||
if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp);
|
||||
if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
|
||||
|
||||
*target_path_p = new_argv[0] = cp;
|
||||
return new_argv;
|
||||
@ -230,7 +232,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
|
||||
|
||||
if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
|
||||
|
||||
if (cp) ck_free(cp);
|
||||
if (cp) { ck_free(cp); }
|
||||
*target_path_p = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace");
|
||||
|
||||
return new_argv;
|
||||
@ -277,13 +279,13 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
|
||||
|
||||
cp = alloc_printf("%s/afl-qemu-trace", tmp);
|
||||
|
||||
if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp);
|
||||
if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
|
||||
|
||||
ck_free(cp);
|
||||
|
||||
cp = alloc_printf("%s/afl-wine-trace", tmp);
|
||||
|
||||
if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp);
|
||||
if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
|
||||
|
||||
*target_path_p = new_argv[0] = cp;
|
||||
return new_argv;
|
||||
@ -416,15 +418,22 @@ u8 *find_binary(u8 *fname) {
|
||||
ck_free(cur_elem);
|
||||
|
||||
if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
|
||||
(st.st_mode & 0111) && st.st_size >= 4)
|
||||
(st.st_mode & 0111) && st.st_size >= 4) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
ck_free(target_path);
|
||||
target_path = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (!target_path) FATAL("Program '%s' not found or not executable", fname);
|
||||
if (!target_path) {
|
||||
|
||||
FATAL("Program '%s' not found or not executable", fname);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -434,7 +443,7 @@ u8 *find_binary(u8 *fname) {
|
||||
|
||||
void check_environment_vars(char **envp) {
|
||||
|
||||
if (be_quiet) return;
|
||||
if (be_quiet) { return; }
|
||||
|
||||
int index = 0, found = 0;
|
||||
char *env, *val;
|
||||
@ -448,24 +457,30 @@ void check_environment_vars(char **envp) {
|
||||
} else if (strncmp(env, "AFL_", 4) == 0) {
|
||||
|
||||
int i = 0, match = 0;
|
||||
while (match == 0 && afl_environment_variables[i] != NULL)
|
||||
while (match == 0 && afl_environment_variables[i] != NULL) {
|
||||
|
||||
if (strncmp(env, afl_environment_variables[i],
|
||||
strlen(afl_environment_variables[i])) == 0 &&
|
||||
env[strlen(afl_environment_variables[i])] == '=') {
|
||||
|
||||
match = 1;
|
||||
if ((val = getenv(afl_environment_variables[i])) && !*val)
|
||||
if ((val = getenv(afl_environment_variables[i])) && !*val) {
|
||||
|
||||
WARNF(
|
||||
"AFL environment variable %s defined but is empty, this can "
|
||||
"lead to unexpected consequences",
|
||||
afl_environment_variables[i]);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (match == 0) {
|
||||
|
||||
WARNF("Mistyped AFL environment variable: %s", env);
|
||||
@ -477,7 +492,7 @@ void check_environment_vars(char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (found) sleep(2);
|
||||
if (found) { sleep(2); }
|
||||
|
||||
}
|
||||
|
||||
@ -485,10 +500,16 @@ char *get_afl_env(char *env) {
|
||||
|
||||
char *val;
|
||||
|
||||
if ((val = getenv(env)) != NULL)
|
||||
if (!be_quiet)
|
||||
if ((val = getenv(env)) != NULL) {
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
OKF("Loaded environment variable %s with value %s", env, val);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return val;
|
||||
|
||||
}
|
||||
@ -499,7 +520,7 @@ void read_bitmap(u8 *fname, u8 *map, size_t len) {
|
||||
|
||||
s32 fd = open(fname, O_RDONLY);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to open '%s'", fname);
|
||||
if (fd < 0) { PFATAL("Unable to open '%s'", fname); }
|
||||
|
||||
ck_read(fd, map, len, fname);
|
||||
|
||||
@ -898,7 +919,7 @@ u32 read_timed(s32 fd, void *buf, size_t len, u32 timeout_ms,
|
||||
|
||||
}
|
||||
|
||||
u32 get_map_size() {
|
||||
u32 get_map_size(void) {
|
||||
|
||||
uint32_t map_size = MAP_SIZE;
|
||||
char * ptr;
|
||||
@ -906,9 +927,14 @@ u32 get_map_size() {
|
||||
if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) {
|
||||
|
||||
map_size = atoi(ptr);
|
||||
if (map_size < 8 || map_size > (1 << 29))
|
||||
FATAL("illegal AFL_MAP_SIZE %u, must be between 2^3 and 2^30", map_size);
|
||||
if (map_size % 8) map_size = (((map_size >> 3) + 1) << 3);
|
||||
if (map_size < 8 || map_size > (1 << 29)) {
|
||||
|
||||
FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 8,
|
||||
1 << 29);
|
||||
|
||||
}
|
||||
|
||||
if (map_size % 8) { map_size = (((map_size >> 3) + 1) << 3); }
|
||||
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@
|
||||
|
||||
/* Describe integer as memory size. */
|
||||
|
||||
list_t fsrv_list = {.element_prealloc_count = 0};
|
||||
static list_t fsrv_list = {.element_prealloc_count = 0};
|
||||
|
||||
static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) {
|
||||
|
||||
@ -67,7 +67,6 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
|
||||
|
||||
// this structure needs default so we initialize it if this was not done
|
||||
// already
|
||||
|
||||
fsrv->out_fd = -1;
|
||||
fsrv->out_dir_fd = -1;
|
||||
fsrv->dev_null_fd = -1;
|
||||
@ -83,7 +82,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
|
||||
|
||||
/* exec related stuff */
|
||||
fsrv->child_pid = -1;
|
||||
fsrv->map_size = MAP_SIZE;
|
||||
fsrv->map_size = get_map_size();
|
||||
fsrv->use_fauxsrv = 0;
|
||||
fsrv->last_run_timed_out = 0;
|
||||
|
||||
@ -128,12 +127,16 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) {
|
||||
unsigned char tmp[4] = {0, 0, 0, 0};
|
||||
pid_t child_pid = -1;
|
||||
|
||||
if (!be_quiet) ACTF("Using Fauxserver:");
|
||||
if (!be_quiet) { ACTF("Using Fauxserver:"); }
|
||||
|
||||
/* Phone home and tell the parent that we're OK. If parent isn't there,
|
||||
assume we're not running in forkserver mode and just execute program. */
|
||||
|
||||
if (write(FORKSRV_FD + 1, tmp, 4) != 4) abort(); // TODO: Abort?
|
||||
if (write(FORKSRV_FD + 1, tmp, 4) != 4) {
|
||||
|
||||
abort(); // TODO: Abort?
|
||||
|
||||
}
|
||||
|
||||
void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
@ -144,13 +147,13 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) {
|
||||
|
||||
/* Wait for parent by reading from the pipe. Exit if read fails. */
|
||||
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(0);
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) { exit(0); }
|
||||
|
||||
/* Create a clone of our process. */
|
||||
|
||||
child_pid = fork();
|
||||
|
||||
if (child_pid < 0) PFATAL("Fork failed");
|
||||
if (child_pid < 0) { PFATAL("Fork failed"); }
|
||||
|
||||
/* In child process: close fds, resume execution. */
|
||||
|
||||
@ -177,7 +180,7 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) {
|
||||
|
||||
/* In parent process: write PID to AFL. */
|
||||
|
||||
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(0);
|
||||
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) { exit(0); }
|
||||
|
||||
/* after child exited, get and relay exit status to parent through waitpid.
|
||||
*/
|
||||
@ -191,7 +194,45 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) {
|
||||
|
||||
/* Relay wait status to AFL pipe, then loop back. */
|
||||
|
||||
if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(0);
|
||||
if (write(FORKSRV_FD + 1, &status, 4) != 4) { exit(0); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Report on the error received via the forkserver controller and exit */
|
||||
static void report_error_and_exit(int error) {
|
||||
|
||||
switch (error) {
|
||||
|
||||
case FS_ERROR_MAP_SIZE:
|
||||
FATAL(
|
||||
"AFL_MAP_SIZE is not set and fuzzing target reports that the "
|
||||
"required size is very large. Solution: Run the fuzzing target "
|
||||
"stand-alone with the environment variable AFL_DEBUG=1 set and set "
|
||||
"the value for __afl_final_loc in the AFL_MAP_SIZE environment "
|
||||
"variable for afl-fuzz.");
|
||||
break;
|
||||
case FS_ERROR_MAP_ADDR:
|
||||
FATAL(
|
||||
"the fuzzing target reports that hardcoded map address might be the "
|
||||
"reason the mmap of the shared memory failed. Solution: recompile "
|
||||
"the target with either afl-clang-lto and the environment variable "
|
||||
"AFL_LLVM_MAP_DYNAMIC set or recompile with afl-clang-fast.");
|
||||
break;
|
||||
case FS_ERROR_SHM_OPEN:
|
||||
FATAL("the fuzzing target reports that the shm_open() call failed.");
|
||||
break;
|
||||
case FS_ERROR_SHMAT:
|
||||
FATAL("the fuzzing target reports that the shmat() call failed.");
|
||||
break;
|
||||
case FS_ERROR_MMAP:
|
||||
FATAL(
|
||||
"the fuzzing target reports that the mmap() call to the share memory "
|
||||
"failed.");
|
||||
break;
|
||||
default:
|
||||
FATAL("unknown error code %u from fuzzing target!", error);
|
||||
|
||||
}
|
||||
|
||||
@ -212,25 +253,28 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
int status;
|
||||
s32 rlen;
|
||||
|
||||
if (!be_quiet) ACTF("Spinning up the fork server...");
|
||||
if (!be_quiet) { ACTF("Spinning up the fork server..."); }
|
||||
|
||||
if (fsrv->use_fauxsrv) {
|
||||
|
||||
/* TODO: Come up with sone nice way to initalize this all */
|
||||
|
||||
if (fsrv->init_child_func != fsrv_exec_child)
|
||||
if (fsrv->init_child_func != fsrv_exec_child) {
|
||||
|
||||
FATAL("Different forkserver not compatible with fauxserver");
|
||||
|
||||
}
|
||||
|
||||
fsrv->init_child_func = afl_fauxsrv_execv;
|
||||
|
||||
}
|
||||
|
||||
if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed");
|
||||
if (pipe(st_pipe) || pipe(ctl_pipe)) { PFATAL("pipe() failed"); }
|
||||
|
||||
fsrv->last_run_timed_out = 0;
|
||||
fsrv->fsrv_pid = fork();
|
||||
|
||||
if (fsrv->fsrv_pid < 0) PFATAL("fork() failed");
|
||||
if (fsrv->fsrv_pid < 0) { PFATAL("fork() failed"); }
|
||||
|
||||
if (!fsrv->fsrv_pid) {
|
||||
|
||||
@ -295,8 +339,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
/* Set up control and status pipes, close the unneeded original fds. */
|
||||
|
||||
if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed");
|
||||
if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed");
|
||||
if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) { PFATAL("dup2() failed"); }
|
||||
if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) { PFATAL("dup2() failed"); }
|
||||
|
||||
close(ctl_pipe[0]);
|
||||
close(ctl_pipe[1]);
|
||||
@ -308,12 +352,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
close(fsrv->dev_urandom_fd);
|
||||
#endif
|
||||
if (fsrv->plot_file != NULL) fclose(fsrv->plot_file);
|
||||
if (fsrv->plot_file != NULL) { fclose(fsrv->plot_file); }
|
||||
|
||||
/* This should improve performance a bit, since it stops the linker from
|
||||
doing extra work post-fork(). */
|
||||
|
||||
if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);
|
||||
if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 0); }
|
||||
|
||||
/* Set sane defaults for ASAN if nothing else specified. */
|
||||
|
||||
@ -391,17 +435,23 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
if (rlen == 4) {
|
||||
|
||||
if (!be_quiet) OKF("All right - fork server is up.");
|
||||
if (!be_quiet) { OKF("All right - fork server is up."); }
|
||||
|
||||
if ((status & FS_OPT_ERROR) == FS_OPT_ERROR)
|
||||
report_error_and_exit(FS_OPT_GET_ERROR(status));
|
||||
|
||||
if ((status & FS_OPT_ENABLED) == FS_OPT_ENABLED) {
|
||||
|
||||
if (!be_quiet && getenv("AFL_DEBUG"))
|
||||
if (!be_quiet && getenv("AFL_DEBUG")) {
|
||||
|
||||
ACTF("Extended forkserver functions received (%08x).", status);
|
||||
|
||||
}
|
||||
|
||||
if ((status & FS_OPT_SNAPSHOT) == FS_OPT_SNAPSHOT) {
|
||||
|
||||
fsrv->snapshot = 1;
|
||||
if (!be_quiet) ACTF("Using SNAPSHOT feature.");
|
||||
if (!be_quiet) { ACTF("Using SNAPSHOT feature."); }
|
||||
|
||||
}
|
||||
|
||||
@ -409,7 +459,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
u32 tmp_map_size = FS_OPT_GET_MAPSIZE(status);
|
||||
|
||||
if (!fsrv->map_size) fsrv->map_size = MAP_SIZE;
|
||||
if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; }
|
||||
|
||||
if (unlikely(tmp_map_size % 8)) {
|
||||
|
||||
@ -419,13 +469,18 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
}
|
||||
|
||||
if (!be_quiet) ACTF("Target map size: %u", tmp_map_size);
|
||||
if (tmp_map_size > fsrv->map_size)
|
||||
if (!be_quiet) { ACTF("Target map size: %u", tmp_map_size); }
|
||||
if (tmp_map_size > fsrv->map_size) {
|
||||
|
||||
FATAL(
|
||||
"Target's coverage map size of %u is larger than the one this "
|
||||
"afl++ is set with (%u) (change MAP_SIZE_POW2 in config.h and "
|
||||
"recompile or set AFL_MAP_SIZE)\n",
|
||||
tmp_map_size, fsrv->map_size);
|
||||
"afl++ is set with (%u). Either set AFL_MAP_SIZE=%u and restart "
|
||||
" afl-fuzz, or change MAP_SIZE_POW2 in config.h and recompile "
|
||||
"afl-fuzz",
|
||||
tmp_map_size, fsrv->map_size, tmp_map_size);
|
||||
|
||||
}
|
||||
|
||||
fsrv->map_size = tmp_map_size;
|
||||
|
||||
}
|
||||
@ -436,27 +491,44 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
// this is not afl-fuzz - we deny and return
|
||||
status = (0xffffffff ^ (FS_OPT_ENABLED | FS_OPT_AUTODICT));
|
||||
if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4)
|
||||
if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
|
||||
|
||||
FATAL("Writing to forkserver failed.");
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!be_quiet) ACTF("Using AUTODICT feature.");
|
||||
if (!be_quiet) { ACTF("Using AUTODICT feature."); }
|
||||
status = (FS_OPT_ENABLED | FS_OPT_AUTODICT);
|
||||
if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4)
|
||||
if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
|
||||
|
||||
FATAL("Writing to forkserver failed.");
|
||||
if (read(fsrv->fsrv_st_fd, &status, 4) != 4)
|
||||
|
||||
}
|
||||
|
||||
if (read(fsrv->fsrv_st_fd, &status, 4) != 4) {
|
||||
|
||||
FATAL("Reading from forkserver failed.");
|
||||
|
||||
if (status < 2 || (u32)status > 0xffffff)
|
||||
}
|
||||
|
||||
if (status < 2 || (u32)status > 0xffffff) {
|
||||
|
||||
FATAL("Dictionary has an illegal size: %d", status);
|
||||
|
||||
}
|
||||
|
||||
u32 len = status, offset = 0, count = 0;
|
||||
u8 *dict = ck_alloc(len);
|
||||
if (dict == NULL)
|
||||
if (dict == NULL) {
|
||||
|
||||
FATAL("Could not allocate %u bytes of autodictionary memory", len);
|
||||
|
||||
}
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
rlen = read(fsrv->fsrv_st_fd, dict + offset, len);
|
||||
@ -486,7 +558,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
}
|
||||
|
||||
if (!be_quiet) ACTF("Loaded %u autodictionary entries", count);
|
||||
if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); }
|
||||
ck_free(dict);
|
||||
|
||||
}
|
||||
@ -497,10 +569,13 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
}
|
||||
|
||||
if (fsrv->last_run_timed_out)
|
||||
if (fsrv->last_run_timed_out) {
|
||||
|
||||
FATAL("Timeout while initializing fork server (adjusting -t may help)");
|
||||
|
||||
if (waitpid(fsrv->fsrv_pid, &status, 0) <= 0) PFATAL("waitpid() failed");
|
||||
}
|
||||
|
||||
if (waitpid(fsrv->fsrv_pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); }
|
||||
|
||||
if (WIFSIGNALED(status)) {
|
||||
|
||||
@ -580,9 +655,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
}
|
||||
|
||||
if (*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG)
|
||||
if (*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG) {
|
||||
|
||||
FATAL("Unable to execute target application ('%s')", argv[0]);
|
||||
|
||||
}
|
||||
|
||||
if (fsrv->mem_limit && fsrv->mem_limit < 500 && fsrv->uses_asan) {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
@ -598,12 +676,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
} else if (!fsrv->mem_limit) {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"Hmm, looks like the target binary terminated "
|
||||
"before we could complete a\n"
|
||||
" handshake with the injected code. Perhaps there is a horrible "
|
||||
"bug in the\n"
|
||||
" fuzzer. Poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
"tips.\n");
|
||||
"Hmm, looks like the target binary terminated before we could"
|
||||
"complete a handshake with the injected code.\n"
|
||||
"If the target was compiled with afl-clang-lto then recompiling with"
|
||||
"AFL_LLVM_MAP_DYNAMIC might solve your problem.\n"
|
||||
"Otherwise there is a horrible bug in the fuzzer.\n"
|
||||
"Poke <afl-users@googlegroups.com> for troubleshooting tips.\n");
|
||||
|
||||
} else {
|
||||
|
||||
@ -630,6 +708,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
" estimate the required amount of virtual memory for the "
|
||||
"binary.\n\n"
|
||||
|
||||
" - the target was compiled with afl-clang-lto and a constructor "
|
||||
"was\n"
|
||||
" instrumented, recompiling with AFL_LLVM_MAP_DYNAMIC might solve "
|
||||
"your\n"
|
||||
" problem\n\n"
|
||||
|
||||
" - Less likely, there is a horrible bug in the fuzzer. If other "
|
||||
"options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
@ -651,7 +735,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
static void afl_fsrv_kill(afl_forkserver_t *fsrv) {
|
||||
|
||||
if (fsrv->child_pid > 0) kill(fsrv->child_pid, SIGKILL);
|
||||
if (fsrv->child_pid > 0) { kill(fsrv->child_pid, SIGKILL); }
|
||||
if (fsrv->fsrv_pid > 0) {
|
||||
|
||||
kill(fsrv->fsrv_pid, SIGKILL);
|
||||
@ -680,7 +764,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
|
||||
|
||||
}
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fsrv->out_file);
|
||||
if (fd < 0) { PFATAL("Unable to create '%s'", fsrv->out_file); }
|
||||
|
||||
} else {
|
||||
|
||||
@ -692,7 +776,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
|
||||
|
||||
if (!fsrv->out_file) {
|
||||
|
||||
if (ftruncate(fd, len)) PFATAL("ftruncate() failed");
|
||||
if (ftruncate(fd, len)) { PFATAL("ftruncate() failed"); }
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
} else {
|
||||
@ -712,8 +796,6 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
s32 res;
|
||||
u32 exec_ms;
|
||||
|
||||
int status = 0;
|
||||
|
||||
/* After this memset, fsrv->trace_bits[] are effectively volatile, so we
|
||||
must prevent any earlier operations from venturing into that
|
||||
territory. */
|
||||
@ -727,7 +809,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
if ((res = write(fsrv->fsrv_ctl_fd, &fsrv->last_run_timed_out, 4)) != 4) {
|
||||
|
||||
if (*stop_soon_p) return 0;
|
||||
if (*stop_soon_p) { return 0; }
|
||||
RPFATAL(res, "Unable to request new process from fork server (OOM?)");
|
||||
|
||||
}
|
||||
@ -736,14 +818,15 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
if ((res = read(fsrv->fsrv_st_fd, &fsrv->child_pid, 4)) != 4) {
|
||||
|
||||
if (*stop_soon_p) return 0;
|
||||
if (*stop_soon_p) { return 0; }
|
||||
RPFATAL(res, "Unable to request new process from fork server (OOM?)");
|
||||
|
||||
}
|
||||
|
||||
if (fsrv->child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");
|
||||
if (fsrv->child_pid <= 0) { FATAL("Fork server is misbehaving (OOM?)"); }
|
||||
|
||||
exec_ms = read_timed(fsrv->fsrv_st_fd, &status, 4, timeout, stop_soon_p);
|
||||
exec_ms = read_timed(fsrv->fsrv_st_fd, &fsrv->child_status, 4, timeout,
|
||||
stop_soon_p);
|
||||
|
||||
if (exec_ms > timeout) {
|
||||
|
||||
@ -752,13 +835,13 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
kill(fsrv->child_pid, SIGKILL);
|
||||
fsrv->last_run_timed_out = 1;
|
||||
if (read(fsrv->fsrv_st_fd, &status, 4) < 4) exec_ms = 0;
|
||||
if (read(fsrv->fsrv_st_fd, &fsrv->child_status, 4) < 4) { exec_ms = 0; }
|
||||
|
||||
}
|
||||
|
||||
if (!exec_ms) {
|
||||
|
||||
if (*stop_soon_p) return 0;
|
||||
if (*stop_soon_p) { return 0; }
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"Unable to communicate with fork server. Some possible reasons:\n\n"
|
||||
" - You've run out of memory. Use -m to increase the the memory "
|
||||
@ -784,7 +867,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(status)) fsrv->child_pid = 0;
|
||||
if (!WIFSTOPPED(fsrv->child_status)) { fsrv->child_pid = 0; }
|
||||
|
||||
fsrv->total_execs++;
|
||||
|
||||
@ -796,13 +879,16 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
/* Report outcome to caller. */
|
||||
|
||||
if (WIFSIGNALED(status) && !*stop_soon_p) {
|
||||
if (WIFSIGNALED(fsrv->child_status) && !*stop_soon_p) {
|
||||
|
||||
fsrv->last_kill_signal = WTERMSIG(status);
|
||||
fsrv->last_kill_signal = WTERMSIG(fsrv->child_status);
|
||||
|
||||
if (fsrv->last_run_timed_out && fsrv->last_kill_signal == SIGKILL) {
|
||||
|
||||
if (fsrv->last_run_timed_out && fsrv->last_kill_signal == SIGKILL)
|
||||
return FSRV_RUN_TMOUT;
|
||||
|
||||
}
|
||||
|
||||
return FSRV_RUN_CRASH;
|
||||
|
||||
}
|
||||
@ -810,7 +896,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
/* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and
|
||||
must use a special exit code. */
|
||||
|
||||
if (fsrv->uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
|
||||
if (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) {
|
||||
|
||||
fsrv->last_kill_signal = 0;
|
||||
return FSRV_RUN_CRASH;
|
||||
|
@ -35,13 +35,13 @@ void write_bitmap(afl_state_t *afl) {
|
||||
u8 fname[PATH_MAX];
|
||||
s32 fd;
|
||||
|
||||
if (!afl->bitmap_changed) return;
|
||||
if (!afl->bitmap_changed) { return; }
|
||||
afl->bitmap_changed = 0;
|
||||
|
||||
snprintf(fname, PATH_MAX, "%s/fuzz_bitmap", afl->out_dir);
|
||||
fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to open '%s'", fname);
|
||||
if (fd < 0) { PFATAL("Unable to open '%s'", fname); }
|
||||
|
||||
ck_write(fd, afl->virgin_bits, afl->fsrv.map_size, fname);
|
||||
|
||||
@ -102,11 +102,16 @@ u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) {
|
||||
(cur[1] && vir[1] == 0xff) || (cur[2] && vir[2] == 0xff) ||
|
||||
(cur[3] && vir[3] == 0xff) || (cur[4] && vir[4] == 0xff) ||
|
||||
(cur[5] && vir[5] == 0xff) || (cur[6] && vir[6] == 0xff) ||
|
||||
(cur[7] && vir[7] == 0xff))
|
||||
(cur[7] && vir[7] == 0xff)) {
|
||||
|
||||
ret = 2;
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
ret = 1;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (*virgin == 0xffffffff || (cur[0] && vir[0] == 0xff) ||
|
||||
@ -129,9 +134,12 @@ u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) {
|
||||
|
||||
}
|
||||
|
||||
if (unlikely(ret) && likely(virgin_map == afl->virgin_bits))
|
||||
if (unlikely(ret) && likely(virgin_map == afl->virgin_bits)) {
|
||||
|
||||
afl->bitmap_changed = 1;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
@ -183,11 +191,11 @@ u32 count_bytes(afl_state_t *afl, u8 *mem) {
|
||||
|
||||
u32 v = *(ptr++);
|
||||
|
||||
if (!v) continue;
|
||||
if (v & 0x000000ff) ++ret;
|
||||
if (v & 0x0000ff00) ++ret;
|
||||
if (v & 0x00ff0000) ++ret;
|
||||
if (v & 0xff000000) ++ret;
|
||||
if (!v) { continue; }
|
||||
if (v & 0x000000ff) { ++ret; }
|
||||
if (v & 0x0000ff00) { ++ret; }
|
||||
if (v & 0x00ff0000) { ++ret; }
|
||||
if (v & 0xff000000) { ++ret; }
|
||||
|
||||
}
|
||||
|
||||
@ -211,11 +219,11 @@ u32 count_non_255_bytes(afl_state_t *afl, u8 *mem) {
|
||||
/* This is called on the virgin bitmap, so optimize for the most likely
|
||||
case. */
|
||||
|
||||
if (v == 0xffffffff) continue;
|
||||
if ((v & 0x000000ff) != 0x000000ff) ++ret;
|
||||
if ((v & 0x0000ff00) != 0x0000ff00) ++ret;
|
||||
if ((v & 0x00ff0000) != 0x00ff0000) ++ret;
|
||||
if ((v & 0xff000000) != 0xff000000) ++ret;
|
||||
if (v == 0xffffffff) { continue; }
|
||||
if ((v & 0x000000ff) != 0x000000ff) { ++ret; }
|
||||
if ((v & 0x0000ff00) != 0x0000ff00) { ++ret; }
|
||||
if ((v & 0x00ff0000) != 0x00ff0000) { ++ret; }
|
||||
if ((v & 0xff000000) != 0xff000000) { ++ret; }
|
||||
|
||||
}
|
||||
|
||||
@ -257,10 +265,12 @@ void simplify_trace(afl_state_t *afl, u64 *mem) {
|
||||
mem8[6] = simplify_lookup[mem8[6]];
|
||||
mem8[7] = simplify_lookup[mem8[7]];
|
||||
|
||||
} else
|
||||
} else {
|
||||
|
||||
*mem = 0x0101010101010101ULL;
|
||||
|
||||
}
|
||||
|
||||
++mem;
|
||||
|
||||
}
|
||||
@ -322,11 +332,17 @@ void init_count_class16(void) {
|
||||
|
||||
u32 b1, b2;
|
||||
|
||||
for (b1 = 0; b1 < 256; b1++)
|
||||
for (b2 = 0; b2 < 256; b2++)
|
||||
for (b1 = 0; b1 < 256; b1++) {
|
||||
|
||||
for (b2 = 0; b2 < 256; b2++) {
|
||||
|
||||
count_class_lookup16[(b1 << 8) + b2] =
|
||||
(count_class_lookup8[b1] << 8) | count_class_lookup8[b2];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef WORD_SIZE_64
|
||||
@ -397,7 +413,7 @@ void minimize_bits(afl_state_t *afl, u8 *dst, u8 *src) {
|
||||
|
||||
while (i < afl->fsrv.map_size) {
|
||||
|
||||
if (*(src++)) dst[i >> 3] |= 1 << (i & 7);
|
||||
if (*(src++)) { dst[i >> 3] |= 1 << (i & 7); }
|
||||
++i;
|
||||
|
||||
}
|
||||
@ -421,29 +437,37 @@ u8 *describe_op(afl_state_t *afl, u8 hnb) {
|
||||
|
||||
sprintf(ret, "src:%06u", afl->current_entry);
|
||||
|
||||
sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time);
|
||||
if (afl->splicing_with >= 0) {
|
||||
|
||||
if (afl->splicing_with >= 0)
|
||||
sprintf(ret + strlen(ret), "+%06d", afl->splicing_with);
|
||||
|
||||
}
|
||||
|
||||
sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time);
|
||||
|
||||
sprintf(ret + strlen(ret), ",op:%s", afl->stage_short);
|
||||
|
||||
if (afl->stage_cur_byte >= 0) {
|
||||
|
||||
sprintf(ret + strlen(ret), ",pos:%d", afl->stage_cur_byte);
|
||||
|
||||
if (afl->stage_val_type != STAGE_VAL_NONE)
|
||||
if (afl->stage_val_type != STAGE_VAL_NONE) {
|
||||
|
||||
sprintf(ret + strlen(ret), ",val:%s%+d",
|
||||
(afl->stage_val_type == STAGE_VAL_BE) ? "be:" : "",
|
||||
afl->stage_cur_val);
|
||||
|
||||
} else
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
sprintf(ret + strlen(ret), ",rep:%d", afl->stage_cur_val);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hnb == 2) strcat(ret, ",+cov");
|
||||
if (hnb == 2) { strcat(ret, ",+cov"); }
|
||||
|
||||
return ret;
|
||||
|
||||
@ -467,7 +491,7 @@ static void write_crash_readme(afl_state_t *afl) {
|
||||
|
||||
/* Do not die on errors here - that would be impolite. */
|
||||
|
||||
if (unlikely(fd < 0)) return;
|
||||
if (unlikely(fd < 0)) { return; }
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
|
||||
@ -512,7 +536,7 @@ static void write_crash_readme(afl_state_t *afl) {
|
||||
|
||||
u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
|
||||
if (unlikely(len == 0)) return 0;
|
||||
if (unlikely(len == 0)) { return 0; }
|
||||
|
||||
u8 *queue_fn = "";
|
||||
u8 hnb = '\0';
|
||||
@ -545,7 +569,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
|
||||
if (!(hnb = has_new_bits(afl, afl->virgin_bits))) {
|
||||
|
||||
if (unlikely(afl->crash_mode)) ++afl->total_crashes;
|
||||
if (unlikely(afl->crash_mode)) { ++afl->total_crashes; }
|
||||
return 0;
|
||||
|
||||
}
|
||||
@ -578,11 +602,14 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
|
||||
res = calibrate_case(afl, afl->queue_top, mem, afl->queue_cycle - 1, 0);
|
||||
|
||||
if (unlikely(res == FSRV_RUN_ERROR))
|
||||
if (unlikely(res == FSRV_RUN_ERROR)) {
|
||||
|
||||
FATAL("Unable to execute target application");
|
||||
|
||||
}
|
||||
|
||||
fd = open(queue_fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (unlikely(fd < 0)) PFATAL("Unable to create '%s'", queue_fn);
|
||||
if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", queue_fn); }
|
||||
ck_write(fd, mem, len, queue_fn);
|
||||
close(fd);
|
||||
|
||||
@ -601,7 +628,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
|
||||
++afl->total_tmouts;
|
||||
|
||||
if (afl->unique_hangs >= KEEP_UNIQUE_HANG) return keeping;
|
||||
if (afl->unique_hangs >= KEEP_UNIQUE_HANG) { return keeping; }
|
||||
|
||||
if (likely(!afl->dumb_mode)) {
|
||||
|
||||
@ -611,7 +638,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
simplify_trace(afl, (u32 *)afl->fsrv.trace_bits);
|
||||
#endif /* ^WORD_SIZE_64 */
|
||||
|
||||
if (!has_new_bits(afl, afl->virgin_tmout)) return keeping;
|
||||
if (!has_new_bits(afl, afl->virgin_tmout)) { return keeping; }
|
||||
|
||||
}
|
||||
|
||||
@ -631,9 +658,13 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
timeout actually uncovers a crash. Make sure we don't discard it if
|
||||
so. */
|
||||
|
||||
if (!afl->stop_soon && new_fault == FSRV_RUN_CRASH) goto keep_as_crash;
|
||||
if (!afl->stop_soon && new_fault == FSRV_RUN_CRASH) {
|
||||
|
||||
if (afl->stop_soon || new_fault != FSRV_RUN_TMOUT) return keeping;
|
||||
goto keep_as_crash;
|
||||
|
||||
}
|
||||
|
||||
if (afl->stop_soon || new_fault != FSRV_RUN_TMOUT) { return keeping; }
|
||||
|
||||
}
|
||||
|
||||
@ -665,7 +696,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
|
||||
++afl->total_crashes;
|
||||
|
||||
if (afl->unique_crashes >= KEEP_UNIQUE_CRASH) return keeping;
|
||||
if (afl->unique_crashes >= KEEP_UNIQUE_CRASH) { return keeping; }
|
||||
|
||||
if (likely(!afl->dumb_mode)) {
|
||||
|
||||
@ -675,11 +706,11 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
simplify_trace(afl, (u32 *)afl->fsrv.trace_bits);
|
||||
#endif /* ^WORD_SIZE_64 */
|
||||
|
||||
if (!has_new_bits(afl, afl->virgin_crash)) return keeping;
|
||||
if (!has_new_bits(afl, afl->virgin_crash)) { return keeping; }
|
||||
|
||||
}
|
||||
|
||||
if (unlikely(!afl->unique_crashes)) write_crash_readme(afl);
|
||||
if (unlikely(!afl->unique_crashes)) { write_crash_readme(afl); }
|
||||
|
||||
#ifndef SIMPLE_FILES
|
||||
|
||||
@ -690,7 +721,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
#else
|
||||
|
||||
snprintf(fn, PATH_MAX, "%s/crashes/id_%06llu_%02u", afl->out_dir,
|
||||
afl->unique_crashes, afl->kill_signal);
|
||||
afl->unique_crashes, afl->last_kill_signal);
|
||||
|
||||
#endif /* ^!SIMPLE_FILES */
|
||||
|
||||
@ -715,9 +746,11 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
|
||||
break;
|
||||
|
||||
case FSRV_RUN_ERROR: FATAL("Unable to execute target application");
|
||||
case FSRV_RUN_ERROR:
|
||||
FATAL("Unable to execute target application");
|
||||
|
||||
default: return keeping;
|
||||
default:
|
||||
return keeping;
|
||||
|
||||
}
|
||||
|
||||
@ -725,7 +758,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
test case, too. */
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (unlikely(fd < 0)) PFATAL("Unable to create '%s'", fn);
|
||||
if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", fn); }
|
||||
ck_write(fd, mem, len, fn);
|
||||
close(fd);
|
||||
|
||||
|
@ -37,9 +37,12 @@ void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv) {
|
||||
|
||||
setenv("___AFL_EINS_ZWEI_POLIZEI___", "1", 1);
|
||||
|
||||
if (!fsrv->qemu_mode && argv[0] != fsrv->cmplog_binary)
|
||||
if (!fsrv->qemu_mode && argv[0] != fsrv->cmplog_binary) {
|
||||
|
||||
argv[0] = fsrv->cmplog_binary;
|
||||
|
||||
}
|
||||
|
||||
execv(argv[0], argv);
|
||||
|
||||
}
|
||||
@ -48,23 +51,11 @@ u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
|
||||
|
||||
u8 fault;
|
||||
|
||||
if (afl->post_handler) {
|
||||
|
||||
u8 *post_buf = NULL;
|
||||
|
||||
size_t post_len =
|
||||
afl->post_handler(afl->post_data, out_buf, len, &post_buf);
|
||||
if (!post_buf || !post_len) return 0;
|
||||
out_buf = post_buf;
|
||||
len = post_len;
|
||||
|
||||
}
|
||||
|
||||
write_to_testcase(afl, out_buf, len);
|
||||
|
||||
fault = fuzz_run_target(afl, &afl->cmplog_fsrv, afl->fsrv.exec_tmout);
|
||||
|
||||
if (afl->stop_soon) return 1;
|
||||
if (afl->stop_soon) { return 1; }
|
||||
|
||||
if (fault == FSRV_RUN_TMOUT) {
|
||||
|
||||
@ -75,10 +66,12 @@ u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
|
||||
|
||||
}
|
||||
|
||||
} else
|
||||
} else {
|
||||
|
||||
afl->subseq_tmouts = 0;
|
||||
|
||||
}
|
||||
|
||||
/* Users can hit us with SIGUSR1 to request the current input
|
||||
to be abandoned. */
|
||||
|
||||
|
@ -59,7 +59,7 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len,
|
||||
|
||||
f = fopen(fname, "r");
|
||||
|
||||
if (!f) PFATAL("Unable to open '%s'", fname);
|
||||
if (!f) { PFATAL("Unable to open '%s'", fname); }
|
||||
|
||||
while ((lptr = fgets(buf, MAX_LINE, f))) {
|
||||
|
||||
@ -70,57 +70,79 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len,
|
||||
|
||||
/* Trim on left and right. */
|
||||
|
||||
while (isspace(*lptr))
|
||||
while (isspace(*lptr)) {
|
||||
|
||||
++lptr;
|
||||
|
||||
}
|
||||
|
||||
rptr = lptr + strlen(lptr) - 1;
|
||||
while (rptr >= lptr && isspace(*rptr))
|
||||
while (rptr >= lptr && isspace(*rptr)) {
|
||||
|
||||
--rptr;
|
||||
|
||||
}
|
||||
|
||||
++rptr;
|
||||
*rptr = 0;
|
||||
|
||||
/* Skip empty lines and comments. */
|
||||
|
||||
if (!*lptr || *lptr == '#') continue;
|
||||
if (!*lptr || *lptr == '#') { continue; }
|
||||
|
||||
/* All other lines must end with '"', which we can consume. */
|
||||
|
||||
--rptr;
|
||||
|
||||
if (rptr < lptr || *rptr != '"')
|
||||
if (rptr < lptr || *rptr != '"') {
|
||||
|
||||
FATAL("Malformed name=\"value\" pair in line %u.", cur_line);
|
||||
|
||||
}
|
||||
|
||||
*rptr = 0;
|
||||
|
||||
/* Skip alphanumerics and dashes (label). */
|
||||
|
||||
while (isalnum(*lptr) || *lptr == '_')
|
||||
while (isalnum(*lptr) || *lptr == '_') {
|
||||
|
||||
++lptr;
|
||||
|
||||
}
|
||||
|
||||
/* If @number follows, parse that. */
|
||||
|
||||
if (*lptr == '@') {
|
||||
|
||||
++lptr;
|
||||
if (atoi(lptr) > dict_level) continue;
|
||||
while (isdigit(*lptr))
|
||||
if (atoi(lptr) > dict_level) { continue; }
|
||||
while (isdigit(*lptr)) {
|
||||
|
||||
++lptr;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Skip whitespace and = signs. */
|
||||
|
||||
while (isspace(*lptr) || *lptr == '=')
|
||||
while (isspace(*lptr) || *lptr == '=') {
|
||||
|
||||
++lptr;
|
||||
|
||||
}
|
||||
|
||||
/* Consume opening '"'. */
|
||||
|
||||
if (*lptr != '"')
|
||||
if (*lptr != '"') {
|
||||
|
||||
FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line);
|
||||
|
||||
}
|
||||
|
||||
++lptr;
|
||||
|
||||
if (!*lptr) FATAL("Empty keyword in line %u.", cur_line);
|
||||
if (!*lptr) { FATAL("Empty keyword in line %u.", cur_line); }
|
||||
|
||||
/* Okay, let's allocate memory and copy data between "...", handling
|
||||
\xNN escaping, \\, and \". */
|
||||
@ -130,7 +152,7 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len,
|
||||
|
||||
wptr = afl->extras[afl->extras_cnt].data = ck_alloc(rptr - lptr);
|
||||
|
||||
if (!wptr) PFATAL("no mem for data");
|
||||
if (!wptr) { PFATAL("no mem for data"); }
|
||||
|
||||
while (*lptr) {
|
||||
|
||||
@ -154,9 +176,12 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len,
|
||||
|
||||
}
|
||||
|
||||
if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2]))
|
||||
if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2])) {
|
||||
|
||||
FATAL("Invalid escaping (not \\xNN) in line %u.", cur_line);
|
||||
|
||||
}
|
||||
|
||||
*(wptr++) = ((strchr(hexdigits, tolower(lptr[1])) - hexdigits) << 4) |
|
||||
(strchr(hexdigits, tolower(lptr[2])) - hexdigits);
|
||||
|
||||
@ -165,7 +190,9 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len,
|
||||
|
||||
break;
|
||||
|
||||
default: *(wptr++) = *(lptr++); ++klen;
|
||||
default:
|
||||
*(wptr++) = *(lptr++);
|
||||
++klen;
|
||||
|
||||
}
|
||||
|
||||
@ -173,14 +200,17 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len,
|
||||
|
||||
afl->extras[afl->extras_cnt].len = klen;
|
||||
|
||||
if (afl->extras[afl->extras_cnt].len > MAX_DICT_FILE)
|
||||
if (afl->extras[afl->extras_cnt].len > MAX_DICT_FILE) {
|
||||
|
||||
FATAL(
|
||||
"Keyword too big in line %u (%s, limit is %s)", cur_line,
|
||||
stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), klen),
|
||||
stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE));
|
||||
|
||||
if (*min_len > klen) *min_len = klen;
|
||||
if (*max_len < klen) *max_len = klen;
|
||||
}
|
||||
|
||||
if (*min_len > klen) { *min_len = klen; }
|
||||
if (*max_len < klen) { *max_len = klen; }
|
||||
|
||||
++afl->extras_cnt;
|
||||
|
||||
@ -227,7 +257,7 @@ void load_extras(afl_state_t *afl, u8 *dir) {
|
||||
|
||||
}
|
||||
|
||||
if (x) FATAL("Dictionary levels not supported for directories.");
|
||||
if (x) { FATAL("Dictionary levels not supported for directories."); }
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
|
||||
@ -235,7 +265,11 @@ void load_extras(afl_state_t *afl, u8 *dir) {
|
||||
u8 * fn = alloc_printf("%s/%s", dir, de->d_name);
|
||||
s32 fd;
|
||||
|
||||
if (lstat(fn, &st) || access(fn, R_OK)) PFATAL("Unable to access '%s'", fn);
|
||||
if (lstat(fn, &st) || access(fn, R_OK)) {
|
||||
|
||||
PFATAL("Unable to access '%s'", fn);
|
||||
|
||||
}
|
||||
|
||||
/* This also takes care of . and .. */
|
||||
if (!S_ISREG(st.st_mode) || !st.st_size) {
|
||||
@ -245,14 +279,17 @@ void load_extras(afl_state_t *afl, u8 *dir) {
|
||||
|
||||
}
|
||||
|
||||
if (st.st_size > MAX_DICT_FILE)
|
||||
if (st.st_size > MAX_DICT_FILE) {
|
||||
|
||||
FATAL(
|
||||
"Extra '%s' is too big (%s, limit is %s)", fn,
|
||||
stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), st.st_size),
|
||||
stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE));
|
||||
|
||||
if (min_len > st.st_size) min_len = st.st_size;
|
||||
if (max_len < st.st_size) max_len = st.st_size;
|
||||
}
|
||||
|
||||
if (min_len > st.st_size) { min_len = st.st_size; }
|
||||
if (max_len < st.st_size) { max_len = st.st_size; }
|
||||
|
||||
afl->extras = ck_realloc_block(
|
||||
afl->extras, (afl->extras_cnt + 1) * sizeof(struct extra_data));
|
||||
@ -262,7 +299,7 @@ void load_extras(afl_state_t *afl, u8 *dir) {
|
||||
|
||||
fd = open(fn, O_RDONLY);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to open '%s'", fn);
|
||||
if (fd < 0) { PFATAL("Unable to open '%s'", fn); }
|
||||
|
||||
ck_read(fd, afl->extras[afl->extras_cnt].data, st.st_size, fn);
|
||||
|
||||
@ -277,7 +314,7 @@ void load_extras(afl_state_t *afl, u8 *dir) {
|
||||
|
||||
check_and_sort:
|
||||
|
||||
if (!afl->extras_cnt) FATAL("No usable files in '%s'", dir);
|
||||
if (!afl->extras_cnt) { FATAL("No usable files in '%s'", dir); }
|
||||
|
||||
qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data),
|
||||
compare_extras_len);
|
||||
@ -286,22 +323,32 @@ check_and_sort:
|
||||
stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len),
|
||||
stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len));
|
||||
|
||||
if (max_len > 32)
|
||||
if (max_len > 32) {
|
||||
|
||||
WARNF("Some tokens are relatively large (%s) - consider trimming.",
|
||||
stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), max_len));
|
||||
|
||||
if (afl->extras_cnt > MAX_DET_EXTRAS)
|
||||
}
|
||||
|
||||
if (afl->extras_cnt > MAX_DET_EXTRAS) {
|
||||
|
||||
WARNF("More than %d tokens - will use them probabilistically.",
|
||||
MAX_DET_EXTRAS);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Helper function for maybe_add_auto(afl, ) */
|
||||
|
||||
static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) {
|
||||
|
||||
while (len--)
|
||||
if (tolower(*(m1++)) ^ tolower(*(m2++))) return 1;
|
||||
while (len--) {
|
||||
|
||||
if (tolower(*(m1++)) ^ tolower(*(m2++))) { return 1; }
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
@ -318,14 +365,17 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) {
|
||||
|
||||
/* Allow users to specify that they don't want auto dictionaries. */
|
||||
|
||||
if (!MAX_AUTO_EXTRAS || !USE_AUTO_EXTRAS) return;
|
||||
if (!MAX_AUTO_EXTRAS || !USE_AUTO_EXTRAS) { return; }
|
||||
|
||||
/* Skip runs of identical bytes. */
|
||||
|
||||
for (i = 1; i < len; ++i)
|
||||
if (mem[0] ^ mem[i]) break;
|
||||
for (i = 1; i < len; ++i) {
|
||||
|
||||
if (i == len) return;
|
||||
if (mem[0] ^ mem[i]) { break; }
|
||||
|
||||
}
|
||||
|
||||
if (i == len) { return; }
|
||||
|
||||
/* Reject builtin interesting values. */
|
||||
|
||||
@ -333,33 +383,51 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) {
|
||||
|
||||
i = sizeof(interesting_16) >> 1;
|
||||
|
||||
while (i--)
|
||||
while (i--) {
|
||||
|
||||
if (*((u16 *)mem) == interesting_16[i] ||
|
||||
*((u16 *)mem) == SWAP16(interesting_16[i]))
|
||||
*((u16 *)mem) == SWAP16(interesting_16[i])) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (len == 4) {
|
||||
|
||||
i = sizeof(interesting_32) >> 2;
|
||||
|
||||
while (i--)
|
||||
while (i--) {
|
||||
|
||||
if (*((u32 *)mem) == interesting_32[i] ||
|
||||
*((u32 *)mem) == SWAP32(interesting_32[i]))
|
||||
*((u32 *)mem) == SWAP32(interesting_32[i])) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Reject anything that matches existing extras. Do a case-insensitive
|
||||
match. We optimize by exploiting the fact that extras[] are sorted
|
||||
by size. */
|
||||
|
||||
for (i = 0; i < afl->extras_cnt; ++i)
|
||||
if (afl->extras[i].len >= len) break;
|
||||
for (i = 0; i < afl->extras_cnt; ++i) {
|
||||
|
||||
for (; i < afl->extras_cnt && afl->extras[i].len == len; ++i)
|
||||
if (!memcmp_nocase(afl->extras[i].data, mem, len)) return;
|
||||
if (afl->extras[i].len >= len) { break; }
|
||||
|
||||
}
|
||||
|
||||
for (; i < afl->extras_cnt && afl->extras[i].len == len; ++i) {
|
||||
|
||||
if (!memcmp_nocase(afl->extras[i].data, mem, len)) { return; }
|
||||
|
||||
}
|
||||
|
||||
/* Last but not least, check afl->a_extras[] for matches. There are no
|
||||
guarantees of a particular sort order. */
|
||||
@ -423,7 +491,7 @@ void save_auto(afl_state_t *afl) {
|
||||
|
||||
u32 i;
|
||||
|
||||
if (!afl->auto_changed) return;
|
||||
if (!afl->auto_changed) { return; }
|
||||
afl->auto_changed = 0;
|
||||
|
||||
for (i = 0; i < MIN(USE_AUTO_EXTRAS, afl->a_extras_cnt); ++i) {
|
||||
@ -434,7 +502,7 @@ void save_auto(afl_state_t *afl) {
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fn);
|
||||
if (fd < 0) { PFATAL("Unable to create '%s'", fn); }
|
||||
|
||||
ck_write(fd, afl->a_extras[i].data, afl->a_extras[i].len, fn);
|
||||
|
||||
@ -461,7 +529,7 @@ void load_auto(afl_state_t *afl) {
|
||||
|
||||
if (fd < 0) {
|
||||
|
||||
if (errno != ENOENT) PFATAL("Unable to open '%s'", fn);
|
||||
if (errno != ENOENT) { PFATAL("Unable to open '%s'", fn); }
|
||||
ck_free(fn);
|
||||
break;
|
||||
|
||||
@ -472,21 +540,29 @@ void load_auto(afl_state_t *afl) {
|
||||
|
||||
len = read(fd, tmp, MAX_AUTO_EXTRA + 1);
|
||||
|
||||
if (len < 0) PFATAL("Unable to read from '%s'", fn);
|
||||
if (len < 0) { PFATAL("Unable to read from '%s'", fn); }
|
||||
|
||||
if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA) {
|
||||
|
||||
if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA)
|
||||
maybe_add_auto((u8 *)afl, tmp, len);
|
||||
|
||||
}
|
||||
|
||||
close(fd);
|
||||
ck_free(fn);
|
||||
|
||||
}
|
||||
|
||||
if (i)
|
||||
if (i) {
|
||||
|
||||
OKF("Loaded %u auto-discovered dictionary tokens.", i);
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
OKF("No auto-generated dictionary tokens to reuse.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Destroy extras. */
|
||||
@ -495,14 +571,20 @@ void destroy_extras(afl_state_t *afl) {
|
||||
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < afl->extras_cnt; ++i)
|
||||
for (i = 0; i < afl->extras_cnt; ++i) {
|
||||
|
||||
ck_free(afl->extras[i].data);
|
||||
|
||||
}
|
||||
|
||||
ck_free(afl->extras);
|
||||
|
||||
for (i = 0; i < afl->a_extras_cnt; ++i)
|
||||
for (i = 0; i < afl->a_extras_cnt; ++i) {
|
||||
|
||||
ck_free(afl->a_extras[i].data);
|
||||
|
||||
}
|
||||
|
||||
ck_free(afl->a_extras);
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -26,12 +26,17 @@
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
void load_custom_mutator(afl_state_t *, const char *);
|
||||
struct custom_mutator *load_custom_mutator(afl_state_t *, const char *);
|
||||
#ifdef USE_PYTHON
|
||||
struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *);
|
||||
#endif
|
||||
|
||||
void setup_custom_mutator(afl_state_t *afl) {
|
||||
void setup_custom_mutators(afl_state_t *afl) {
|
||||
|
||||
/* Try mutator library first */
|
||||
u8 *fn = afl->afl_env.afl_custom_mutator_library;
|
||||
struct custom_mutator *mutator;
|
||||
u8 * fn = afl->afl_env.afl_custom_mutator_library;
|
||||
u32 prev_mutator_count = 0;
|
||||
|
||||
if (fn) {
|
||||
|
||||
@ -39,11 +44,37 @@ void setup_custom_mutator(afl_state_t *afl) {
|
||||
FATAL(
|
||||
"MOpt and custom mutator are mutually exclusive. We accept pull "
|
||||
"requests that integrates MOpt with the optional mutators "
|
||||
"(custom/radamsa/redquenn/...).");
|
||||
"(custom/radamsa/redqueen/...).");
|
||||
|
||||
load_custom_mutator(afl, fn);
|
||||
u8 *fn_token = (u8 *)strsep((char **)&fn, ";:,");
|
||||
|
||||
return;
|
||||
if (likely(!fn_token)) {
|
||||
|
||||
mutator = load_custom_mutator(afl, fn);
|
||||
list_append(&afl->custom_mutator_list, mutator);
|
||||
afl->custom_mutators_count++;
|
||||
|
||||
} else {
|
||||
|
||||
while (fn_token) {
|
||||
|
||||
if (*fn_token) { // strsep can be empty if ";;"
|
||||
|
||||
if (afl->not_on_tty && afl->debug)
|
||||
SAYF("[Custom] Processing: %s\n", fn_token);
|
||||
prev_mutator_count = afl->custom_mutators_count;
|
||||
mutator = load_custom_mutator(afl, fn_token);
|
||||
list_append(&afl->custom_mutator_list, mutator);
|
||||
afl->custom_mutators_count++;
|
||||
if (prev_mutator_count > afl->custom_mutators_count)
|
||||
FATAL("Maximum Custom Mutator count reached.");
|
||||
fn_token = (u8 *)strsep((char **)&fn, ";:,");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -53,149 +84,164 @@ void setup_custom_mutator(afl_state_t *afl) {
|
||||
|
||||
if (module_name) {
|
||||
|
||||
if (afl->limit_time_sig)
|
||||
if (afl->limit_time_sig) {
|
||||
|
||||
FATAL(
|
||||
"MOpt and Python mutator are mutually exclusive. We accept pull "
|
||||
"requests that integrates MOpt with the optional mutators "
|
||||
"(custom/radamsa/redqueen/...).");
|
||||
|
||||
load_custom_mutator_py(afl, module_name);
|
||||
}
|
||||
|
||||
struct custom_mutator *mutator = load_custom_mutator_py(afl, module_name);
|
||||
afl->custom_mutators_count++;
|
||||
list_append(&afl->custom_mutator_list, mutator);
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (afl->afl_env.afl_python_module)
|
||||
if (afl->afl_env.afl_python_module) {
|
||||
|
||||
FATAL("Your AFL binary was built without Python support");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (afl->post_library_mutator)
|
||||
list_append(&afl->custom_mutator_list, afl->post_library_mutator);
|
||||
|
||||
}
|
||||
|
||||
void destroy_custom_mutator(afl_state_t *afl) {
|
||||
void destroy_custom_mutators(afl_state_t *afl) {
|
||||
|
||||
if (afl->mutator) {
|
||||
if (afl->custom_mutators_count) {
|
||||
|
||||
afl->mutator->afl_custom_deinit(afl->mutator->data);
|
||||
LIST_FOREACH_CLEAR(&afl->custom_mutator_list, struct custom_mutator, {
|
||||
|
||||
if (afl->mutator->dh) dlclose(afl->mutator->dh);
|
||||
if (!el->data) { FATAL("Deintializing NULL mutator"); }
|
||||
if (el->afl_custom_deinit) el->afl_custom_deinit(el->data);
|
||||
if (el->dh) dlclose(el->dh);
|
||||
|
||||
if (afl->mutator->pre_save_buf) {
|
||||
if (el->post_process_buf) {
|
||||
|
||||
ck_free(afl->mutator->pre_save_buf);
|
||||
afl->mutator->pre_save_buf = NULL;
|
||||
afl->mutator->pre_save_size = 0;
|
||||
ck_free(el->post_process_buf);
|
||||
el->post_process_buf = NULL;
|
||||
el->post_process_size = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ck_free(afl->mutator);
|
||||
afl->mutator = NULL;
|
||||
ck_free(el);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void load_custom_mutator(afl_state_t *afl, const char *fn) {
|
||||
struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) {
|
||||
|
||||
void *dh;
|
||||
afl->mutator = ck_alloc(sizeof(struct custom_mutator));
|
||||
afl->mutator->pre_save_buf = NULL;
|
||||
afl->mutator->pre_save_size = 0;
|
||||
void * dh;
|
||||
struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator));
|
||||
|
||||
afl->mutator->name = fn;
|
||||
mutator->name = fn;
|
||||
ACTF("Loading custom mutator library from '%s'...", fn);
|
||||
|
||||
dh = dlopen(fn, RTLD_NOW);
|
||||
if (!dh) FATAL("%s", dlerror());
|
||||
afl->mutator->dh = dh;
|
||||
mutator->dh = dh;
|
||||
|
||||
/* Mutator */
|
||||
/* "afl_custom_init", required */
|
||||
afl->mutator->afl_custom_init = dlsym(dh, "afl_custom_init");
|
||||
if (!afl->mutator->afl_custom_init)
|
||||
FATAL("Symbol 'afl_custom_init' not found.");
|
||||
|
||||
/* "afl_custom_deinit", required */
|
||||
afl->mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit");
|
||||
if (!afl->mutator->afl_custom_deinit)
|
||||
FATAL("Symbol 'afl_custom_deinit' not found.");
|
||||
/* "afl_custom_init", optional for backward compatibility */
|
||||
mutator->afl_custom_init = dlsym(dh, "afl_custom_init");
|
||||
if (!mutator->afl_custom_init) FATAL("Symbol 'afl_custom_init' not found.");
|
||||
|
||||
/* "afl_custom_fuzz" or "afl_custom_mutator", required */
|
||||
afl->mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz");
|
||||
if (!afl->mutator->afl_custom_fuzz) {
|
||||
mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz");
|
||||
if (!mutator->afl_custom_fuzz) {
|
||||
|
||||
/* Try "afl_custom_mutator" for backward compatibility */
|
||||
WARNF("Symbol 'afl_custom_fuzz' not found. Try 'afl_custom_mutator'.");
|
||||
|
||||
afl->mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator");
|
||||
if (!afl->mutator->afl_custom_fuzz)
|
||||
FATAL("Symbol 'afl_custom_mutator' not found.");
|
||||
mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator");
|
||||
if (!mutator->afl_custom_fuzz)
|
||||
WARNF("Symbol 'afl_custom_mutator' not found.");
|
||||
|
||||
}
|
||||
|
||||
/* "afl_custom_pre_save", optional */
|
||||
afl->mutator->afl_custom_pre_save = dlsym(dh, "afl_custom_pre_save");
|
||||
if (!afl->mutator->afl_custom_pre_save)
|
||||
WARNF("Symbol 'afl_custom_pre_save' not found.");
|
||||
/* "afl_custom_deinit", optional for backward compatibility */
|
||||
mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit");
|
||||
if (!mutator->afl_custom_deinit) FATAL("Symbol 'afl_custom_init' not found.");
|
||||
|
||||
/* "afl_custom_post_process", optional */
|
||||
mutator->afl_custom_post_process = dlsym(dh, "afl_custom_post_process");
|
||||
if (!mutator->afl_custom_post_process)
|
||||
ACTF("optional symbol 'afl_custom_post_process' not found.");
|
||||
|
||||
u8 notrim = 0;
|
||||
/* "afl_custom_init_trim", optional */
|
||||
afl->mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim");
|
||||
if (!afl->mutator->afl_custom_init_trim)
|
||||
WARNF("Symbol 'afl_custom_init_trim' not found.");
|
||||
mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim");
|
||||
if (!mutator->afl_custom_init_trim)
|
||||
ACTF("optional symbol 'afl_custom_init_trim' not found.");
|
||||
|
||||
/* "afl_custom_trim", optional */
|
||||
afl->mutator->afl_custom_trim = dlsym(dh, "afl_custom_trim");
|
||||
if (!afl->mutator->afl_custom_trim)
|
||||
WARNF("Symbol 'afl_custom_trim' not found.");
|
||||
mutator->afl_custom_trim = dlsym(dh, "afl_custom_trim");
|
||||
if (!mutator->afl_custom_trim)
|
||||
ACTF("optional symbol 'afl_custom_trim' not found.");
|
||||
|
||||
/* "afl_custom_post_trim", optional */
|
||||
afl->mutator->afl_custom_post_trim = dlsym(dh, "afl_custom_post_trim");
|
||||
if (!afl->mutator->afl_custom_post_trim)
|
||||
WARNF("Symbol 'afl_custom_post_trim' not found.");
|
||||
mutator->afl_custom_post_trim = dlsym(dh, "afl_custom_post_trim");
|
||||
if (!mutator->afl_custom_post_trim)
|
||||
ACTF("optional symbol 'afl_custom_post_trim' not found.");
|
||||
|
||||
if (notrim) {
|
||||
|
||||
afl->mutator->afl_custom_init_trim = NULL;
|
||||
afl->mutator->afl_custom_trim = NULL;
|
||||
afl->mutator->afl_custom_post_trim = NULL;
|
||||
WARNF(
|
||||
mutator->afl_custom_init_trim = NULL;
|
||||
mutator->afl_custom_trim = NULL;
|
||||
mutator->afl_custom_post_trim = NULL;
|
||||
ACTF(
|
||||
"Custom mutator does not implement all three trim APIs, standard "
|
||||
"trimming will be used.");
|
||||
|
||||
}
|
||||
|
||||
/* "afl_custom_havoc_mutation", optional */
|
||||
afl->mutator->afl_custom_havoc_mutation =
|
||||
dlsym(dh, "afl_custom_havoc_mutation");
|
||||
if (!afl->mutator->afl_custom_havoc_mutation)
|
||||
WARNF("Symbol 'afl_custom_havoc_mutation' not found.");
|
||||
mutator->afl_custom_havoc_mutation = dlsym(dh, "afl_custom_havoc_mutation");
|
||||
if (!mutator->afl_custom_havoc_mutation)
|
||||
ACTF("optional symbol 'afl_custom_havoc_mutation' not found.");
|
||||
|
||||
/* "afl_custom_havoc_mutation", optional */
|
||||
afl->mutator->afl_custom_havoc_mutation_probability =
|
||||
mutator->afl_custom_havoc_mutation_probability =
|
||||
dlsym(dh, "afl_custom_havoc_mutation_probability");
|
||||
if (!afl->mutator->afl_custom_havoc_mutation_probability)
|
||||
WARNF("Symbol 'afl_custom_havoc_mutation_probability' not found.");
|
||||
if (!mutator->afl_custom_havoc_mutation_probability)
|
||||
ACTF("optional symbol 'afl_custom_havoc_mutation_probability' not found.");
|
||||
|
||||
/* "afl_custom_queue_get", optional */
|
||||
afl->mutator->afl_custom_queue_get = dlsym(dh, "afl_custom_queue_get");
|
||||
if (!afl->mutator->afl_custom_queue_get)
|
||||
WARNF("Symbol 'afl_custom_queue_get' not found.");
|
||||
mutator->afl_custom_queue_get = dlsym(dh, "afl_custom_queue_get");
|
||||
if (!mutator->afl_custom_queue_get)
|
||||
ACTF("optional symbol 'afl_custom_queue_get' not found.");
|
||||
|
||||
/* "afl_custom_queue_new_entry", optional */
|
||||
afl->mutator->afl_custom_queue_new_entry =
|
||||
dlsym(dh, "afl_custom_queue_new_entry");
|
||||
if (!afl->mutator->afl_custom_queue_new_entry)
|
||||
WARNF("Symbol 'afl_custom_queue_new_entry' not found");
|
||||
mutator->afl_custom_queue_new_entry = dlsym(dh, "afl_custom_queue_new_entry");
|
||||
if (!mutator->afl_custom_queue_new_entry)
|
||||
ACTF("optional symbol 'afl_custom_queue_new_entry' not found");
|
||||
|
||||
OKF("Custom mutator '%s' installed successfully.", fn);
|
||||
|
||||
/* Initialize the custom mutator */
|
||||
if (afl->mutator->afl_custom_init)
|
||||
afl->mutator->data =
|
||||
afl->mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF));
|
||||
if (mutator->afl_custom_init)
|
||||
mutator->data = mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF));
|
||||
|
||||
mutator->stacked_custom = (mutator && mutator->afl_custom_havoc_mutation);
|
||||
mutator->stacked_custom_prob =
|
||||
6; // like one of the default mutations in havoc
|
||||
|
||||
return mutator;
|
||||
|
||||
}
|
||||
|
||||
u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
|
||||
u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf,
|
||||
struct custom_mutator *mutator) {
|
||||
|
||||
u8 needs_write = 0, fault = 0;
|
||||
u32 trim_exec = 0;
|
||||
@ -208,14 +254,20 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
|
||||
|
||||
/* Initialize trimming in the custom mutator */
|
||||
afl->stage_cur = 0;
|
||||
afl->stage_max =
|
||||
afl->mutator->afl_custom_init_trim(afl->mutator->data, in_buf, q->len);
|
||||
if (unlikely(afl->stage_max) < 0)
|
||||
afl->stage_max = mutator->afl_custom_init_trim(mutator->data, in_buf, q->len);
|
||||
if (unlikely(afl->stage_max) < 0) {
|
||||
|
||||
FATAL("custom_init_trim error ret: %d", afl->stage_max);
|
||||
if (afl->not_on_tty && afl->debug)
|
||||
|
||||
}
|
||||
|
||||
if (afl->not_on_tty && afl->debug) {
|
||||
|
||||
SAYF("[Custom Trimming] START: Max %d iterations, %u bytes", afl->stage_max,
|
||||
q->len);
|
||||
|
||||
}
|
||||
|
||||
while (afl->stage_cur < afl->stage_max) {
|
||||
|
||||
u8 *retbuf = NULL;
|
||||
@ -225,15 +277,20 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
|
||||
|
||||
u32 cksum;
|
||||
|
||||
size_t retlen = afl->mutator->afl_custom_trim(afl->mutator->data, &retbuf);
|
||||
size_t retlen = mutator->afl_custom_trim(mutator->data, &retbuf);
|
||||
|
||||
if (unlikely(!retbuf)) {
|
||||
|
||||
if (unlikely(!retbuf))
|
||||
FATAL("custom_trim failed (ret %zd)", retlen);
|
||||
else if (unlikely(retlen > orig_len))
|
||||
|
||||
} else if (unlikely(retlen > orig_len)) {
|
||||
|
||||
FATAL(
|
||||
"Trimmed data returned by custom mutator is larger than original "
|
||||
"data");
|
||||
|
||||
}
|
||||
|
||||
write_to_testcase(afl, retbuf, retlen);
|
||||
|
||||
fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
|
||||
@ -260,35 +317,46 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
|
||||
}
|
||||
|
||||
/* Tell the custom mutator that the trimming was successful */
|
||||
afl->stage_cur =
|
||||
afl->mutator->afl_custom_post_trim(afl->mutator->data, 1);
|
||||
afl->stage_cur = mutator->afl_custom_post_trim(mutator->data, 1);
|
||||
|
||||
if (afl->not_on_tty && afl->debug) {
|
||||
|
||||
if (afl->not_on_tty && afl->debug)
|
||||
SAYF("[Custom Trimming] SUCCESS: %d/%d iterations (now at %u bytes)",
|
||||
afl->stage_cur, afl->stage_max, q->len);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* Tell the custom mutator that the trimming was unsuccessful */
|
||||
afl->stage_cur =
|
||||
afl->mutator->afl_custom_post_trim(afl->mutator->data, 0);
|
||||
if (unlikely(afl->stage_cur < 0))
|
||||
afl->stage_cur = mutator->afl_custom_post_trim(mutator->data, 0);
|
||||
if (unlikely(afl->stage_cur < 0)) {
|
||||
|
||||
FATAL("Error ret in custom_post_trim: %d", afl->stage_cur);
|
||||
if (afl->not_on_tty && afl->debug)
|
||||
|
||||
}
|
||||
|
||||
if (afl->not_on_tty && afl->debug) {
|
||||
|
||||
SAYF("[Custom Trimming] FAILURE: %d/%d iterations", afl->stage_cur,
|
||||
afl->stage_max);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Since this can be slow, update the screen every now and then. */
|
||||
|
||||
if (!(trim_exec++ % afl->stats_update_freq)) show_stats(afl);
|
||||
if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); }
|
||||
|
||||
}
|
||||
|
||||
if (afl->not_on_tty && afl->debug)
|
||||
if (afl->not_on_tty && afl->debug) {
|
||||
|
||||
SAYF("[Custom Trimming] DONE: %u bytes -> %u bytes", orig_len, q->len);
|
||||
|
||||
}
|
||||
|
||||
/* If we have made changes to in_buf, we also need to update the on-disk
|
||||
version of the test case. */
|
||||
|
||||
@ -300,7 +368,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
|
||||
|
||||
fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", q->fname);
|
||||
if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); }
|
||||
|
||||
ck_write(fd, in_buf, q->len, q->fname);
|
||||
close(fd);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user