mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-24 14:43:22 +00:00
Compare commits
498 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 | |||
ddea300822 | |||
9900c92ebc | |||
0bd1264faf | |||
49753eb2d0 | |||
c9605f2251 | |||
6ee11c2a6f | |||
ef1d384184 | |||
a6d4f04019 | |||
87d27b8616 | |||
f22d8120ef | |||
8ecfbcdf34 | |||
263daaacaf | |||
d3254d8066 | |||
76e15a0695 | |||
1931838a11 | |||
6bd49b1d5c | |||
2d4d16755c | |||
bfcf6db17a | |||
bda4d8812e | |||
f70d4a6fd4 | |||
90ff345d73 | |||
8fa5d4c313 | |||
2162fd8e1a | |||
248a2f2f0b | |||
ef311ec70c | |||
5b70d23211 | |||
16ce555845 | |||
c961925356 | |||
23ea727915 | |||
f157bca548 | |||
35937e6263 | |||
69bd7c16eb | |||
6940e13629 | |||
380ff114e9 | |||
5e53002303 | |||
872d1c1d98 | |||
94187837c7 | |||
ede3545d8b | |||
cdac882834 | |||
1ee224652c | |||
8511638afb | |||
b10007a7b5 | |||
19ce862810 | |||
124665b392 | |||
b420ccdbf8 | |||
5e8f385705 | |||
dcc889a264 | |||
f3789801f2 | |||
0f08b13fa0 | |||
2ce6e7a7fb | |||
83d5864203 | |||
7e2c52f2b9 | |||
e88f364282 | |||
21f696f02e | |||
ef1ea07e68 | |||
0c02a8f4d3 | |||
48f7f7a17b | |||
115ee8bad5 | |||
c009896c34 | |||
6dc36f1e6e | |||
f4436f118c | |||
8440aaea5a | |||
0d0338012b | |||
5ac1b6e940 | |||
92aaaef381 | |||
2c87e68ad6 | |||
26e690c220 | |||
b63dd7a464 | |||
d2a7628748 | |||
b1e7b834ac | |||
9eb47a924a | |||
1d62bf1c37 | |||
982d46e7cb | |||
1fbface656 | |||
1374e65401 | |||
0fab4e1955 | |||
a2574169e2 | |||
4b88e059ef | |||
12a7059ae8 | |||
a897f355a9 | |||
326ab632c3 | |||
dda096da03 | |||
5daec436f9 | |||
033c743a41 | |||
459d8f9ba2 | |||
0022cc4782 | |||
995e556065 | |||
a93268acec | |||
e70c54547e | |||
7919545499 | |||
5a8db5954c | |||
ee4e1936d0 | |||
ec67780854 | |||
aaf8835cdb | |||
0a525f768b | |||
4ed43b7632 | |||
5e09e13ece | |||
eec725a345 | |||
fd63344ffc | |||
90a9f22c3d | |||
cc3ac932d9 | |||
d8947d1fa4 | |||
32ba60185e | |||
8bcfba8231 | |||
68f269437d | |||
29ee3a1ffc | |||
39e8b91806 | |||
3ab7fcf5dd | |||
3a509c6168 | |||
6dcbc4dff4 | |||
d928b148d8 | |||
3209a9d4e8 | |||
f0f83bab52 | |||
6aa6af04ac | |||
5b977453cb | |||
a60e425d39 | |||
4e3739cdf2 | |||
e51b4700e2 | |||
0b9f7c4c89 | |||
9dcdbbb649 | |||
ac2f0c9896 | |||
fbf5e08425 | |||
df5c264754 | |||
66f535ad61 | |||
b8d84ababb | |||
b485b7a252 | |||
c1395bb543 | |||
0c2e998f69 | |||
504529c3aa | |||
1fab45024b | |||
e354235ebb | |||
d55196352c | |||
45e569845e | |||
b55421d4a1 | |||
f06acc4e27 | |||
3c546341e3 | |||
650bd1c179 | |||
9efa96fe0c | |||
361c2ac576 | |||
54359be31a | |||
77949acaee | |||
88a852ef0f | |||
cb52b1757d | |||
b4869d0663 | |||
30fa7b8006 | |||
7793ca4055 | |||
3353a23fcb | |||
b32b9bd1d5 | |||
133a8cea1c | |||
729dbf7dda | |||
67e9ef43a7 | |||
1b5b54fe94 | |||
9b3e6112ff | |||
7663e7dd05 | |||
2e29e64a3f | |||
15e0319c98 | |||
055ca98fb2 | |||
968295ed81 | |||
48e366d7a8 | |||
9aa10ef7ca | |||
a85b8d69ef | |||
2900d51e5d | |||
63cc113f12 | |||
3bf88da091 | |||
11e3122843 | |||
dbe6b1da44 | |||
91dc7776ec | |||
a5036499d2 | |||
9ef4b45609 | |||
5b160f187d | |||
604f17b897 | |||
da1f3bf41e | |||
452acf3a75 | |||
5ad50adaa8 | |||
c165165b58 | |||
0303d315fc | |||
2129257454 | |||
a7e9ce2e33 | |||
b9851cdabe | |||
a9261c6d64 | |||
064131887b | |||
6b5d5b11b0 | |||
3f2859ec16 | |||
5340f2f2eb | |||
8610b0e406 | |||
ffb4767fc1 | |||
97cae2df99 | |||
dc0369eb10 | |||
71f0bd003f | |||
cc65e91eeb | |||
2ae0208d3b | |||
7114663f52 | |||
26e45e41ed | |||
38b14c5c44 | |||
0c76d7e621 | |||
c69c84da09 | |||
0565fe4213 | |||
cb0bc98576 | |||
ef4eeede86 | |||
1cce581ffe | |||
989a85bb05 | |||
db3645c76b | |||
b5c5496b2f | |||
4aec6dabde | |||
0fac7bd373 | |||
9c1c1062be | |||
36a03e3bc8 | |||
dfb0a65e07 | |||
48655c2e12 |
@ -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 \
|
||||
|
201
GNUmakefile
201
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
|
||||
@ -69,17 +85,27 @@ ifneq "$(shell uname -m)" "x86_64"
|
||||
endif
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT)
|
||||
override CFLAGS += -Wall -g -Wno-pointer-sign \
|
||||
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
|
||||
@ -145,18 +197,23 @@ else
|
||||
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
|
||||
ifdef NO_PYTHON
|
||||
PYTHON_OK=0
|
||||
PYFLAGS=
|
||||
endif
|
||||
|
||||
ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
|
||||
ASAN_LDFLAGS+=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
|
||||
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
|
||||
|
||||
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)
|
||||
@ -164,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
|
||||
@ -199,6 +250,7 @@ performance-test: source-only
|
||||
@cd test ; ./test-performance.sh
|
||||
|
||||
|
||||
# hint: make targets are also listed in the top level README.md
|
||||
help:
|
||||
@echo "HELP --- the following make targets exist:"
|
||||
@echo "=========================================="
|
||||
@ -208,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 "=========================================="
|
||||
@ -266,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
|
||||
|
||||
@ -298,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!)
|
||||
@ -318,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
|
||||
@ -359,6 +421,7 @@ code-format:
|
||||
./.custom-format.py -i gcc_plugin/*.cc
|
||||
./.custom-format.py -i examples/*/*.c
|
||||
./.custom-format.py -i examples/*/*.h
|
||||
./.custom-format.py -i test/*.c
|
||||
./.custom-format.py -i qemu_mode/patches/*.h
|
||||
./.custom-format.py -i qemu_mode/libcompcov/*.c
|
||||
./.custom-format.py -i qemu_mode/libcompcov/*.cc
|
||||
@ -394,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
|
||||
@ -423,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
|
||||
@ -433,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++" > $@
|
||||
@ -468,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
|
||||
|
31
README.md
31
README.md
@ -4,9 +4,9 @@
|
||||
|
||||

|
||||
|
||||
Release Version: [2.63c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
Release Version: [2.65c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
|
||||
Github Version: 2.63d
|
||||
Github Version: 2.65d
|
||||
|
||||
includes all necessary/interesting changes from Google's afl 2.56b
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
* 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)
|
||||
it is unlikely to receive any notable enhancements: [https://twitter.com/Dor3s/status/1154737061787660288](https://twitter.com/Dor3s/status/1154737061787660288)
|
||||
|
||||
## The enhancements compared to the original stock afl
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
* Custom mutator by a library (instead of Python) by kyakdan
|
||||
|
||||
* Unicron mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk)
|
||||
* 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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -124,9 +124,9 @@ However, we already work on so many things that we do not have the time for
|
||||
all the big ideas.
|
||||
|
||||
This can be your way to support and contribute to AFL++ - extend it to
|
||||
something cool
|
||||
something cool.
|
||||
|
||||
We have an idea list in [docs/ideas.md](docs/ideas.md)
|
||||
We have an idea list in [docs/ideas.md](docs/ideas.md).
|
||||
|
||||
For everyone who wants to contribute (and send pull requests) please read
|
||||
[CONTRIBUTING.md](CONTRIBUTING.md) before your submit.
|
||||
@ -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
|
||||
```
|
||||
@ -145,7 +145,7 @@ $ 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
|
||||
hence in this case
|
||||
|
||||
```shell
|
||||
$ make source-only
|
||||
@ -158,8 +158,10 @@ These build targets exist:
|
||||
* 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)
|
||||
* 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)
|
||||
@ -177,6 +179,7 @@ These build options exist:
|
||||
* STATIC - compile AFL++ static
|
||||
* ASAN_BUILD - compiles with memory sanitizer for debug purposes
|
||||
* PROFILING - compile with profiling information (gprof)
|
||||
* NO_PYTHON - disable python support
|
||||
* AFL_NO_X86 - if compiling on non-intel/amd platforms
|
||||
* LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian)
|
||||
|
||||
@ -184,7 +187,7 @@ e.g.: make ASAN_BUILD=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.
|
||||
Hence at least 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
|
||||
@ -350,6 +353,7 @@ Here are some good writeups to show how to effectively use AFL++:
|
||||
* [https://aflplus.plus/docs/tutorials/libxml2_tutorial/](https://aflplus.plus/docs/tutorials/libxml2_tutorial/)
|
||||
* [https://bananamafia.dev/post/gb-fuzz/](https://bananamafia.dev/post/gb-fuzz/)
|
||||
* [https://securitylab.github.com/research/fuzzing-challenges-solutions-1](https://securitylab.github.com/research/fuzzing-challenges-solutions-1)
|
||||
* [https://securitylab.github.com/research/fuzzing-sockets-FTP](https://securitylab.github.com/research/fuzzing-sockets-FTP)
|
||||
|
||||
If you are interested in fuzzing structured data (where you define what the
|
||||
structure is), these two links have you covered:
|
||||
@ -668,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)
|
||||
|
28
TODO.md
28
TODO.md
@ -1,29 +1,22 @@
|
||||
# TODO list for AFL++
|
||||
|
||||
## Roadmap 2.63
|
||||
## Roadmap 2.65+
|
||||
|
||||
- complete custom_mutator API changes and documentation
|
||||
- fix stability calculation bug
|
||||
|
||||
## Roadmap 2.64
|
||||
|
||||
- context sensitive branch coverage in llvm_mode
|
||||
- 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?
|
||||
- laf-intel build auto-dictionary?
|
||||
- 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:
|
||||
@ -31,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,75 @@ 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! :)
|
||||
- autodictionary feature added, enable with `AFL_LLVM_LTO_AUTODICTIONARY`
|
||||
- variable map size usage
|
||||
- afl-fuzz:
|
||||
- variable map size support added (only LTO mode can use this)
|
||||
- snapshot feature usage now visible in UI
|
||||
- Now setting `-L -1` will enable MOpt in parallel to normal mutation.
|
||||
Additionally, this allows to run dictionaries, radamsa and cmplog.
|
||||
- fix for cmplog/redqueen mode if stdin was used
|
||||
- fix for writing a better plot_data file
|
||||
- qemu_mode: fix for persistent mode (which would not terminate or get stuck)
|
||||
- compare-transform/AFL_LLVM_LAF_TRANSFORM_COMPARES now transforms also
|
||||
static global and local variable comparisons (cannot find all though)
|
||||
- extended forkserver: map_size and more information is communicated to
|
||||
afl-fuzz (and afl-fuzz acts accordingly)
|
||||
- new environment variable: AFL_MAP_SIZE to specify the size of the shared map
|
||||
- if AFL_CC/AFL_CXX is set but empty afl compilers did fail, fixed
|
||||
(this bug is in vanilla afl too)
|
||||
- added NO_PYTHON flag to disable python support when building afl-fuzz
|
||||
- more refactoring
|
||||
|
||||
|
||||
### Version ++2.63c (release):
|
||||
|
||||
! the repository was moved from vanhauser-thc to AFLplusplus. It is now
|
||||
@ -41,7 +110,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
easier: DEFAULT, CFG (INSTRIM), LTO, CTX, NGRAM-x (x=2-16)
|
||||
- made USE_TRACE_PC compile obsolete
|
||||
- LTO collision free instrumented added in llvm_mode with afl-clang-lto -
|
||||
note that this mode is amazing, but quite some targets won't compile
|
||||
this mode is amazing but requires you to build llvm 11 yourself
|
||||
- Added llvm_mode NGRAM prev_loc coverage by Adrean Herrera
|
||||
(https://github.com/adrianherrera/afl-ngram-pass/), activate by setting
|
||||
AFL_LLVM_INSTRUMENT=NGRAM-<value> or AFL_LLVM_NGRAM_SIZE=<value>
|
||||
@ -117,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
|
||||
@ -294,7 +363,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
you use the new -p option :-) - see docs/power_schedules.md
|
||||
- added afl-system-config script to set all system performance options for fuzzing
|
||||
- llvm_mode works with llvm 3.9 up to including 8 !
|
||||
- qemu_mode got upgraded from 2.1 to 3.1 - incorporated from
|
||||
- qemu_mode got upgraded from 2.1 to 3.1 - incorporated from
|
||||
https://github.com/andreafioraldi/afl and with community patches added
|
||||
|
||||
|
||||
@ -2508,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.
|
||||
|
@ -36,6 +36,9 @@ enter the pacemaker fuzzing mode.
|
||||
Setting 0 will enter the pacemaker fuzzing mode at first, which is
|
||||
recommended in a short time-scale evaluation.
|
||||
|
||||
Setting -1 will enable both pacemaker mode and normal aflmutation fuzzing in
|
||||
parallel.
|
||||
|
||||
Other important parameters can be found in afl-fuzz.c, for instance,
|
||||
|
||||
'swarm_num': the number of the PSO swarms used in the fuzzing process.
|
||||
|
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
|
||||
|
||||
@ -111,13 +118,25 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
instrumentation which is 100% collision free (collisions are a big issue
|
||||
in afl and afl-like instrumentations). This is performed by using
|
||||
afl-clang-lto/afl-clang-lto++ instead of afl-clang-fast, but is only
|
||||
built if LLVM 9 or newer is used.
|
||||
built if LLVM 11 or newer is used.
|
||||
|
||||
None of these options are necessary to be used and are rather for manual
|
||||
use (which only ever the author of this LTO implementation will use ;-)
|
||||
- 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
|
||||
fuzz.
|
||||
|
||||
None of the following options are necessary to be used and are rather for
|
||||
manual use (which only ever the author of this LTO implementation will use).
|
||||
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
|
||||
@ -127,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
|
||||
|
||||
@ -135,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
|
||||
@ -195,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
|
||||
@ -238,6 +263,11 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
normally indicated by the cycle counter in the UI turning green. May be
|
||||
convenient for some types of automated jobs.
|
||||
|
||||
- AFL_MAP_SIZE sets the size of the shared map that afl-fuzz, afl-showmap,
|
||||
afl-tmin and afl-analyze create to gather instrumentation data from
|
||||
the target. This must be equal or larger than the size the target was
|
||||
compiled with.
|
||||
|
||||
- Setting AFL_NO_AFFINITY disables attempts to bind to a specific CPU core
|
||||
on Linux systems. This slows things down, but lets you run more instances
|
||||
of afl-fuzz than would be prudent (if you really want to).
|
||||
@ -280,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
|
@ -31,8 +31,10 @@ LDFLAGS += $(LDFLAGS_ADD)
|
||||
M32FLAG = -m32
|
||||
M64FLAG = -m64
|
||||
|
||||
CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep gcc; echo $$?)
|
||||
CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep arm; echo $$?)
|
||||
CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?)
|
||||
CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$?
|
||||
CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?)
|
||||
CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?
|
||||
|
||||
_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)
|
||||
__M32FLAG=$(_M32FLAG:00=-mbe32)
|
||||
@ -42,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)/
|
||||
|
@ -91,10 +91,10 @@ for crash in $DIR/crashes/id:*; do
|
||||
for a in $@; do
|
||||
|
||||
if [ "$a" = "@@" ] ; then
|
||||
args="$use_args $crash"
|
||||
use_args="$use_args $crash"
|
||||
unset use_stdio
|
||||
else
|
||||
args="$use_args $a"
|
||||
use_args="$use_args $a"
|
||||
fi
|
||||
|
||||
done
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,10 @@ LDFLAGS += $(LDFLAGS_ADD)
|
||||
M32FLAG = -m32
|
||||
M64FLAG = -m64
|
||||
|
||||
CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep gcc; echo $$?)
|
||||
CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep arm; echo $$?)
|
||||
CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?)
|
||||
CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$?
|
||||
CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?)
|
||||
CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?
|
||||
|
||||
_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)
|
||||
__M32FLAG=$(_M32FLAG:00=-mbe32)
|
||||
@ -45,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
|
||||
|
||||
|
@ -121,12 +121,18 @@ static void edit_params(u32 argc, char **argv) {
|
||||
if (!strcmp(name, "afl-g++-fast")) {
|
||||
|
||||
u8 *alt_cxx = getenv("AFL_CXX");
|
||||
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)AFL_GCC_CXX;
|
||||
cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)AFL_GCC_CXX;
|
||||
|
||||
} 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 {
|
||||
|
||||
u8 *alt_cc = getenv("AFL_CC");
|
||||
cc_params[0] = alt_cc ? alt_cc : (u8 *)AFL_GCC_CC;
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
@ -364,6 +370,16 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
u8 *ptr;
|
||||
if (!be_quiet &&
|
||||
((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE")))) {
|
||||
|
||||
u32 map_size = atoi(ptr);
|
||||
if (map_size != MAP_SIZE)
|
||||
FATAL("AFL_MAP_SIZE is not supported by afl-gcc-fast");
|
||||
|
||||
}
|
||||
|
||||
check_environment_vars(envp);
|
||||
|
||||
find_obj(argv[0]);
|
||||
|
@ -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>
|
||||
@ -138,8 +138,9 @@ static void __afl_map_shm(void) {
|
||||
|
||||
static void __afl_start_forkserver(void) {
|
||||
|
||||
static u8 tmp[4];
|
||||
s32 child_pid;
|
||||
u8 tmp[4] = {0, 0, 0, 0};
|
||||
u32 map_size = MAP_SIZE;
|
||||
s32 child_pid;
|
||||
|
||||
u8 child_stopped = 0;
|
||||
|
||||
@ -148,6 +149,13 @@ static void __afl_start_forkserver(void) {
|
||||
/* 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 (MAP_SIZE <= 0x800000) {
|
||||
|
||||
map_size = (FS_OPT_ENABLED | FS_OPT_MAPSIZE | FS_OPT_SET_MAPSIZE(MAP_SIZE));
|
||||
memcpy(tmp, &map_size, 4);
|
||||
|
||||
}
|
||||
|
||||
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
|
||||
|
||||
while (1) {
|
||||
|
@ -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 */
|
||||
@ -195,19 +197,6 @@ enum {
|
||||
|
||||
};
|
||||
|
||||
/* Execution status fault codes */
|
||||
|
||||
enum {
|
||||
|
||||
/* 00 */ FAULT_NONE,
|
||||
/* 01 */ FAULT_TMOUT,
|
||||
/* 02 */ FAULT_CRASH,
|
||||
/* 03 */ FAULT_ERROR,
|
||||
/* 04 */ FAULT_NOINST,
|
||||
/* 05 */ FAULT_NOBITS
|
||||
|
||||
};
|
||||
|
||||
#define operator_num 16
|
||||
#define swarm_num 5
|
||||
#define period_core 500000
|
||||
@ -242,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,
|
||||
@ -294,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;
|
||||
@ -331,14 +320,21 @@ typedef struct afl_env_vars {
|
||||
u8 afl_skip_cpufreq, afl_exit_when_done, afl_no_affinity, afl_skip_bin_check,
|
||||
afl_dumb_forksrv, afl_import_first, afl_custom_mutator_only, afl_no_ui,
|
||||
afl_force_ui, afl_i_dont_care_about_missing_crashes, afl_bench_just_one,
|
||||
afl_bench_until_crash, afl_debug_child_output, afl_autoresume;
|
||||
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;
|
||||
|
||||
struct afl_pass_stat {
|
||||
|
||||
u8 total;
|
||||
u8 faileds;
|
||||
|
||||
};
|
||||
|
||||
typedef struct afl_state {
|
||||
|
||||
/* Position of this state in the global states list */
|
||||
@ -353,14 +349,14 @@ typedef struct afl_state {
|
||||
/* MOpt:
|
||||
Lots of globals, but mostly for the status UI and other things where it
|
||||
really makes no sense to haul them around as function parameters. */
|
||||
u64 limit_time_puppet, orig_hit_cnt_puppet, last_limit_time_start,
|
||||
tmp_pilot_time, total_pacemaker_time, total_puppet_find, temp_puppet_find,
|
||||
most_time_key, most_time, most_execs_key, most_execs, old_hit_count,
|
||||
force_ui_update;
|
||||
u64 orig_hit_cnt_puppet, last_limit_time_start, tmp_pilot_time,
|
||||
total_pacemaker_time, total_puppet_find, temp_puppet_find, most_time_key,
|
||||
most_time, most_execs_key, most_execs, old_hit_count, force_ui_update;
|
||||
|
||||
MOpt_globals_t mopt_globals_core, mopt_globals_pilot;
|
||||
|
||||
s32 SPLICE_CYCLES_puppet, limit_time_sig, key_puppet, key_module;
|
||||
s32 limit_time_puppet, SPLICE_CYCLES_puppet, limit_time_sig, key_puppet,
|
||||
key_module;
|
||||
|
||||
double w_init, w_end, w_now;
|
||||
|
||||
@ -410,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) */
|
||||
|
||||
@ -421,11 +419,9 @@ 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? */
|
||||
kill_signal, /* Signal that killed the child */
|
||||
resuming_fuzz, /* Resuming an older fuzzing job? */
|
||||
timeout_given, /* Specific timeout given? */
|
||||
not_on_tty, /* stdout is not a tty */
|
||||
@ -439,7 +435,6 @@ typedef struct afl_state {
|
||||
no_arith, /* Skip most arithmetic ops */
|
||||
shuffle_queue, /* Shuffle input queue? */
|
||||
bitmap_changed, /* Time to update bitmap? */
|
||||
qemu_mode, /* Running in QEMU mode? */
|
||||
unicorn_mode, /* Running in Unicorn mode? */
|
||||
use_wine, /* Use WINE with QEMU mode */
|
||||
skip_requested, /* Skip request, via SIGUSR1 */
|
||||
@ -450,11 +445,11 @@ typedef struct afl_state {
|
||||
fast_cal, /* Try to calibrate faster? */
|
||||
disable_trim; /* Never trim in fuzz_one */
|
||||
|
||||
u8 virgin_bits[MAP_SIZE], /* Regions yet untouched by fuzzing */
|
||||
virgin_tmout[MAP_SIZE], /* Bits we haven't seen in tmouts */
|
||||
virgin_crash[MAP_SIZE]; /* Bits we haven't seen in crashes */
|
||||
u8 *virgin_bits, /* Regions yet untouched by fuzzing */
|
||||
*virgin_tmout, /* Bits we haven't seen in tmouts */
|
||||
*virgin_crash; /* Bits we haven't seen in crashes */
|
||||
|
||||
u8 var_bytes[MAP_SIZE]; /* Bytes that appear to be variable */
|
||||
u8 *var_bytes; /* Bytes that appear to be variable */
|
||||
|
||||
volatile u8 stop_soon, /* Ctrl-C pressed? */
|
||||
clear_screen; /* Window resized? */
|
||||
@ -481,7 +476,6 @@ typedef struct afl_state {
|
||||
total_tmouts, /* Total number of timeouts */
|
||||
unique_tmouts, /* Timeouts with unique signatures */
|
||||
unique_hangs, /* Hangs with unique signatures */
|
||||
total_execs, /* Total execve() calls */
|
||||
last_crash_execs, /* Exec counter at last crash */
|
||||
queue_cycle, /* Queue round counter */
|
||||
cycles_wo_finds, /* Cycles without any new paths */
|
||||
@ -543,7 +537,7 @@ typedef struct afl_state {
|
||||
*queue_top, /* Top of the list */
|
||||
*q_prev100; /* Previous 100 marker */
|
||||
|
||||
struct queue_entry *top_rated[MAP_SIZE]; /* Top entries for bitmap bytes */
|
||||
struct queue_entry **top_rated; /* Top entries for bitmap bytes */
|
||||
|
||||
struct extra_data *extras; /* Extra tokens to fuzz with */
|
||||
u32 extras_cnt; /* Total number of tokens read */
|
||||
@ -551,16 +545,14 @@ 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 */
|
||||
|
||||
char *cmplog_binary;
|
||||
s32 cmplog_child_pid, cmplog_fsrv_pid;
|
||||
char * cmplog_binary;
|
||||
afl_forkserver_t cmplog_fsrv; /* cmplog has its own little forkserver */
|
||||
|
||||
/* Custom mutators */
|
||||
struct custom_mutator *mutator;
|
||||
@ -569,6 +561,9 @@ typedef struct afl_state {
|
||||
s32 cmplog_fsrv_ctl_fd, cmplog_fsrv_st_fd;
|
||||
u32 cmplog_prev_timed_out;
|
||||
|
||||
struct afl_pass_stat *pass_stats;
|
||||
struct cmp_map * orig_cmp_map;
|
||||
|
||||
u8 describe_op_buf_256[256]; /* describe_op will use this to return a string
|
||||
up to 256 */
|
||||
|
||||
@ -577,7 +572,9 @@ typedef struct afl_state {
|
||||
u32 document_counter;
|
||||
#endif
|
||||
|
||||
/* statis file */
|
||||
void *maybe_add_auto;
|
||||
|
||||
/* statistics file */
|
||||
double last_bitmap_cvg, last_stability, last_eps;
|
||||
|
||||
/* plot file saves from last run */
|
||||
@ -587,9 +584,9 @@ typedef struct afl_state {
|
||||
u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs;
|
||||
double stats_avg_exec;
|
||||
|
||||
u8 clean_trace[MAP_SIZE];
|
||||
u8 clean_trace_custom[MAP_SIZE];
|
||||
u8 first_trace[MAP_SIZE];
|
||||
u8 *clean_trace;
|
||||
u8 *clean_trace_custom;
|
||||
u8 *first_trace;
|
||||
|
||||
/*needed for afl_fuzz_one */
|
||||
// TODO: see which we can reuse
|
||||
@ -610,20 +607,23 @@ typedef struct afl_state {
|
||||
|
||||
u8 * ex_buf;
|
||||
size_t ex_size;
|
||||
u32 custom_mutators_count;
|
||||
|
||||
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
|
||||
@ -797,23 +797,33 @@ struct custom_mutator {
|
||||
|
||||
};
|
||||
|
||||
void afl_state_init(afl_state_t *);
|
||||
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 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 **);
|
||||
@ -838,20 +848,19 @@ u32 calculate_score(afl_state_t *, struct queue_entry *);
|
||||
|
||||
/* Bitmap */
|
||||
|
||||
void read_bitmap(afl_state_t *, u8 *);
|
||||
void write_bitmap(afl_state_t *);
|
||||
u32 count_bits(u8 *);
|
||||
u32 count_bytes(u8 *);
|
||||
u32 count_non_255_bytes(u8 *);
|
||||
u32 count_bits(afl_state_t *, u8 *);
|
||||
u32 count_bytes(afl_state_t *, u8 *);
|
||||
u32 count_non_255_bytes(afl_state_t *, u8 *);
|
||||
#ifdef WORD_SIZE_64
|
||||
void simplify_trace(u64 *);
|
||||
void classify_counts(u64 *);
|
||||
void simplify_trace(afl_state_t *, u64 *);
|
||||
void classify_counts(afl_forkserver_t *);
|
||||
#else
|
||||
void simplify_trace(u32 *);
|
||||
void classify_counts(u32 *);
|
||||
void simplify_trace(afl_state_t *, u32 *);
|
||||
void classify_counts(afl_forkserver_t *);
|
||||
#endif
|
||||
void init_count_class16(void);
|
||||
void minimize_bits(u8 *, u8 *);
|
||||
void minimize_bits(afl_state_t *, u8 *, u8 *);
|
||||
#ifndef SIMPLE_FILES
|
||||
u8 *describe_op(afl_state_t *, u8);
|
||||
#endif
|
||||
@ -862,7 +871,7 @@ u8 has_new_bits(afl_state_t *, u8 *);
|
||||
|
||||
void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32);
|
||||
void load_extras(afl_state_t *, u8 *);
|
||||
void maybe_add_auto(afl_state_t *, u8 *, u32);
|
||||
void maybe_add_auto(void *, u8 *, u32);
|
||||
void save_auto(afl_state_t *);
|
||||
void load_auto(afl_state_t *);
|
||||
void destroy_extras(afl_state_t *);
|
||||
@ -876,8 +885,8 @@ void show_init_stats(afl_state_t *);
|
||||
|
||||
/* Run */
|
||||
|
||||
u8 run_target(afl_state_t *, u32);
|
||||
void write_to_testcase(afl_state_t *, void *, u32);
|
||||
fsrv_run_result_t fuzz_run_target(afl_state_t *, afl_forkserver_t *fsrv, u32);
|
||||
void write_to_testcase(afl_state_t *, void *, u32);
|
||||
u8 calibrate_case(afl_state_t *, struct queue_entry *, u8 *, u32, u8);
|
||||
void sync_fuzzers(afl_state_t *);
|
||||
u8 trim_case(afl_state_t *, struct queue_entry *, u8 *);
|
||||
@ -920,8 +929,7 @@ void save_cmdline(afl_state_t *, u32, char **);
|
||||
|
||||
/* CmpLog */
|
||||
|
||||
void init_cmplog_forkserver(afl_state_t *afl);
|
||||
u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len);
|
||||
u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len);
|
||||
|
||||
/* RedQueen */
|
||||
u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len,
|
||||
@ -957,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];
|
||||
|
||||
}
|
||||
@ -968,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,53 +41,53 @@
|
||||
// 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. */
|
||||
|
||||
static inline void *DFL_ck_alloc_nozero(u32 size) {
|
||||
|
||||
u8 *ret;
|
||||
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);
|
||||
|
||||
@ -127,7 +127,7 @@ static inline void DFL_ck_free(void *mem) {
|
||||
|
||||
static inline void *DFL_ck_realloc(void *orig, u32 size) {
|
||||
|
||||
u8 *ret;
|
||||
void *ret;
|
||||
|
||||
if (!size) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -182,9 +182,9 @@ static inline u8 *DFL_ck_strdup(u8 *str) {
|
||||
|
||||
static inline void *DFL_ck_memdup(void *mem, u32 size) {
|
||||
|
||||
u8 *ret;
|
||||
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);
|
||||
|
@ -26,26 +26,29 @@
|
||||
#ifndef _ANDROID_ASHMEM_H
|
||||
#define _ANDROID_ASHMEM_H
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/ashmem.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#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>
|
||||
#include <fcntl.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/ashmem.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
#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>
|
||||
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
|
||||
static inline int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf) {
|
||||
|
||||
@ -103,5 +106,7 @@ static inline void *shmat(int __shmid, const void *__shmaddr, int __shmflg) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#define _AFL_CMPLOG_H
|
||||
|
||||
#include "config.h"
|
||||
#include "forkserver.h"
|
||||
|
||||
#define CMP_MAP_W 65536
|
||||
#define CMP_MAP_H 256
|
||||
@ -74,5 +75,9 @@ struct cmp_map {
|
||||
|
||||
};
|
||||
|
||||
/* Execs the child */
|
||||
|
||||
void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -51,6 +51,16 @@ char * get_afl_env(char *env);
|
||||
extern u8 be_quiet;
|
||||
extern u8 *doc_path; /* path to documentation dir */
|
||||
|
||||
/* Find binary, used by analyze, showmap, tmin
|
||||
@returns the path, allocating the string */
|
||||
|
||||
u8 *find_binary(u8 *fname);
|
||||
|
||||
/* Read a bitmap from file fname to memory
|
||||
This is for the -B option again. */
|
||||
|
||||
void read_bitmap(u8 *fname, u8 *map, size_t len);
|
||||
|
||||
/* Get unix time in milliseconds */
|
||||
|
||||
u64 get_cur_time(void);
|
||||
@ -99,9 +109,13 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms);
|
||||
|
||||
/* Wrapper for select() and read(), reading exactly len bytes.
|
||||
Returns the time passed to read.
|
||||
stop_soon should point to a variable indicating ctrl+c was pressed.
|
||||
If the wait times out, returns timeout_ms + 1;
|
||||
Returns 0 if an error occurred (fd closed, signal, ...); */
|
||||
u32 read_timed(s32 fd, void *buf, size_t len, u32 timeout_ms);
|
||||
u32 read_timed(s32 fd, void *buf, size_t len, u32 timeout_ms,
|
||||
volatile u8 *stop_soon_p);
|
||||
|
||||
u32 get_map_size(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
/* Version string: */
|
||||
|
||||
// c = release, d = volatile github dev, e = experimental branch
|
||||
#define VERSION "++2.63c"
|
||||
#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): */
|
||||
|
||||
@ -201,8 +201,8 @@
|
||||
(first value), and to keep in memory as candidates. The latter should be much
|
||||
higher than the former. */
|
||||
|
||||
#define USE_AUTO_EXTRAS 50
|
||||
#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 10)
|
||||
#define USE_AUTO_EXTRAS 128
|
||||
#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 64)
|
||||
|
||||
/* Scaling factor for the effector map used to skip some of the more
|
||||
expensive deterministic steps. The actual divisor is set to
|
||||
@ -395,9 +395,9 @@
|
||||
|
||||
/* 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__ */
|
||||
|
||||
#endif /* ! _HAVE_CONFIG_H */
|
||||
|
203
include/debug.h
203
include/debug.h
@ -28,87 +28,92 @@
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
/* __FUNCTION__ is non-iso */
|
||||
#ifdef __func__
|
||||
#define __FUNCTION__ __func__
|
||||
#endif
|
||||
|
||||
/*******************
|
||||
* Terminal colors *
|
||||
*******************/
|
||||
|
||||
#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 */
|
||||
|
||||
@ -118,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 */
|
||||
|
||||
@ -171,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. */
|
||||
|
@ -29,6 +29,9 @@
|
||||
#define __AFL_FORKSERVER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct afl_forkserver {
|
||||
|
||||
@ -40,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 */
|
||||
@ -50,42 +54,76 @@ typedef struct afl_forkserver {
|
||||
fsrv_ctl_fd, /* Fork server control pipe (write) */
|
||||
fsrv_st_fd; /* Fork server status pipe (read) */
|
||||
|
||||
u8 no_unlink; /* do not unlink cur_input */
|
||||
|
||||
u32 exec_tmout; /* Configurable exec timeout (ms) */
|
||||
u32 map_size; /* map size used by the target */
|
||||
u32 snapshot; /* is snapshot feature used */
|
||||
u64 mem_limit; /* Memory cap for child (MB) */
|
||||
|
||||
u64 total_execs; /* How often run_target was called */
|
||||
|
||||
u8 *out_file, /* File to fuzz, if any */
|
||||
*target_path; /* Path of the target */
|
||||
*target_path; /* Path of the target */
|
||||
|
||||
FILE *plot_file; /* Gnuplot output file */
|
||||
|
||||
u8 child_timed_out; /* Traced process timed out? */
|
||||
/* Note: lat_run_timed_out is u32 to send it to the child as 4 byte array */
|
||||
u32 last_run_timed_out; /* Traced process timed out? */
|
||||
|
||||
u8 last_kill_signal; /* Signal that killed the child */
|
||||
|
||||
u8 use_fauxsrv; /* Fauxsrv for non-forking targets? */
|
||||
|
||||
u32 prev_timed_out; /* if prev forkserver run timed out */
|
||||
u8 qemu_mode; /* if running in qemu mode or not */
|
||||
|
||||
char *cmplog_binary; /* the name of the cmplog binary */
|
||||
|
||||
/* Function to kick off the forkserver child */
|
||||
void (*init_child_func)(struct afl_forkserver *fsrv, char **argv);
|
||||
|
||||
u8 *function_opt; /* for autodictionary: afl ptr */
|
||||
|
||||
void (*function_ptr)(void *afl_tmp, u8 *mem, u32 len);
|
||||
|
||||
} afl_forkserver_t;
|
||||
|
||||
typedef enum fsrv_run_result {
|
||||
|
||||
/* 00 */ FSRV_RUN_OK = 0,
|
||||
/* 01 */ FSRV_RUN_TMOUT,
|
||||
/* 02 */ FSRV_RUN_CRASH,
|
||||
/* 03 */ FSRV_RUN_ERROR,
|
||||
/* 04 */ FSRV_RUN_NOINST,
|
||||
/* 05 */ FSRV_RUN_NOBITS,
|
||||
|
||||
} fsrv_run_result_t;
|
||||
|
||||
void afl_fsrv_init(afl_forkserver_t *fsrv);
|
||||
void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv);
|
||||
void afl_fsrv_deinit(afl_forkserver_t *fsrv);
|
||||
void afl_fsrv_killall();
|
||||
void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from);
|
||||
void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
volatile u8 *stop_soon_p, u8 debug_child_output);
|
||||
void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len);
|
||||
fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
volatile u8 *stop_soon_p);
|
||||
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) {
|
||||
|
||||
|
@ -50,7 +50,7 @@ typedef struct list_element {
|
||||
typedef struct list {
|
||||
|
||||
element_t element_prealloc_buf[LIST_PREALLOC_SIZE];
|
||||
u32 element_prealloc_count;
|
||||
s32 element_prealloc_count;
|
||||
|
||||
} list_t;
|
||||
|
||||
@ -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;
|
||||
|
@ -28,6 +28,8 @@
|
||||
#ifndef __AFL_SHAREDMEM_H
|
||||
#define __AFL_SHAREDMEM_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct sharedmem {
|
||||
|
||||
// extern unsigned char *trace_bits;
|
||||
@ -44,8 +46,7 @@ typedef struct sharedmem {
|
||||
|
||||
u8 *map; /* shared memory region */
|
||||
|
||||
size_t size_alloc; /* actual allocated size */
|
||||
size_t size_used; /* in use by shmem app */
|
||||
size_t map_size; /* actual allocated size */
|
||||
|
||||
int cmplog_mode;
|
||||
struct cmp_map *cmp_map;
|
||||
|
107
include/types.h
107
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
|
||||
@ -46,7 +69,7 @@ typedef uint32_t u32;
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__) || defined(__aarch64__)
|
||||
typedef unsigned long long u64;
|
||||
#else
|
||||
typedef uint64_t u64;
|
||||
@ -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");
|
||||
|
@ -1,63 +0,0 @@
|
||||
#
|
||||
# american fuzzy lop++ - libtokencap
|
||||
# --------------------------------
|
||||
#
|
||||
# Originally written by Michal Zalewski
|
||||
#
|
||||
# Copyright 2016 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
|
||||
#
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
HELPER_PATH = $(PREFIX)/lib/afl
|
||||
DOC_PATH ?= $(PREFIX)/share/doc/afl
|
||||
MAN_PATH ?= $(PREFIX)/man/man8
|
||||
|
||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops
|
||||
override CFLAGS += -I ../include/ -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
|
||||
|
||||
ifeq "$(shell uname)" "Linux"
|
||||
TARGETS = libtokencap.so
|
||||
LDFLAGS += -ldl
|
||||
endif
|
||||
ifeq "$(shell uname)" "Darwin"
|
||||
TARGETS = libtokencap.so
|
||||
LDFLAGS += -ldl
|
||||
endif
|
||||
ifeq "$(shell uname)" "FreeBSD"
|
||||
TARGETS = libtokencap.so
|
||||
endif
|
||||
ifeq "$(shell uname)" "OpenBSD"
|
||||
TARGETS = libtokencap.so
|
||||
endif
|
||||
ifeq "$(shell uname)" "NetBSD"
|
||||
TARGETS = libtokencap.so
|
||||
endif
|
||||
ifeq "$(shell uname)" "DragonFly"
|
||||
TARGETS = libtokencap.so
|
||||
LDFLAGS += -ldl
|
||||
endif
|
||||
all: $(TARGETS)
|
||||
|
||||
VPATH = ..
|
||||
libtokencap.so: libtokencap.so.c ../config.h
|
||||
$(CC) $(CFLAGS) -shared -fPIC $< -o ../$@ $(LDFLAGS)
|
||||
|
||||
.NOTPARALLEL: clean
|
||||
|
||||
clean:
|
||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
|
||||
rm -f ../libtokencap.so
|
||||
|
||||
install: all
|
||||
install -m 755 -d $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 755 ../libtokencap.so $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.tokencap.md
|
||||
|
@ -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)" ""
|
||||
@ -58,22 +58,24 @@ endif
|
||||
|
||||
ifeq "$(LLVM_MAJOR)" "9"
|
||||
$(info [+] llvm_mode detected llvm 9, enabling neverZero implementation)
|
||||
$(info [+] llvm_mode detected llvm 9, enabling afl-clang-lto LTO implementation)
|
||||
LLVM_LTO = 1
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_NEW_API)" "1"
|
||||
$(info [+] llvm_mode detected llvm 10+, enabling neverZero implementation and c++14)
|
||||
$(info [+] llvm_mode detected llvm 9, enabling afl-clang-lto LTO implementation)
|
||||
LLVM_STDCXX = c++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 < 9, afl-clang-lto LTO will not be build.)
|
||||
$(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
|
||||
|
||||
@ -84,27 +86,76 @@ endif
|
||||
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
|
||||
@ -113,41 +164,47 @@ 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
|
||||
endif
|
||||
|
||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||
ifeq "$(AFL_REAL_LD)" ""
|
||||
AFL_REAL_LD = $(shell readlink /bin/ld 2>/dev/null)
|
||||
ifeq "$(AFL_REAL_LD)" ""
|
||||
AFL_REAL_LD = $(shell readlink /usr/bin/ld 2>/dev/null)
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||
ifeq "$(AFL_REAL_LD)" ""
|
||||
ifneq "$(shell readlink $(LLVM_BINDIR)/ld.lld 2>&1)" ""
|
||||
AFL_REAL_LD = $(LLVM_BINDIR)/ld.lld
|
||||
else
|
||||
$(warn ld.lld not found, can not enable LTO mode)
|
||||
LLVM_LTO = 0
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
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
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2
|
||||
override CFLAGS = -Wall \
|
||||
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)\" \
|
||||
-DAFL_REAL_LD=\"$(AFL_REAL_LD)\" -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" -Wno-unused-function
|
||||
-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=\"$(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
|
||||
@ -156,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"
|
||||
@ -174,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-ld ../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
|
||||
|
||||
@ -222,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)'..."
|
||||
@ -230,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
|
||||
@ -242,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"
|
||||
@ -251,64 +309,65 @@ ifeq "$(LLVM_LTO)" "1"
|
||||
endif
|
||||
endif
|
||||
|
||||
../afl-ld: afl-ld.c
|
||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
||||
ln -sf afl-ld ../ld
|
||||
@rm -f .test-instr
|
||||
@-export AFL_QUIET=1 AFL_PATH=.. PATH="..:$(PATH)" ; ../afl-clang-lto -Wl,--afl -o .test-instr ../test-instr.c && echo "[+] afl-clang-lto and afl-ld seem to work fine :)" || echo "[!] WARNING: clang seems to have a hardcoded "'/bin/ld'" - check README.lto"
|
||||
@rm -f .test-instr
|
||||
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 MarkNodes.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 $< MarkNodes.cc -o $@ $(CLANG_LFL)
|
||||
$(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) -fPIC -c $< -o $@
|
||||
$(CC) $(CFLAGS) -Wno-unused-result -fPIC -c $< -o $@
|
||||
|
||||
../afl-llvm-rt-32.o: afl-llvm-rt.o.c | test_deps
|
||||
@printf "[*] Building 32-bit variant of the runtime (-m32)... "
|
||||
@$(CC) $(CFLAGS) -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
@$(CC) $(CFLAGS) -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
|
||||
../afl-llvm-rt-64.o: afl-llvm-rt.o.c | test_deps
|
||||
@printf "[*] Building 64-bit variant of the runtime (-m64)... "
|
||||
@$(CC) $(CFLAGS) -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
@$(CC) $(CFLAGS) -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
|
||||
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
|
||||
@ -323,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 -a -f ../afl-ld ]; 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-ld $${DESTDIR}$(HELPER_PATH); ln -sf afl-ld $${DESTDIR}$(HELPER_PATH)/ld; install -m 755 ../afl-llvm-lto-instrumentation.so $${DESTDIR}$(HELPER_PATH); install -m 755 ../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
|
||||
@ -363,4 +422,4 @@ endif
|
||||
|
||||
clean:
|
||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1 *.dwo
|
||||
rm -f $(PROGS) afl-common.o ../afl-clang-fast++ ../afl-clang-lto ../afl-clang-lto++ ../afl-clang*.8 ../ld
|
||||
rm -f $(PROGS) afl-common.o ../afl-clang-fast++ ../afl-clang-lto ../afl-clang-lto++ ../afl-clang*.8 ../ld ../afl-ld ../afl-llvm-rt*.o
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -2,31 +2,39 @@
|
||||
|
||||
## TLDR;
|
||||
|
||||
1. This compile mode is very frickle if it works it is amazing, if it fails
|
||||
- well use afl-clang-fast
|
||||
This version requires a current llvm 11 compiled from the github master.
|
||||
|
||||
2. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better
|
||||
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)
|
||||
|
||||
3. You can use it together with llvm_mode: laf-intel and whitelisting
|
||||
2. You can use it together with llvm_mode: laf-intel and whitelisting
|
||||
features and can be combined with cmplog/Redqueen
|
||||
|
||||
4. It only works with llvm 9 (and likely 10+ but is not tested there yet)
|
||||
3. It only works with llvm 11 (current github master state)
|
||||
|
||||
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
|
||||
set during compilation are random - and hence natually the larger the number
|
||||
of instrumented locations, the higher the number of edge collisions in the
|
||||
set during compilation are random - and hence naturally the larger the number
|
||||
of instrumented locations, the higher the number of edge collisions are in the
|
||||
map. This can result in not discovering new paths and therefore degrade the
|
||||
efficiency of the fuzzing.
|
||||
efficiency of the fuzzing process.
|
||||
|
||||
*This issue is understimated in the fuzzing community!*
|
||||
*This issue is underestimated in the fuzzing community!*
|
||||
With a 2^16 = 64kb standard map at already 256 instrumented blocks there is
|
||||
on average one collision. On average a target has 10.000 to 50.000
|
||||
instrumented blocks hence the real collisions are between 750-18.000!
|
||||
|
||||
To get to a solution that prevents any collision took several approaches
|
||||
To reach a solution that prevents any collisions took several approaches
|
||||
and many dead ends until we got to this:
|
||||
|
||||
* We instrument at link time when we have all files pre-compiled
|
||||
@ -34,49 +42,79 @@ and many dead ends until we got to this:
|
||||
* Our compiler (afl-clang-lto/afl-clang-lto++) takes care of setting the
|
||||
correct LTO options and runs our own afl-ld linker instead of the system
|
||||
linker
|
||||
* Our linker collects all LTO files to link and instruments them so that
|
||||
* The LLVM linker collects all LTO files to link and instruments them so that
|
||||
we have non-colliding edge overage
|
||||
* We use a new (for afl) edge coverage - which is the same as in llvm
|
||||
-fsanitize=coverage edge coverage mode :)
|
||||
* after inserting our instrumentation in all interesting edges we link
|
||||
all parts of the program together to our executable
|
||||
|
||||
The result:
|
||||
* 10-15% 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
|
||||
|
||||
Example build output from a libtiff build:
|
||||
```
|
||||
/bin/bash ../libtool --tag=CC --mode=link afl-clang-lto -g -O2 -Wall -W -o thumbnail thumbnail.o ../libtiff/libtiff.la ../port/libport.la -llzma -ljbig -ljpeg -lz -lm
|
||||
libtool: link: afl-clang-lto -g -O2 -Wall -W -o thumbnail thumbnail.o ../libtiff/.libs/libtiff.a ../port/.libs/libport.a -llzma -ljbig -ljpeg -lz -lm
|
||||
afl-clang-lto++2.62d by Marc "vanHauser" Heuse <mh@mh-sec.de>
|
||||
afl-ld++2.62d by Marc "vanHauser" Heuse <mh@mh-sec.de> (level 0)
|
||||
[+] Running ar unpacker on /prg/tests/lto/tiff-4.0.4/tools/../libtiff/.libs/libtiff.a into /tmp/.afl-3914343-1583339800.dir
|
||||
[+] Running ar unpacker on /prg/tests/lto/tiff-4.0.4/tools/../port/.libs/libport.a into /tmp/.afl-3914343-1583339800.dir
|
||||
[+] Running bitcode linker, creating /tmp/.afl-3914343-1583339800-1.ll
|
||||
[+] Performing optimization via opt, creating /tmp/.afl-3914343-1583339800-2.bc
|
||||
[+] Performing instrumentation via opt, creating /tmp/.afl-3914343-1583339800-3.bc
|
||||
afl-llvm-lto++2.62d by Marc "vanHauser" Heuse <mh@mh-sec.de>
|
||||
[+] Instrumented 15833 locations with no collisions (on average 1767 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode).
|
||||
[+] Running real linker /bin/x86_64-linux-gnu-ld
|
||||
[+] Linker was successful
|
||||
afl-clang-lto++2.63d by Marc "vanHauser" Heuse <mh@mh-sec.de> in mode LTO
|
||||
afl-llvm-lto++2.63d by Marc "vanHauser" Heuse <mh@mh-sec.de>
|
||||
AUTODICTIONARY: 11 strings found
|
||||
[+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode).
|
||||
```
|
||||
|
||||
## Building llvm 11
|
||||
|
||||
```
|
||||
$ sudo apt install binutils-dev # this is *essential*!
|
||||
$ git clone https://github.com/llvm/llvm-project
|
||||
$ cd llvm-project
|
||||
$ mkdir build
|
||||
$ 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/llvm-config
|
||||
$ cd /path/to/AFLplusplus/
|
||||
$ make
|
||||
$ cd llvm_mode
|
||||
$ make
|
||||
$ cd ..
|
||||
$ make install
|
||||
```
|
||||
|
||||
## How to use afl-clang-lto
|
||||
|
||||
Just use afl-clang-lto like you did afl-clang-fast or afl-gcc.
|
||||
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
|
||||
```
|
||||
|
||||
## AUTODICTIONARY feature
|
||||
|
||||
Setting `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 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
|
||||
@ -92,147 +130,40 @@ 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 ...
|
||||
|
||||
### "linking globals named '...': symbol multiply defined" error
|
||||
|
||||
The target program is using multiple global variables or functions with the
|
||||
same name. This is a common error when compiling a project with LTO, and
|
||||
the fix is `-Wl,--allow-multiple-definition` - however llvm-link which we
|
||||
need to link all llvm IR LTO files does not support this - yet (hopefully).
|
||||
Hence if you see this error either you have to remove the duplicate global
|
||||
variable (think `#ifdef` ...) or you are out of luck. :-(
|
||||
|
||||
### "expected top-level entity" + binary ouput error
|
||||
|
||||
This happens if multiple .a archives are to be linked and they contain the
|
||||
same object filenames, the first in LTO form, the other in ELF form.
|
||||
This can not be fixed programmatically, but can be fixed by hand.
|
||||
You can try to delete the file from either archive
|
||||
(`llvm-ar d <archive>.a <file>.o`) or performing the llvm-linking, optimizing
|
||||
and instrumentation by hand (see below).
|
||||
|
||||
### "undefined reference to ..."
|
||||
|
||||
This *can* be the opposite situation of the "expected top-level entity" error -
|
||||
the library with the ELF file is before the LTO library.
|
||||
However it can also be a bug in the program - try to compile it normally. If
|
||||
fails then it is a bug in the program.
|
||||
Solutions: You can try to delete the file from either archive, e.g.
|
||||
(`llvm-ar d <archive>.a <file>.o`) or performing the llvm-linking, optimizing
|
||||
and instrumentation by hand (see below).
|
||||
|
||||
### "File format not recognized"
|
||||
|
||||
This happens if the build system has fixed LDFLAGS, CPPFLAGS, CXXFLAGS and/or
|
||||
CFLAGS. Ensure that they all contain the `-flto` flag that afl-clang-lto was
|
||||
compiled with (you can see that by typing `afl-clang-lto -h` and inspecting
|
||||
the last line of the help output) and add them otherwise
|
||||
|
||||
### clang is hardcoded to /bin/ld
|
||||
|
||||
Some clang packages have 'ld' hardcoded to /bin/ld. This is an issue as this
|
||||
prevents "our" afl-ld being called.
|
||||
|
||||
-fuse-ld=/path/to/afl-ld should be set through makefile magic in llvm_mode -
|
||||
if it is supported - however if this fails you can try:
|
||||
```
|
||||
LDFLAGS=-fuse-ld=</path/to/afl-ld
|
||||
```
|
||||
|
||||
As workaround attempt #2 you will have to switch /bin/ld:
|
||||
```
|
||||
mv /bin/ld /bin/ld.orig
|
||||
cp afl-ld /bin/ld
|
||||
```
|
||||
This can result in two problems though:
|
||||
|
||||
!1!
|
||||
When compiling afl-ld, the build process looks at where the /bin/ld link
|
||||
is going to. So when the workaround was applied and a recompiling afl-ld
|
||||
is performed then the link is gone and the new afl-ld clueless where
|
||||
the real ld is.
|
||||
In this case set AFL_REAL_LD=/bin/ld.orig
|
||||
|
||||
!2!
|
||||
When you install an updated gcc/clang/... package, your OS might restore
|
||||
the ld link.
|
||||
|
||||
### Performing the steps by hand
|
||||
|
||||
It is possible to perform all the steps afl-ld by hand to workaround issues
|
||||
in the target.
|
||||
|
||||
1. Recompile with AFL_DEBUG=1 and collect the afl-clang-lto command that fails
|
||||
e.g.: `AFL_DEBUG=1 make 2>&1 | grep afl-clang-lto | tail -n 1`
|
||||
|
||||
2. run this command prepended with AFL_DEBUG=1 and collect the afl-ld command
|
||||
parameters, e.g. `AFL_DEBUG=1 afl-clang-lto[++] .... | grep /afl/ld`
|
||||
|
||||
3. for every .a archive you want to instrument unpack it into a seperate
|
||||
directory, e.g.
|
||||
`mkdir archive1.dir ; cd archive1.dir ; llvm-link x ../<archive>.a`
|
||||
|
||||
4. run `file archive*.dir/*.o` and make two lists, one containing all ELF files
|
||||
and one containing all LLVM IR bitcode files.
|
||||
You do the same for all .o files of the ../afl/ld command options
|
||||
|
||||
5. Create a single bitcode file by using llvm-link, e.g.
|
||||
`llvm-link -o all-bitcode.bc <list of all LLVM IR .o files>`
|
||||
If this fails it is game over - or you modify the source code
|
||||
|
||||
6. Run the optimizer on the new bitcode file:
|
||||
`opt -O3 --polly -o all-optimized.bc all-bitcode.bc`
|
||||
|
||||
7. Instrument the optimized bitcode file:
|
||||
`opt --load=$AFL_PATH/afl-llvm-lto-instrumentation.so --disable-opt --afl-lto all-optimized.bc -o all-instrumented.bc
|
||||
|
||||
8. If the parameter `--allow-multiple-definition` is not in the list, add it
|
||||
as first command line option.
|
||||
|
||||
9. Link everything together.
|
||||
a) You use the afl-ld command and instead of e.g. `/usr/local/lib/afl/ld`
|
||||
you replace that with `ld`, the real linker.
|
||||
b) Every .a archive you instrumented files from you remove the <archive>.a
|
||||
or -l<archive> from the command
|
||||
c) If you have entries in your ELF files list (see step 4), you put them to
|
||||
the command line - but them in the same order!
|
||||
d) put the all-instrumented.bc before the first library or .o file
|
||||
e) run the command and hope it compiles, if it doesn't you have to analyze
|
||||
what the issue is and fix that in the approriate step above.
|
||||
|
||||
Yes this is long and complicated. That is why there is afl-ld doing this and
|
||||
that why this can easily fail and not all different ways how it *can* fail can
|
||||
be implemented ...
|
||||
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.
|
||||
Complex targets are still likely not to compile and this needs to be fixed.
|
||||
Please report issues at:
|
||||
[https://github.com/AFLplusplus/AFLplusplus/issues/226](https://github.com/AFLplusplus/AFLplusplus/issues/226)
|
||||
|
||||
Known issues:
|
||||
* ffmpeg
|
||||
* bogofilter
|
||||
* libjpeg-turbo-1.3.1
|
||||
* 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 not instrument main, start and init functions
|
||||
2. Modify the forkserver + afl-fuzz so that only the necessary map size is
|
||||
loaded and used - and communicated to afl-fuzz too.
|
||||
Result: faster fork in the target and faster map analysis in afl-fuzz
|
||||
=> more speed :-)
|
||||
|
||||
## Tested and working targets
|
||||
|
||||
* libpng-1.2.53
|
||||
* libxml2-2.9.2
|
||||
* tiff-4.0.4
|
||||
* unrar-nonfree-5.6.6
|
||||
* exiv 0.27
|
||||
* jpeg-6b
|
||||
1. Currently the LTO whitelist feature does not allow to instrument main,
|
||||
start and init functions
|
||||
|
||||
## History
|
||||
|
||||
@ -249,14 +180,20 @@ This was first implemented in January and work ... kinda.
|
||||
The LTO time instrumentation worked, however the "how" the basic blocks were
|
||||
instrumented was a problem, as reducing duplicates turned out to be very,
|
||||
very difficult with a program that has so many paths and therefore so many
|
||||
dependencies. At lot of stratgies were implemented - and failed.
|
||||
dependencies. At lot of strategies were implemented - and failed.
|
||||
And then sat solvers were tried, but with over 10.000 variables that turned
|
||||
out to be a dead-end too.
|
||||
|
||||
The final idea to solve this came from domenukk who proposed to insert a block
|
||||
into an edge and then just use incremental counters ... and this worked!
|
||||
After some trials and errors to implement this vanhauser-thc found out that
|
||||
there is actually an llvm function for this: SplitEdge() :-)
|
||||
|
||||
Still more problems came up though as this only works without bugs from
|
||||
llvm 9 onwards, and with high optimization the link optimization ruins
|
||||
the instrumented control flow graph.
|
||||
As long as there are no larger changes in llvm this all should work well now ...
|
||||
|
||||
This is all now fixed with llvm 11. The llvm's own linker is now able to
|
||||
load passes and this bypasses all problems we had.
|
||||
|
||||
Happy end :)
|
||||
|
@ -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", ""
|
||||
|
||||
};
|
||||
|
||||
@ -159,7 +160,6 @@ static void find_obj(u8 *argv0) {
|
||||
static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0;
|
||||
u8 has_llvm_config = 0;
|
||||
u8 *name;
|
||||
|
||||
cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
|
||||
@ -170,9 +170,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
else
|
||||
++name;
|
||||
|
||||
has_llvm_config = (strlen(LLVM_BINDIR) > 0);
|
||||
|
||||
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 "
|
||||
@ -181,29 +179,45 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
if (!strcmp(name, "afl-clang-fast++") || !strcmp(name, "afl-clang-lto++")) {
|
||||
|
||||
u8 *alt_cxx = getenv("AFL_CXX");
|
||||
if (has_llvm_config)
|
||||
if (USE_BINDIR)
|
||||
snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++", LLVM_BINDIR);
|
||||
else
|
||||
sprintf(llvm_fullpath, "clang++");
|
||||
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)llvm_fullpath;
|
||||
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") ||
|
||||
|
||||
!strcmp(name, "afl-clang-lto")) {
|
||||
|
||||
u8 *alt_cc = getenv("AFL_CC");
|
||||
if (USE_BINDIR)
|
||||
snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", LLVM_BINDIR);
|
||||
else
|
||||
sprintf(llvm_fullpath, CLANG_BIN);
|
||||
cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)llvm_fullpath;
|
||||
|
||||
} else {
|
||||
|
||||
u8 *alt_cc = getenv("AFL_CC");
|
||||
if (has_llvm_config)
|
||||
snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", LLVM_BINDIR);
|
||||
else
|
||||
sprintf(llvm_fullpath, "clang");
|
||||
cc_params[0] = alt_cc ? alt_cc : (u8 *)llvm_fullpath;
|
||||
fprintf(stderr, "Name of the binary: %s\n", argv[0]);
|
||||
FATAL(
|
||||
"Name of the binary is not a known name, expected afl-clang-fast(++) "
|
||||
"or afl-clang-lto(++)");
|
||||
|
||||
}
|
||||
|
||||
/* 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
|
||||
@ -220,6 +234,12 @@ 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") && lto_mode)
|
||||
WARNF(
|
||||
"using AFL_LLVM_LAF_TRANSFORM_COMPARES together with "
|
||||
"AFL_LLVM_LTO_AUTODICTIONARY makes no sense. Use only "
|
||||
"AFL_LLVM_LTO_AUTODICTIONARY.");
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
@ -267,13 +287,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (instrument_mode == INSTRUMENT_LTO) {
|
||||
|
||||
char *old_path = getenv("PATH");
|
||||
char *new_path = alloc_printf("%s:%s", AFL_PATH, old_path);
|
||||
|
||||
setenv("PATH", new_path, 1);
|
||||
setenv("AFL_LD", "1", 1);
|
||||
if (lto_mode) {
|
||||
|
||||
if (getenv("AFL_LLVM_WHITELIST") != NULL) {
|
||||
|
||||
@ -285,13 +299,14 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
#ifdef AFL_CLANG_FUSELD
|
||||
cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s/afl-ld", AFL_PATH);
|
||||
#endif
|
||||
|
||||
cc_params[cc_par_cnt++] = "-B";
|
||||
cc_params[cc_par_cnt++] = AFL_PATH;
|
||||
|
||||
cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD);
|
||||
cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition";
|
||||
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 {
|
||||
@ -318,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) {
|
||||
@ -386,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)
|
||||
@ -410,7 +441,10 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_NO_BUILTIN")) {
|
||||
if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
|
||||
getenv("LAF_TRANSFORM_COMPARES") ||
|
||||
(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";
|
||||
@ -491,21 +525,38 @@ 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 (lto_mode)
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/afl-llvm-rt-lto.o", obj_path);
|
||||
break;
|
||||
|
||||
case 32:
|
||||
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 (lto_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path);
|
||||
if (access(cc_params[cc_par_cnt - 1], R_OK))
|
||||
FATAL("-m32 is not supported by your compiler");
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 64:
|
||||
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 (lto_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path);
|
||||
if (access(cc_params[cc_par_cnt - 1], R_OK))
|
||||
FATAL("-m64 is not supported by your compiler");
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@ -522,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")) {
|
||||
|
||||
@ -537,50 +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, "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")) {
|
||||
|
||||
@ -602,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
|
||||
@ -692,55 +813,54 @@ int main(int argc, char **argv, char **envp) {
|
||||
"Environment variables used:\n"
|
||||
"AFL_CC: path to the C compiler to use\n"
|
||||
"AFL_CXX: path to the C++ compiler to use\n"
|
||||
"AFL_PATH: path to instrumenting pass and runtime "
|
||||
"(afl-llvm-rt.*o)\n"
|
||||
"AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n"
|
||||
"AFL_NO_BUILTIN: compile for use with libtokencap.so\n"
|
||||
"AFL_INST_RATIO: percentage of branches to instrument\n"
|
||||
"AFL_QUIET: suppress verbose output\n"
|
||||
"AFL_DEBUG: enable developer debugging output\n"
|
||||
"AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n"
|
||||
"AFL_HARDEN: adds code hardening to catch memory bugs\n"
|
||||
"AFL_USE_ASAN: activate address sanitizer\n"
|
||||
"AFL_USE_MSAN: activate memory sanitizer\n"
|
||||
"AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"
|
||||
"AFL_USE_CFISAN: activate control flow sanitizer\n"
|
||||
"AFL_LLVM_WHITELIST: enable whitelisting (selective "
|
||||
"instrumentation)\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_SWITCHES: casc. comp. in 'switch'\n"
|
||||
"AFL_LLVM_LAF_TRANSFORM_COMPARES: transform library comparison "
|
||||
"function calls\n"
|
||||
" to cascaded comparisons\n"
|
||||
"AFL_LLVM_LAF_SPLIT_FLOATS: transform floating point comp. to "
|
||||
"cascaded "
|
||||
"comp.\n"
|
||||
"AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n",
|
||||
"AFL_LLVM_LAF_SPLIT_SWITCHES: casc. comp. in 'switch'\n"
|
||||
" to cascaded comparisons\n"
|
||||
"AFL_LLVM_LAF_TRANSFORM_COMPARES: transform library comparison "
|
||||
"function calls\n"
|
||||
"AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n"
|
||||
"AFL_LLVM_WHITELIST: enable whitelisting (selective "
|
||||
"instrumentation)\n"
|
||||
"AFL_NO_BUILTIN: compile for use with libtokencap.so\n"
|
||||
"AFL_PATH: path to instrumenting pass and runtime "
|
||||
"(afl-llvm-rt.*o)\n"
|
||||
"AFL_QUIET: suppress verbose output\n"
|
||||
"AFL_USE_ASAN: activate address sanitizer\n"
|
||||
"AFL_USE_CFISAN: activate control flow sanitizer\n"
|
||||
"AFL_USE_MSAN: activate memory sanitizer\n"
|
||||
"AFL_USE_UBSAN: activate undefined behaviour sanitizer\n",
|
||||
callname, BIN_PATH, BIN_PATH);
|
||||
|
||||
SAYF(
|
||||
"\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"
|
||||
"You can also use the old environment variables:"
|
||||
"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");
|
||||
"(INSTRIM), PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n"
|
||||
" You can also use the old environment variables instead:"
|
||||
" AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n"
|
||||
" AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n"
|
||||
" AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed (sub "
|
||||
"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(
|
||||
"\nafl-clang-lto specific environment variables:\n"
|
||||
"AFL_LLVM_LTO_STARTID: from which ID to start counting from for a "
|
||||
"bb\n"
|
||||
"AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a "
|
||||
"global var\n"
|
||||
"AFL_REAL_LD: use this linker instead of the compiled in path\n"
|
||||
"AFL_LD_PASSTHROUGH: do not perform instrumentation (for configure "
|
||||
"scripts)\n"
|
||||
"AFL_LLVM_LTO_STARTID: from which ID to start counting from for a "
|
||||
"bb\n"
|
||||
"AFL_REAL_LD: use this lld linker instead of the compiled in path\n"
|
||||
"\nafl-clang-lto was built with linker target \"%s\" and LTO flags "
|
||||
"\"%s\"\n"
|
||||
"If anything fails - be sure to read README.lto.md!\n",
|
||||
@ -760,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",
|
||||
@ -774,6 +894,16 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
u8 *ptr2;
|
||||
if (!be_quiet && !lto_mode &&
|
||||
((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) {
|
||||
|
||||
u32 map_size = atoi(ptr2);
|
||||
if (map_size != MAP_SIZE)
|
||||
FATAL("AFL_MAP_SIZE is not supported by afl-clang-fast");
|
||||
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
|
||||
SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd());
|
||||
|
@ -1,839 +0,0 @@
|
||||
/*
|
||||
american fuzzy lop++ - wrapper for GNU ld
|
||||
-----------------------------------------
|
||||
|
||||
Written by Marc Heuse <mh@mh-sec.de> for afl++
|
||||
|
||||
Maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de>
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
Dominik Maier <domenukk@gmail.com>
|
||||
|
||||
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
|
||||
|
||||
The sole purpose of this wrapper is to preprocess clang LTO files before
|
||||
linking by ld and perform the instrumentation on the whole program.
|
||||
|
||||
*/
|
||||
|
||||
#define AFL_MAIN
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#define MAX_PARAM_COUNT 4096
|
||||
|
||||
static u8 **ld_params, /* Parameters passed to the real 'ld' */
|
||||
**link_params, /* Parameters passed to 'llvm-link' */
|
||||
**opt_params, /* Parameters passed to 'opt' opt */
|
||||
**inst_params; /* Parameters passed to 'opt' inst */
|
||||
|
||||
static u8 *input_file; /* Originally specified input file */
|
||||
static u8 *final_file, /* Instrumented file for the real 'ld' */
|
||||
*linked_file, /* file where we link all files */
|
||||
*modified_file; /* file that was optimized before instr */
|
||||
static u8 *afl_path = AFL_PATH;
|
||||
static u8 *real_ld = AFL_REAL_LD;
|
||||
static u8 cwd[4096];
|
||||
static u8 *tmp_dir;
|
||||
static u8 *ar_dir;
|
||||
static u8 ar_dir_cnt;
|
||||
static u8 *libdirs[254];
|
||||
static u8 libdir_cnt;
|
||||
|
||||
static u8 be_quiet, /* Quiet mode (no stderr output) */
|
||||
debug, /* AFL_DEBUG */
|
||||
passthrough, /* AFL_LD_PASSTHROUGH - no link+optimize*/
|
||||
we_link, /* we have bc/ll -> link + optimize */
|
||||
just_version; /* Just show version? */
|
||||
|
||||
static u32 ld_param_cnt = 1, /* Number of params to 'ld' */
|
||||
link_param_cnt = 1, /* Number of params to 'llvm-link' */
|
||||
opt_param_cnt = 1, /* Number of params to 'opt' opt */
|
||||
inst_param_cnt = 1; /* Number of params to 'opt' instr */
|
||||
|
||||
/* This function wipes a directory - our AR unpack directory in this case */
|
||||
static u8 wipe_directory(u8 *path) {
|
||||
|
||||
DIR * d;
|
||||
struct dirent *d_ent;
|
||||
|
||||
d = opendir(path);
|
||||
|
||||
if (!d) return 0;
|
||||
|
||||
while ((d_ent = readdir(d))) {
|
||||
|
||||
if (strcmp(d_ent->d_name, ".") != 0 && strcmp(d_ent->d_name, "..") != 0) {
|
||||
|
||||
u8 *fname = alloc_printf("%s/%s", path, d_ent->d_name);
|
||||
if (unlink(fname)) PFATAL("Unable to delete '%s'", fname);
|
||||
ck_free(fname);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
return !!rmdir(path);
|
||||
|
||||
}
|
||||
|
||||
/* remove temporary files on fatal errors */
|
||||
static void at_exit_handler(void) {
|
||||
|
||||
if (!getenv("AFL_KEEP_ASSEMBLY")) {
|
||||
|
||||
if (linked_file) {
|
||||
|
||||
unlink(linked_file);
|
||||
linked_file = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (modified_file) {
|
||||
|
||||
unlink(modified_file);
|
||||
modified_file = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (final_file) {
|
||||
|
||||
unlink(final_file);
|
||||
final_file = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (ar_dir != NULL) {
|
||||
|
||||
wipe_directory(ar_dir);
|
||||
ar_dir = NULL;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This function checks if the parameter is a) an existing file and b)
|
||||
if it is a BC or LL file, if both are true it returns 1 and 0 otherwise */
|
||||
int is_llvm_file(const char *file) {
|
||||
|
||||
int fd;
|
||||
u8 buf[5];
|
||||
|
||||
if ((fd = open(file, O_RDONLY)) < 0) {
|
||||
|
||||
if (debug) SAYF(cMGN "[D] " cRST "File %s not found", file);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
if (read(fd, buf, 4) != 4) return 0;
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
|
||||
close(fd);
|
||||
|
||||
if (strncmp(buf, "; Mo", 4) == 0) return 1;
|
||||
|
||||
if (buf[0] == 'B' && buf[1] == 'C' && buf[2] == 0xc0 && buf[3] == 0xde)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Return the current working directory, not thread safe ;-) */
|
||||
u8 *getthecwd() {
|
||||
|
||||
static u8 fail[] = "";
|
||||
if (getcwd(cwd, sizeof(cwd)) == NULL) return fail;
|
||||
return cwd;
|
||||
|
||||
}
|
||||
|
||||
/* Check if an ar extracted file is already in the parameter list */
|
||||
int is_duplicate(u8 **params, u32 ld_param_cnt, u8 *ar_file) {
|
||||
|
||||
for (uint32_t i = 0; i < ld_param_cnt; i++)
|
||||
if (params[i] != NULL)
|
||||
if (strcmp(params[i], ar_file) == 0) return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Examine and modify parameters to pass to 'ld', 'llvm-link' and 'llmv-ar'.
|
||||
Note that the file name is always the last parameter passed by GCC,
|
||||
so we exploit this property to keep the code "simple". */
|
||||
static void edit_params(int argc, char **argv) {
|
||||
|
||||
u32 i, have_lto = 0, libdir_index;
|
||||
u8 libdir_file[4096];
|
||||
|
||||
if (tmp_dir == NULL) {
|
||||
|
||||
tmp_dir = getenv("TMPDIR");
|
||||
if (!tmp_dir) tmp_dir = getenv("TEMP");
|
||||
if (!tmp_dir) tmp_dir = getenv("TMP");
|
||||
if (!tmp_dir) tmp_dir = "/tmp";
|
||||
|
||||
}
|
||||
|
||||
linked_file =
|
||||
alloc_printf("%s/.afl-%u-%u-1.ll", tmp_dir, getpid(), (u32)time(NULL));
|
||||
modified_file =
|
||||
alloc_printf("%s/.afl-%u-%u-2.bc", tmp_dir, getpid(), (u32)time(NULL));
|
||||
final_file =
|
||||
alloc_printf("%s/.afl-%u-%u-3.bc", tmp_dir, getpid(), (u32)time(NULL));
|
||||
|
||||
ld_params = ck_alloc(4096 * sizeof(u8 *));
|
||||
link_params = ck_alloc(4096 * sizeof(u8 *));
|
||||
inst_params = ck_alloc(12 * sizeof(u8 *));
|
||||
opt_params = ck_alloc(12 * sizeof(u8 *));
|
||||
|
||||
ld_params[0] = (u8 *)real_ld;
|
||||
ld_params[ld_param_cnt++] = "--allow-multiple-definition";
|
||||
|
||||
link_params[0] = alloc_printf("%s/%s", LLVM_BINDIR, "llvm-link");
|
||||
link_params[link_param_cnt++] = "-S"; // we create the linked file as .ll
|
||||
link_params[link_param_cnt++] = "-o";
|
||||
link_params[link_param_cnt++] = linked_file;
|
||||
|
||||
opt_params[0] = alloc_printf("%s/%s", LLVM_BINDIR, "opt");
|
||||
if (getenv("AFL_DONT_OPTIMIZE") == NULL)
|
||||
opt_params[opt_param_cnt++] = "-O3";
|
||||
else
|
||||
opt_params[opt_param_cnt++] = "-O0";
|
||||
|
||||
// opt_params[opt_param_cnt++] = "-S"; // only when debugging
|
||||
opt_params[opt_param_cnt++] = linked_file; // input: .ll file
|
||||
opt_params[opt_param_cnt++] = "-o";
|
||||
opt_params[opt_param_cnt++] = modified_file; // output: .bc file
|
||||
|
||||
inst_params[0] = alloc_printf("%s/%s", LLVM_BINDIR, "opt");
|
||||
inst_params[inst_param_cnt++] =
|
||||
alloc_printf("--load=%s/afl-llvm-lto-instrumentation.so", afl_path);
|
||||
// inst_params[inst_param_cnt++] = "-S"; // only when debugging
|
||||
inst_params[inst_param_cnt++] = "--disable-opt";
|
||||
inst_params[inst_param_cnt++] = "--afl-lto";
|
||||
inst_params[inst_param_cnt++] = modified_file; // input: .bc file
|
||||
inst_params[inst_param_cnt++] = "-o";
|
||||
inst_params[inst_param_cnt++] = final_file; // output: .bc file
|
||||
|
||||
// first we must collect all library search paths
|
||||
for (i = 1; i < argc; i++)
|
||||
if (strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == 'L')
|
||||
libdirs[libdir_cnt++] = argv[i] + 2;
|
||||
|
||||
// then we inspect all options to the target linker
|
||||
for (i = 1; i < argc; i++) {
|
||||
|
||||
if (ld_param_cnt >= MAX_PARAM_COUNT || link_param_cnt >= MAX_PARAM_COUNT)
|
||||
FATAL(
|
||||
"Too many command line parameters because of unpacking .a archives, "
|
||||
"this would need to be done by hand ... sorry! :-(");
|
||||
|
||||
if (strncmp(argv[i], "-flto", 5) == 0) have_lto = 1;
|
||||
|
||||
if (!strcmp(argv[i], "-version")) {
|
||||
|
||||
just_version = 1;
|
||||
ld_params[1] = argv[i];
|
||||
ld_params[2] = NULL;
|
||||
final_file = input_file;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--afl") == 0) {
|
||||
|
||||
if (!be_quiet) OKF("afl++ test command line flag detected, exiting.");
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
||||
// if a -l library is linked and no .so is found but an .a archive is there
|
||||
// then the archive will be used. So we have to emulate this and check
|
||||
// if an archive will be used and if yes we will instrument it too
|
||||
libdir_file[0] = 0;
|
||||
libdir_index = libdir_cnt;
|
||||
if (strncmp(argv[i], "-l", 2) == 0 && libdir_cnt > 0 &&
|
||||
strncmp(argv[i], "-lgcc", 5) != 0) {
|
||||
|
||||
u8 found = 0;
|
||||
|
||||
for (uint32_t j = 0; j < libdir_cnt && !found; j++) {
|
||||
|
||||
snprintf(libdir_file, sizeof(libdir_file), "%s/lib%s%s", libdirs[j],
|
||||
argv[i] + 2, ".so");
|
||||
if (access(libdir_file, R_OK) != 0) { // no .so found?
|
||||
|
||||
snprintf(libdir_file, sizeof(libdir_file), "%s/lib%s%s", libdirs[j],
|
||||
argv[i] + 2, ".a");
|
||||
if (access(libdir_file, R_OK) == 0) { // but .a found?
|
||||
|
||||
libdir_index = j;
|
||||
found = 1;
|
||||
if (debug) SAYF(cMGN "[D] " cRST "Found %s\n", libdir_file);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
found = 1;
|
||||
if (debug) SAYF(cMGN "[D] " cRST "Found %s\n", libdir_file);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// is the parameter an .a AR archive? If so, unpack and check its files
|
||||
if (libdir_index < libdir_cnt ||
|
||||
(argv[i][0] != '-' && strlen(argv[i]) > 2 &&
|
||||
argv[i][strlen(argv[i]) - 1] == 'a' &&
|
||||
argv[i][strlen(argv[i]) - 2] == '.')) {
|
||||
|
||||
// This gets a bit odd. I encountered several .a files being linked and
|
||||
// where the same "foo.o" was in both .a archives. llvm-link does not
|
||||
// like this so we have to work around that ...
|
||||
|
||||
u8 this_wd[4096], *this_ar;
|
||||
u8 ar_params_cnt = 4;
|
||||
u8 * ar_params[ar_params_cnt];
|
||||
u8 * file = argv[i];
|
||||
s32 pid, status;
|
||||
DIR * arx;
|
||||
struct dirent *dir_ent;
|
||||
|
||||
if (libdir_index < libdir_cnt) file = libdir_file;
|
||||
|
||||
if (ar_dir_cnt == 0) { // first archive, we setup up the basics
|
||||
|
||||
ar_dir = alloc_printf("%s/.afl-%u-%u.dir", tmp_dir, getpid(),
|
||||
(u32)time(NULL));
|
||||
if (mkdir(ar_dir, 0700) != 0)
|
||||
FATAL("can not create temporary directory %s", ar_dir);
|
||||
|
||||
}
|
||||
|
||||
if (getcwd(this_wd, sizeof(this_wd)) == NULL)
|
||||
FATAL("can not get the current working directory");
|
||||
if (chdir(ar_dir) != 0)
|
||||
FATAL("can not chdir to temporary directory %s", ar_dir);
|
||||
if (file[0] == '/')
|
||||
this_ar = file;
|
||||
else
|
||||
this_ar = alloc_printf("%s/%s", this_wd, file);
|
||||
ar_params[0] = alloc_printf("%s/%s", LLVM_BINDIR, "llvm-ar");
|
||||
ar_params[1] = "x";
|
||||
ar_params[2] = this_ar;
|
||||
ar_params[3] = NULL;
|
||||
|
||||
if (!be_quiet) OKF("Running ar unpacker on %s into %s", this_ar, ar_dir);
|
||||
|
||||
if (debug) {
|
||||
|
||||
SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd());
|
||||
for (uint32_t j = 0; j < ar_params_cnt; j++)
|
||||
SAYF(" \"%s\"", ar_params[j]);
|
||||
SAYF("\n");
|
||||
|
||||
}
|
||||
|
||||
if (!(pid = fork())) {
|
||||
|
||||
execvp(ar_params[0], (char **)ar_params);
|
||||
FATAL("Oops, failed to execute '%s'", ar_params[0]);
|
||||
|
||||
}
|
||||
|
||||
if (pid < 0) FATAL("fork() failed");
|
||||
if (waitpid(pid, &status, 0) <= 0) FATAL("waitpid() failed");
|
||||
if (WEXITSTATUS(status) != 0) exit(WEXITSTATUS(status));
|
||||
|
||||
if (chdir(this_wd) != 0)
|
||||
FATAL("can not chdir back to our working directory %s", this_wd);
|
||||
|
||||
if (!(arx = opendir(ar_dir))) FATAL("can not open directory %s", ar_dir);
|
||||
|
||||
while ((dir_ent = readdir(arx)) != NULL) {
|
||||
|
||||
u8 *ar_file = alloc_printf("%s/%s", ar_dir, dir_ent->d_name);
|
||||
|
||||
if (dir_ent->d_name[strlen(dir_ent->d_name) - 1] == 'o' &&
|
||||
dir_ent->d_name[strlen(dir_ent->d_name) - 2] == '.') {
|
||||
|
||||
if (passthrough || is_llvm_file(ar_file) == 0) {
|
||||
|
||||
if (is_duplicate(ld_params, ld_param_cnt, ar_file) == 0) {
|
||||
|
||||
ld_params[ld_param_cnt++] = ar_file;
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST "not a LTO link file: %s\n", ar_file);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (is_duplicate(link_params, link_param_cnt, ar_file) == 0) {
|
||||
|
||||
if (we_link == 0) { // we have to honor order ...
|
||||
|
||||
ld_params[ld_param_cnt++] = final_file;
|
||||
we_link = 1;
|
||||
|
||||
}
|
||||
|
||||
link_params[link_param_cnt++] = ar_file;
|
||||
if (debug) SAYF(cMGN "[D] " cRST "is a link file: %s\n", ar_file);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
if (dir_ent->d_name[0] != '.' && !be_quiet)
|
||||
WARNF("Unusual file found in ar archive %s: %s", argv[i], ar_file);
|
||||
|
||||
}
|
||||
|
||||
closedir(arx);
|
||||
ar_dir_cnt++;
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (passthrough || argv[i][0] == '-' || is_llvm_file(argv[i]) == 0) {
|
||||
|
||||
// -O3 fucks up the CFG and instrumentation, so we downgrade to O2
|
||||
// which is as we want things. Lets hope this is not too different
|
||||
// in the various llvm versions!
|
||||
if (strncmp(argv[i], "-plugin-opt=O", 13) == 0 &&
|
||||
!getenv("AFL_DONT_OPTIMIZE"))
|
||||
ld_params[ld_param_cnt++] = "-plugin-opt=O2";
|
||||
else
|
||||
ld_params[ld_param_cnt++] = argv[i];
|
||||
|
||||
} else {
|
||||
|
||||
if (we_link == 0) { // we have to honor order ...
|
||||
ld_params[ld_param_cnt++] = final_file;
|
||||
we_link = 1;
|
||||
|
||||
}
|
||||
|
||||
link_params[link_param_cnt++] = argv[i];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if (have_lto == 0) ld_params[ld_param_cnt++] = AFL_CLANG_FLTO; // maybe we
|
||||
// should not ...
|
||||
ld_params[ld_param_cnt] = NULL;
|
||||
link_params[link_param_cnt] = NULL;
|
||||
opt_params[opt_param_cnt] = NULL;
|
||||
inst_params[inst_param_cnt] = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* clean AFL_PATH from PATH */
|
||||
|
||||
void clean_path() {
|
||||
|
||||
char *tmp, *newpath = NULL, *path = getenv("PATH");
|
||||
u8 done = 0;
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D]" cRST " old PATH=%s, AFL_PATH=%s\n", path, AFL_PATH);
|
||||
|
||||
// wipe AFL paths from PATH that we set
|
||||
// we added two paths so we remove the two paths
|
||||
while (!done) {
|
||||
|
||||
if (*path == 0)
|
||||
done = 1;
|
||||
else if (*path++ == ':')
|
||||
done = 1;
|
||||
|
||||
}
|
||||
|
||||
while (*path == ':')
|
||||
path++;
|
||||
|
||||
// AFL_PATH could be additionally in PATH so check and remove to not call our
|
||||
// 'ld'
|
||||
const size_t pathlen = strlen(path);
|
||||
const size_t afl_pathlen = strlen(AFL_PATH);
|
||||
newpath = malloc(pathlen + 1);
|
||||
if (strcmp(AFL_PATH, "/bin") != 0 && strcmp(AFL_PATH, "/usr/bin") != 0 &&
|
||||
afl_pathlen > 1 && (tmp = strstr(path, AFL_PATH)) != NULL && // it exists
|
||||
(tmp == path ||
|
||||
(tmp > path &&
|
||||
tmp[-1] == ':')) && // either starts with it or has a colon before
|
||||
(tmp + afl_pathlen == path + pathlen ||
|
||||
(tmp + afl_pathlen <
|
||||
path + (pathlen && tmp[afl_pathlen] ==
|
||||
':')) // end with it or has a colon at the end
|
||||
)) {
|
||||
|
||||
int one_colon = 1;
|
||||
|
||||
if (tmp > path) {
|
||||
|
||||
memcpy(newpath, path, tmp - path);
|
||||
newpath[tmp - path - 1] = 0; // remove ':'
|
||||
one_colon = 0;
|
||||
|
||||
}
|
||||
|
||||
if (tmp + afl_pathlen < path + pathlen) tmp += afl_pathlen + one_colon;
|
||||
|
||||
setenv("PATH", newpath, 1);
|
||||
|
||||
} else
|
||||
|
||||
setenv("PATH", path, 1);
|
||||
|
||||
if (debug) SAYF(cMGN "[D]" cRST " new PATH=%s\n", getenv("PATH"));
|
||||
free(newpath);
|
||||
|
||||
}
|
||||
|
||||
/* Main entry point */
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
s32 pid, i;
|
||||
int status;
|
||||
u8 *ptr, exe[4096], exe2[4096], proc[32], val[2] = " ";
|
||||
int have_afl_ld_caller = 0;
|
||||
|
||||
if (isatty(2) && !getenv("AFL_QUIET") && !getenv("AFL_DEBUG")) {
|
||||
|
||||
if (getenv("AFL_LD") != NULL)
|
||||
SAYF(cCYA "afl-ld" VERSION cRST
|
||||
" by Marc \"vanHauser\" Heuse <mh@mh-sec.de> (level %d)\n",
|
||||
have_afl_ld_caller);
|
||||
|
||||
} else
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
if (getenv("AFL_DEBUG") != NULL) debug = 1;
|
||||
if (getenv("AFL_PATH") != NULL) afl_path = getenv("AFL_PATH");
|
||||
if (getenv("AFL_LD_PASSTHROUGH") != NULL) passthrough = 1;
|
||||
if (getenv("AFL_REAL_LD") != NULL) real_ld = getenv("AFL_REAL_LD");
|
||||
if (real_ld == NULL || strlen(real_ld) < 2) real_ld = "/bin/ld";
|
||||
if (real_ld != NULL && real_ld[0] != '/')
|
||||
real_ld = alloc_printf("/bin/%s", real_ld);
|
||||
|
||||
if ((ptr = getenv("AFL_LD_CALLER")) != NULL) have_afl_ld_caller = atoi(ptr);
|
||||
val[0] = 0x31 + have_afl_ld_caller;
|
||||
setenv("AFL_LD_CALLER", val, 1);
|
||||
|
||||
if (debug) {
|
||||
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"AFL_LD=%s, set AFL_LD_CALLER=%s, have_afl_ld_caller=%d, "
|
||||
"real_ld=%s\n",
|
||||
getenv("AFL_LD"), val, have_afl_ld_caller, real_ld);
|
||||
SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd());
|
||||
for (i = 0; i < argc; i++)
|
||||
SAYF(" \"%s\"", argv[i]);
|
||||
SAYF("\n");
|
||||
|
||||
}
|
||||
|
||||
sprintf(proc, "/proc/%d/exe", getpid());
|
||||
if (readlink(proc, exe, sizeof(exe) - 1) > 0) {
|
||||
|
||||
if (readlink(real_ld, exe2, sizeof(exe2) - 1) < 1) exe2[0] = 0;
|
||||
exe[sizeof(exe) - 1] = 0;
|
||||
exe[sizeof(exe2) - 1] = 0;
|
||||
if (strcmp(exe, real_ld) == 0 || strcmp(exe, exe2) == 0)
|
||||
PFATAL(cLRD "[!] " cRST
|
||||
"Error: real 'ld' path points to afl-ld, set AFL_REAL_LD to "
|
||||
"the real 'ld' program!");
|
||||
|
||||
}
|
||||
|
||||
if (have_afl_ld_caller > 1)
|
||||
PFATAL(cLRD "[!] " cRST
|
||||
"Error: afl-ld calls itself in a loop, set AFL_REAL_LD to the "
|
||||
"real 'ld' program!");
|
||||
|
||||
if (argc < 2) {
|
||||
|
||||
SAYF(
|
||||
"\n"
|
||||
"This is a helper application for afl-fuzz. It is a wrapper around GNU "
|
||||
"'ld',\n"
|
||||
"executed by the toolchain whenever using "
|
||||
"afl-clang-lto/afl-clang-lto++.\n"
|
||||
"You probably don't want to run this program directly.\n\n"
|
||||
|
||||
"Environment variables:\n"
|
||||
" AFL_LD_PASSTHROUGH do not link+optimize == no instrumentation\n"
|
||||
" AFL_REAL_LD point to the real ld if necessary\n"
|
||||
|
||||
"\nafl-ld was compiled with the fixed real 'ld' path of %s and the "
|
||||
"clang "
|
||||
"bin path of %s\n\n",
|
||||
real_ld, LLVM_BINDIR);
|
||||
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LD") == NULL) {
|
||||
|
||||
/* if someone install clang/ld into the same directory as afl++ then
|
||||
they are out of luck ... */
|
||||
|
||||
if (have_afl_ld_caller == 1) { clean_path(); }
|
||||
|
||||
if (real_ld != NULL && strlen(real_ld) > 1) execvp(real_ld, argv);
|
||||
execvp("ld", argv); // fallback
|
||||
PFATAL("Oops, failed to execute 'ld' - check your PATH");
|
||||
|
||||
}
|
||||
|
||||
atexit(at_exit_handler); // ensure to wipe temp files if things fail
|
||||
|
||||
edit_params(argc, argv); // here most of the magic happens :-)
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"param counts: ar:%u lib:%u ld:%u link:%u opt:%u instr:%u\n",
|
||||
ar_dir_cnt, libdir_cnt, ld_param_cnt, link_param_cnt, opt_param_cnt,
|
||||
inst_param_cnt);
|
||||
|
||||
if (!just_version) {
|
||||
|
||||
if (we_link == 0) {
|
||||
|
||||
if (!getenv("AFL_QUIET"))
|
||||
WARNF("No LTO input file found, cannot instrument!");
|
||||
|
||||
} else {
|
||||
|
||||
/* first we link all files */
|
||||
if (!be_quiet) OKF("Running bitcode linker, creating %s", linked_file);
|
||||
|
||||
if (debug) {
|
||||
|
||||
SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd());
|
||||
for (i = 0; i < link_param_cnt; i++)
|
||||
SAYF(" \"%s\"", link_params[i]);
|
||||
SAYF("\n");
|
||||
|
||||
}
|
||||
|
||||
if (!(pid = fork())) {
|
||||
|
||||
execvp(link_params[0], (char **)link_params);
|
||||
FATAL("Oops, failed to execute '%s'", link_params[0]);
|
||||
|
||||
}
|
||||
|
||||
if (pid < 0) PFATAL("fork() failed");
|
||||
if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD
|
||||
"\n[-] PROGRAM ABORT : " cRST);
|
||||
SAYF(
|
||||
"llvm-link failed! Probable causes:\n\n"
|
||||
" #1 If the error is \"linking globals named '...': symbol "
|
||||
"multiply defined\"\n"
|
||||
" then there is nothing we can do - llvm-link is missing an "
|
||||
"important feature\n\n"
|
||||
" #2 If the error is \"expected top-level entity\" and then "
|
||||
"binary output, this\n"
|
||||
" is because the same file is present in different .a archives "
|
||||
"in different\n"
|
||||
" formats. This can be fixed by manual doing the steps afl-ld "
|
||||
"is doing but\n"
|
||||
" programmatically - sorry!\n\n");
|
||||
exit(WEXITSTATUS(status));
|
||||
|
||||
}
|
||||
|
||||
/* then we perform an optimization on the collected objects files */
|
||||
if (!be_quiet)
|
||||
OKF("Performing optimization via opt, creating %s", modified_file);
|
||||
if (debug) {
|
||||
|
||||
SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd());
|
||||
for (i = 0; i < opt_param_cnt; i++)
|
||||
SAYF(" \"%s\"", opt_params[i]);
|
||||
SAYF("\n");
|
||||
|
||||
}
|
||||
|
||||
if (!(pid = fork())) {
|
||||
|
||||
execvp(opt_params[0], (char **)opt_params);
|
||||
FATAL("Oops, failed to execute '%s'", opt_params[0]);
|
||||
|
||||
}
|
||||
|
||||
if (pid < 0) PFATAL("fork() failed");
|
||||
if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
|
||||
if (WEXITSTATUS(status) != 0) exit(WEXITSTATUS(status));
|
||||
|
||||
/* then we run the instrumentation through the optimizer */
|
||||
if (!be_quiet)
|
||||
OKF("Performing instrumentation via opt, creating %s", final_file);
|
||||
if (debug) {
|
||||
|
||||
SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd());
|
||||
for (i = 0; i < inst_param_cnt; i++)
|
||||
SAYF(" \"%s\"", inst_params[i]);
|
||||
SAYF("\n");
|
||||
|
||||
}
|
||||
|
||||
if (!(pid = fork())) {
|
||||
|
||||
execvp(inst_params[0], (char **)inst_params);
|
||||
FATAL("Oops, failed to execute '%s'", inst_params[0]);
|
||||
|
||||
}
|
||||
|
||||
if (pid < 0) PFATAL("fork() failed");
|
||||
if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
|
||||
if (WEXITSTATUS(status) != 0) exit(WEXITSTATUS(status));
|
||||
|
||||
}
|
||||
|
||||
/* next step - run the linker! :-) */
|
||||
|
||||
}
|
||||
|
||||
if (!be_quiet) OKF("Running real linker %s", real_ld);
|
||||
if (debug) {
|
||||
|
||||
SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd());
|
||||
for (i = 0; i < ld_param_cnt; i++)
|
||||
SAYF(" \"%s\"", ld_params[i]);
|
||||
SAYF("\n");
|
||||
|
||||
}
|
||||
|
||||
if (!(pid = fork())) {
|
||||
|
||||
clean_path();
|
||||
|
||||
unsetenv("AFL_LD");
|
||||
|
||||
if (strlen(real_ld) > 1) execvp(real_ld, (char **)ld_params);
|
||||
execvp("ld", (char **)ld_params); // fallback
|
||||
FATAL("Oops, failed to execute 'ld' - check your PATH");
|
||||
|
||||
}
|
||||
|
||||
if (pid < 0) PFATAL("fork() failed");
|
||||
|
||||
if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
|
||||
if (debug) SAYF(cMGN "[D] " cRST "linker result: %d\n", status);
|
||||
|
||||
if (!just_version) {
|
||||
|
||||
if (!getenv("AFL_KEEP_ASSEMBLY")) {
|
||||
|
||||
if (linked_file) {
|
||||
|
||||
unlink(linked_file);
|
||||
linked_file = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (modified_file) {
|
||||
|
||||
unlink(modified_file);
|
||||
modified_file = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (final_file) {
|
||||
|
||||
unlink(final_file);
|
||||
final_file = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (ar_dir != NULL) {
|
||||
|
||||
wipe_directory(ar_dir);
|
||||
ar_dir = NULL;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
SAYF(
|
||||
"[!] afl-ld: keeping link file %s, optimized bitcode %s and "
|
||||
"instrumented bitcode %s",
|
||||
linked_file, modified_file, final_file);
|
||||
if (ar_dir_cnt > 0 && ar_dir)
|
||||
SAYF(" and ar archive unpack directory %s", ar_dir);
|
||||
SAYF("\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
|
||||
if (!be_quiet) OKF("Linker was successful");
|
||||
|
||||
} else {
|
||||
|
||||
SAYF(cLRD "[-] " cRST
|
||||
"Linker failed, please investigate and send a bug report. Most "
|
||||
"likely an 'ld' option is incompatible with %s. Try "
|
||||
"AFL_KEEP_ASSEMBLY=1 and AFL_DEBUG=1 for replaying.\n",
|
||||
AFL_CLANG_FLTO);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exit(WEXITSTATUS(status));
|
||||
|
||||
}
|
||||
|
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,18 +12,10 @@
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
// CONFIG OPTION:
|
||||
// If #define USE_SPLIT is used, then the llvm::SplitEdge function is used
|
||||
// instead of our own implementation. Ours looks better and will
|
||||
// compile everywhere. But it is not working for complex code. yet. damn.
|
||||
#define USE_SPLIT
|
||||
|
||||
#define AFL_LLVM_PASS
|
||||
|
||||
#include "config.h"
|
||||
@ -37,38 +24,33 @@
|
||||
#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"
|
||||
#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5
|
||||
typedef long double max_align_t;
|
||||
#endif
|
||||
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||
|
||||
#ifdef USE_SPLIT
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/MemorySSAUpdater.h"
|
||||
#endif
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/Pass.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
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -89,9 +71,10 @@ 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");
|
||||
|
||||
}
|
||||
|
||||
#ifdef USE_SPLIT
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
|
||||
ModulePass::getAnalysisUsage(AU);
|
||||
@ -100,76 +83,14 @@ class AFLLTOPass : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
@ -177,22 +98,19 @@ class AFLLTOPass : public ModulePass {
|
||||
|
||||
bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
LLVMContext &C = M.getContext();
|
||||
LLVMContext & C = M.getContext();
|
||||
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);
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
u32 rand_seed;
|
||||
|
||||
/* Setup random() so we get Actually Random(TM) outputs from AFL_R() */
|
||||
gettimeofday(&tv, &tz);
|
||||
rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
|
||||
AFL_SR(rand_seed);
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
|
||||
|
||||
/* Show a banner */
|
||||
|
||||
if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
|
||||
if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
|
||||
|
||||
SAYF(cCYA "afl-llvm-lto" VERSION cRST
|
||||
" by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n");
|
||||
@ -201,16 +119,69 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
char *neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO");
|
||||
#endif
|
||||
if (getenv("AFL_LLVM_AUTODICTIONARY") ||
|
||||
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);
|
||||
@ -221,17 +192,333 @@ 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;
|
||||
|
||||
#ifdef USE_SPLIT
|
||||
// DominatorTree &DT =
|
||||
// getAnalysis<DominatorTreeWrapperPass>(F).getDomTree(); LoopInfo & LI =
|
||||
// getAnalysis<LoopInfoWrapperPass>(F).getLoopInfo();
|
||||
#endif
|
||||
|
||||
std::vector<BasicBlock *> InsBlocks;
|
||||
|
||||
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 (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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
uint32_t succ = 0;
|
||||
@ -274,11 +561,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
for (uint32_t j = 0; j < Successors.size(); j++) {
|
||||
|
||||
#ifdef USE_SPLIT
|
||||
BasicBlock *newBB = llvm::SplitEdge(origBB, Successors[j]);
|
||||
#else
|
||||
BasicBlock *newBB = BasicBlock::Create(C, "", &F, nullptr);
|
||||
#endif
|
||||
|
||||
if (!newBB) {
|
||||
|
||||
@ -287,12 +570,8 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
#ifdef USE_SPLIT
|
||||
BasicBlock::iterator IP = newBB->getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
#else
|
||||
IRBuilder<> IRB(&(*newBB));
|
||||
#endif
|
||||
|
||||
/* Set the ID of the inserted basic block */
|
||||
|
||||
@ -300,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 */
|
||||
|
||||
@ -313,38 +602,17 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
Value *Incr = IRB.CreateAdd(Counter, One);
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
if (neverZero_counters_str !=
|
||||
NULL) { // with llvm 9 we make this the default as the bug in
|
||||
// llvm is then fixed
|
||||
#endif
|
||||
if (skip_nozero) {
|
||||
|
||||
auto cf = IRB.CreateICmpEQ(Incr, Zero);
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
#ifdef USE_SPLIT
|
||||
// nothing
|
||||
#else
|
||||
|
||||
// Unconditional jump to the destination BB
|
||||
|
||||
IRB.CreateBr(Successors[j]);
|
||||
|
||||
// Replace the original destination to this newly inserted BB
|
||||
|
||||
origBB->replacePhiUsesWith(Successors[j], newBB);
|
||||
BasicBlock *S = Successors[j];
|
||||
S->replacePhiUsesWith(origBB, newBB);
|
||||
TI->setSuccessor(j, newBB);
|
||||
|
||||
#endif
|
||||
// done :)
|
||||
|
||||
inst_blocks++;
|
||||
@ -358,28 +626,151 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
}
|
||||
|
||||
// save highest location ID to global variable
|
||||
|
||||
if (afl_global_id > MAP_SIZE) {
|
||||
// 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++;
|
||||
FATAL(
|
||||
"We have %u blocks to instrument but the map size is only %u! Edit "
|
||||
"config.h and set MAP_SIZE_POW2 from %u to %u, then recompile "
|
||||
"afl-fuzz and llvm_mode.",
|
||||
afl_global_id, MAP_SIZE, MAP_SIZE_POW2, 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") == NULL) {
|
||||
if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) {
|
||||
|
||||
GlobalVariable *AFLFinalLoc = new GlobalVariable(
|
||||
M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc", 0,
|
||||
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
ConstantInt *const_loc = ConstantInt::get(Int32Ty, afl_global_id);
|
||||
AFLFinalLoc->setAlignment(4);
|
||||
AFLFinalLoc->setInitializer(const_loc);
|
||||
// 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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -423,5 +814,5 @@ static RegisterPass<AFLLTOPass> X("afl-lto", "afl++ LTO instrumentation pass",
|
||||
false, false);
|
||||
|
||||
static RegisterStandardPasses RegisterAFLLTOPass(
|
||||
PassManagerBuilder::EP_OptimizerLast, registerAFLLTOPass);
|
||||
PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerAFLLTOPass);
|
||||
|
||||
|
@ -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,59 +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;
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
@ -155,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) {
|
||||
|
||||
@ -192,6 +152,19 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
/*
|
||||
char *ptr;
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/* Decide instrumentation ratio */
|
||||
|
||||
char * inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||
@ -208,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");
|
||||
@ -233,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;
|
||||
|
||||
@ -243,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. */
|
||||
|
||||
@ -267,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__
|
||||
@ -322,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;
|
||||
|
||||
}
|
||||
|
||||
@ -441,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));
|
||||
|
||||
}
|
||||
|
||||
@ -509,7 +364,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
/* Make up cur_loc */
|
||||
|
||||
// cur_loc++;
|
||||
cur_loc = AFL_R(MAP_SIZE);
|
||||
cur_loc = AFL_R(map_size);
|
||||
|
||||
/* There is a problem with Ubuntu 18.04 and llvm 6.0 (see issue #63).
|
||||
The inline function successors() is not inlined and also not found at runtime
|
||||
@ -548,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;
|
||||
@ -572,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 */
|
||||
|
||||
@ -590,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));
|
||||
@ -606,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
|
||||
@ -616,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));
|
||||
|
||||
@ -699,12 +537,78 @@ 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++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// This is currently disabled because we not only need to create/insert a
|
||||
// function (easy), but also add it as a constructor with an ID < 5
|
||||
|
||||
if (getenv("AFL_LLVM_DONTWRITEID") == NULL) {
|
||||
|
||||
// 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.o
|
||||
|
||||
Function *f = ...
|
||||
|
||||
if (!f) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Error: init function could not be created (this should not
|
||||
happen)\n"); exit(-1);
|
||||
|
||||
}
|
||||
|
||||
... constructor for f = 4
|
||||
|
||||
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_size <= 0x800000) {
|
||||
|
||||
GlobalVariable *AFLFinalLoc = new GlobalVariable(
|
||||
M, Int32Ty, true, GlobalValue::ExternalLinkage, 0,
|
||||
"__afl_final_loc");
|
||||
ConstantInt *const_loc = ConstantInt::get(Int32Ty, map_size);
|
||||
StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc);
|
||||
StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/* Say something nice. */
|
||||
|
||||
if (!be_quiet) {
|
||||
|
27
llvm_mode/afl-llvm-rt-lto.o.c
Normal file
27
llvm_mode/afl-llvm-rt-lto.o.c
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
american fuzzy lop++ - LLVM instrumentation bootstrap
|
||||
-----------------------------------------------------
|
||||
|
||||
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>
|
||||
|
||||
// to prevent the function from being removed
|
||||
unsigned char __afl_lto_mode = 0;
|
||||
|
||||
/* Proper initialization routine. */
|
||||
|
||||
__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,18 +43,18 @@
|
||||
#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.
|
||||
Basically, we need to make sure that the forkserver is initialized after
|
||||
the LLVM-generated runtime initialization pass, not before. */
|
||||
|
||||
#ifdef USE_TRACE_PC
|
||||
#define CONST_PRIO 5
|
||||
#else
|
||||
#define CONST_PRIO 0
|
||||
#endif /* ^USE_TRACE_PC */
|
||||
|
||||
#ifndef MAP_FIXED_NOREPLACE
|
||||
#define MAP_FIXED_NOREPLACE MAP_FIXED
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
@ -63,17 +63,25 @@
|
||||
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;
|
||||
#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;
|
||||
#endif
|
||||
@ -84,16 +92,72 @@ 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
|
||||
@ -106,18 +170,35 @@ static void __afl_map_shm(void) {
|
||||
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);
|
||||
|
||||
}
|
||||
@ -126,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);
|
||||
@ -187,8 +290,11 @@ static void __afl_map_shm(void) {
|
||||
#ifdef __linux__
|
||||
static void __afl_start_snapshots(void) {
|
||||
|
||||
static u8 tmp[4];
|
||||
static u8 tmp[4] = {0, 0, 0, 0};
|
||||
s32 child_pid;
|
||||
u32 status = 0;
|
||||
u32 already_read_first = 0;
|
||||
u32 was_killed;
|
||||
|
||||
u8 child_stopped = 0;
|
||||
|
||||
@ -197,16 +303,74 @@ static void __afl_start_snapshots(void) {
|
||||
/* 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. */
|
||||
|
||||
status |= (FS_OPT_ENABLED | FS_OPT_SNAPSHOT);
|
||||
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);
|
||||
|
||||
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
|
||||
|
||||
if (__afl_dictionary_len > 0 && __afl_dictionary) {
|
||||
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
|
||||
|
||||
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) ==
|
||||
(FS_OPT_ENABLED | FS_OPT_AUTODICT)) {
|
||||
|
||||
// great lets pass the dictionary through the forkserver FD
|
||||
u32 len = __afl_dictionary_len, offset = 0;
|
||||
s32 ret;
|
||||
|
||||
if (write(FORKSRV_FD + 1, &len, 4) != 4) {
|
||||
|
||||
write(2, "Error: could not send dictionary len\n",
|
||||
strlen("Error: could not send dictionary len\n"));
|
||||
_exit(1);
|
||||
|
||||
}
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len);
|
||||
|
||||
if (ret < 1) {
|
||||
|
||||
write(2, "Error: could not send dictionary\n",
|
||||
strlen("Error: could not send dictionary\n"));
|
||||
_exit(1);
|
||||
|
||||
}
|
||||
|
||||
len -= ret;
|
||||
offset += ret;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// uh this forkserver master does not understand extended option passing
|
||||
// or does not want the dictionary
|
||||
already_read_first = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
||||
u32 was_killed;
|
||||
int status;
|
||||
|
||||
/* Wait for parent by reading from the pipe. Abort if read fails. */
|
||||
if (already_read_first) {
|
||||
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
|
||||
already_read_first = 0;
|
||||
|
||||
} else {
|
||||
|
||||
/* Wait for parent by reading from the pipe. Abort if read fails. */
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* If we stopped the child in persistent mode, but there was a race
|
||||
condition and afl-fuzz already issued SIGKILL, write off the old
|
||||
@ -291,26 +455,88 @@ static void __afl_start_forkserver(void) {
|
||||
|
||||
#endif
|
||||
|
||||
static u8 tmp[4];
|
||||
s32 child_pid;
|
||||
u8 tmp[4] = {0, 0, 0, 0};
|
||||
s32 child_pid;
|
||||
u32 status = 0;
|
||||
u32 already_read_first = 0;
|
||||
u32 was_killed;
|
||||
|
||||
u8 child_stopped = 0;
|
||||
|
||||
void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
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);
|
||||
|
||||
/* 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) return;
|
||||
|
||||
if (__afl_dictionary_len > 0 && __afl_dictionary) {
|
||||
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
|
||||
|
||||
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) ==
|
||||
(FS_OPT_ENABLED | FS_OPT_AUTODICT)) {
|
||||
|
||||
// great lets pass the dictionary through the forkserver FD
|
||||
u32 len = __afl_dictionary_len, offset = 0;
|
||||
s32 ret;
|
||||
|
||||
if (write(FORKSRV_FD + 1, &len, 4) != 4) {
|
||||
|
||||
write(2, "Error: could not send dictionary len\n",
|
||||
strlen("Error: could not send dictionary len\n"));
|
||||
_exit(1);
|
||||
|
||||
}
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len);
|
||||
|
||||
if (ret < 1) {
|
||||
|
||||
write(2, "Error: could not send dictionary\n",
|
||||
strlen("Error: could not send dictionary\n"));
|
||||
_exit(1);
|
||||
|
||||
}
|
||||
|
||||
len -= ret;
|
||||
offset += ret;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// uh this forkserver master does not understand extended option passing
|
||||
// or does not want the dictionary
|
||||
already_read_first = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
||||
u32 was_killed;
|
||||
int status;
|
||||
|
||||
/* Wait for parent by reading from the pipe. Abort if read fails. */
|
||||
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
|
||||
if (already_read_first) {
|
||||
|
||||
already_read_first = 0;
|
||||
|
||||
} else {
|
||||
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* If we stopped the child in persistent mode, but there was a race
|
||||
condition and afl-fuzz already issued SIGKILL, write off the old
|
||||
@ -390,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));
|
||||
|
||||
@ -513,13 +739,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
|
||||
|
||||
///// CmpLog instrumentation
|
||||
|
||||
void __cmplog_ins_hook1(uint8_t Arg1, uint8_t Arg2) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void __cmplog_ins_hook2(uint16_t Arg1, uint16_t Arg2) {
|
||||
void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) {
|
||||
|
||||
if (!__afl_cmp_map) return;
|
||||
|
||||
@ -534,16 +754,36 @@ void __cmplog_ins_hook2(uint16_t Arg1, uint16_t Arg2) {
|
||||
// if (!__afl_cmp_map->headers[k].cnt)
|
||||
// __afl_cmp_map->headers[k].cnt = __afl_cmp_counter++;
|
||||
|
||||
__afl_cmp_map->headers[k].shape = 1;
|
||||
//__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
__afl_cmp_map->headers[k].shape = 0;
|
||||
|
||||
hits &= CMP_MAP_H - 1;
|
||||
__afl_cmp_map->log[k][hits].v0 = Arg1;
|
||||
__afl_cmp_map->log[k][hits].v1 = Arg2;
|
||||
__afl_cmp_map->log[k][hits].v0 = arg1;
|
||||
__afl_cmp_map->log[k][hits].v1 = arg2;
|
||||
|
||||
}
|
||||
|
||||
void __cmplog_ins_hook4(uint32_t Arg1, uint32_t Arg2) {
|
||||
void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) {
|
||||
|
||||
if (!__afl_cmp_map) return;
|
||||
|
||||
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
k &= CMP_MAP_W - 1;
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
|
||||
u32 hits = __afl_cmp_map->headers[k].hits;
|
||||
__afl_cmp_map->headers[k].hits = hits + 1;
|
||||
|
||||
__afl_cmp_map->headers[k].shape = 1;
|
||||
|
||||
hits &= CMP_MAP_H - 1;
|
||||
__afl_cmp_map->log[k][hits].v0 = arg1;
|
||||
__afl_cmp_map->log[k][hits].v1 = arg2;
|
||||
|
||||
}
|
||||
|
||||
void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) {
|
||||
|
||||
if (!__afl_cmp_map) return;
|
||||
|
||||
@ -559,12 +799,12 @@ void __cmplog_ins_hook4(uint32_t Arg1, uint32_t Arg2) {
|
||||
__afl_cmp_map->headers[k].shape = 3;
|
||||
|
||||
hits &= CMP_MAP_H - 1;
|
||||
__afl_cmp_map->log[k][hits].v0 = Arg1;
|
||||
__afl_cmp_map->log[k][hits].v1 = Arg2;
|
||||
__afl_cmp_map->log[k][hits].v0 = arg1;
|
||||
__afl_cmp_map->log[k][hits].v1 = arg2;
|
||||
|
||||
}
|
||||
|
||||
void __cmplog_ins_hook8(uint64_t Arg1, uint64_t Arg2) {
|
||||
void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) {
|
||||
|
||||
if (!__afl_cmp_map) return;
|
||||
|
||||
@ -580,44 +820,44 @@ void __cmplog_ins_hook8(uint64_t Arg1, uint64_t Arg2) {
|
||||
__afl_cmp_map->headers[k].shape = 7;
|
||||
|
||||
hits &= CMP_MAP_H - 1;
|
||||
__afl_cmp_map->log[k][hits].v0 = Arg1;
|
||||
__afl_cmp_map->log[k][hits].v1 = Arg2;
|
||||
__afl_cmp_map->log[k][hits].v0 = arg1;
|
||||
__afl_cmp_map->log[k][hits].v1 = 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)
|
||||
void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook1")));
|
||||
void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2)
|
||||
void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook2")));
|
||||
void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2)
|
||||
void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook4")));
|
||||
void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2)
|
||||
void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook8")));
|
||||
|
||||
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2)
|
||||
void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook1")));
|
||||
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2)
|
||||
void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook2")));
|
||||
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2)
|
||||
void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook4")));
|
||||
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2)
|
||||
void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2)
|
||||
__attribute__((alias("__cmplog_ins_hook8")));
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
||||
void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
|
||||
|
||||
for (uint64_t i = 0; i < Cases[0]; i++) {
|
||||
for (uint64_t i = 0; i < cases[0]; i++) {
|
||||
|
||||
uintptr_t k = (uintptr_t)__builtin_return_address(0) + i;
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
@ -631,8 +871,8 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
||||
__afl_cmp_map->headers[k].shape = 7;
|
||||
|
||||
hits &= CMP_MAP_H - 1;
|
||||
__afl_cmp_map->log[k][hits].v0 = Val;
|
||||
__afl_cmp_map->log[k][hits].v1 = Cases[i + 2];
|
||||
__afl_cmp_map->log[k][hits].v0 = val;
|
||||
__afl_cmp_map->log[k][hits].v1 = cases[i + 2];
|
||||
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
@ -112,11 +96,12 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
const bool processStrcasecmp,
|
||||
const bool processStrncasecmp) {
|
||||
|
||||
std::vector<CallInst *> calls;
|
||||
LLVMContext & C = M.getContext();
|
||||
IntegerType * Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType * Int32Ty = IntegerType::getInt32Ty(C);
|
||||
IntegerType * Int64Ty = IntegerType::getInt64Ty(C);
|
||||
DenseMap<Value *, std::string *> valueMap;
|
||||
std::vector<CallInst *> calls;
|
||||
LLVMContext & C = M.getContext();
|
||||
IntegerType * Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType * Int32Ty = IntegerType::getInt32Ty(C);
|
||||
IntegerType * Int64Ty = IntegerType::getInt64Ty(C);
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
Constant *
|
||||
@ -139,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;
|
||||
@ -263,6 +139,8 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
bool isStrncmp = processStrncmp;
|
||||
bool isStrcasecmp = processStrcasecmp;
|
||||
bool isStrncasecmp = processStrncasecmp;
|
||||
bool isIntMemcpy = true;
|
||||
bool indirect = false;
|
||||
|
||||
Function *Callee = callInst->getCalledFunction();
|
||||
if (!Callee) continue;
|
||||
@ -273,9 +151,10 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
isStrncmp &= !FuncName.compare(StringRef("strncmp"));
|
||||
isStrcasecmp &= !FuncName.compare(StringRef("strcasecmp"));
|
||||
isStrncasecmp &= !FuncName.compare(StringRef("strncasecmp"));
|
||||
isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64");
|
||||
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||
!isStrncasecmp)
|
||||
!isStrncasecmp && !isIntMemcpy)
|
||||
continue;
|
||||
|
||||
/* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function
|
||||
@ -309,7 +188,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||
!isStrncasecmp)
|
||||
!isStrncasecmp && !isIntMemcpy)
|
||||
continue;
|
||||
|
||||
/* is a str{n,}{case,}cmp/memcmp, check if we have
|
||||
@ -322,8 +201,107 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||
bool HasStr2 = getConstantStringInfo(Str2P, Str2);
|
||||
|
||||
if (isIntMemcpy && HasStr2) {
|
||||
|
||||
valueMap[Str1P] = new std::string(Str2.str());
|
||||
// fprintf(stderr, "saved %s for %p\n", Str2.str().c_str(), Str1P);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
// not literal? maybe global or local variable
|
||||
if (!(HasStr1 || 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();
|
||||
valueMap[Str2P] = new std::string(Str2.str());
|
||||
fprintf(stderr, "glo2 %s\n", Str2.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!HasStr2) {
|
||||
|
||||
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();
|
||||
valueMap[Str1P] = new std::string(Str1.str());
|
||||
// fprintf(stderr, "glo1 %s\n", Str1.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (isIntMemcpy) {
|
||||
|
||||
valueMap[Str1P] = new std::string(Str2.str());
|
||||
// fprintf(stderr, "saved\n");
|
||||
|
||||
}
|
||||
|
||||
if ((HasStr1 || HasStr2)) indirect = true;
|
||||
|
||||
}
|
||||
|
||||
if (isIntMemcpy) continue;
|
||||
|
||||
if (!(HasStr1 || HasStr2)) {
|
||||
|
||||
// do we have a saved local variable initialization?
|
||||
std::string *val = valueMap[Str1P];
|
||||
if (val && !val->empty()) {
|
||||
|
||||
Str1 = StringRef(*val);
|
||||
HasStr1 = true;
|
||||
indirect = true;
|
||||
// fprintf(stderr, "loaded1 %s\n", Str1.str().c_str());
|
||||
|
||||
} else {
|
||||
|
||||
val = valueMap[Str2P];
|
||||
if (val && !val->empty()) {
|
||||
|
||||
Str2 = StringRef(*val);
|
||||
HasStr2 = true;
|
||||
indirect = true;
|
||||
// fprintf(stderr, "loaded2 %s\n", Str2.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* handle cases of one string is const, one string is variable */
|
||||
if (!(HasStr1 ^ HasStr2)) continue;
|
||||
if (!(HasStr1 || HasStr2)) continue;
|
||||
|
||||
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
||||
|
||||
@ -334,9 +312,8 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
if (!ilen) continue;
|
||||
/* final precaution: if size of compare is larger than constant
|
||||
* string skip it*/
|
||||
uint64_t literalLength =
|
||||
HasStr1 ? GetStringLength(Str1P) : GetStringLength(Str2P);
|
||||
if (literalLength < ilen->getZExtValue()) continue;
|
||||
uint64_t literalLength = HasStr1 ? Str1.size() : Str2.size();
|
||||
if (literalLength + 1 < ilen->getZExtValue()) continue;
|
||||
|
||||
}
|
||||
|
||||
@ -363,9 +340,9 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
std::string TmpConstStr;
|
||||
Value * VarStr;
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||
getConstantStringInfo(Str2P, Str2);
|
||||
uint64_t constLen, sizedLen;
|
||||
bool isMemcmp =
|
||||
bool HasStr2 = getConstantStringInfo(Str2P, Str2);
|
||||
uint64_t constLen, sizedLen;
|
||||
bool isMemcmp =
|
||||
!callInst->getCalledFunction()->getName().compare(StringRef("memcmp"));
|
||||
bool isSizedcmp = isMemcmp ||
|
||||
!callInst->getCalledFunction()->getName().compare(
|
||||
@ -389,17 +366,40 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
|
||||
}
|
||||
|
||||
if (!(HasStr1 || HasStr2)) {
|
||||
|
||||
// do we have a saved local or global variable initialization?
|
||||
std::string *val = valueMap[Str1P];
|
||||
if (val && !val->empty()) {
|
||||
|
||||
Str1 = StringRef(*val);
|
||||
HasStr1 = true;
|
||||
|
||||
} else {
|
||||
|
||||
val = valueMap[Str2P];
|
||||
if (val && !val->empty()) {
|
||||
|
||||
Str2 = StringRef(*val);
|
||||
HasStr2 = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (HasStr1) {
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
@ -407,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
|
||||
@ -500,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;
|
||||
|
@ -52,6 +52,6 @@ ${compiler_prefix}${CC} -shared -o libdemo.so demo-so.c -w -g
|
||||
echo "[+] Building afl-fuzz for Android"
|
||||
# build afl-fuzz
|
||||
cd ..
|
||||
${compiler_prefix}${CC} -DANDROID_DISABLE_FANCY=1 -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ -DAFL_PATH=\"/usr/local/lib/afl\" -DBIN_PATH=\"/usr/local/bin\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -Wno-unused-function src/afl-fuzz-misc.c src/afl-fuzz-extras.c src/afl-fuzz-queue.c src/afl-fuzz-one.c src/afl-fuzz-python.c src/afl-fuzz-stats.c src/afl-fuzz-init.c src/afl-fuzz.c src/afl-fuzz-bitmap.c src/afl-fuzz-run.c src/afl-fuzz-state.c src/afl-common.c src/afl-sharedmem.c src/afl-forkserver.c -o qbdi_mode/afl-fuzz -ldl -w
|
||||
${compiler_prefix}${CC} -DANDROID_DISABLE_FANCY=1 -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ -DAFL_PATH=\"/usr/local/lib/afl\" -DBIN_PATH=\"/usr/local/bin\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -Wno-unused-function src/afl-fuzz-*.c src/afl-fuzz.c src/afl-common.c src/afl-sharedmem.c src/afl-forkserver.c -o qbdi_mode/afl-fuzz -ldl -w
|
||||
|
||||
echo "[+] All done. Enjoy!"
|
||||
|
@ -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,10 +249,12 @@ 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
|
||||
|
||||
echo "[*] Comparing two afl-showmap -Q outputs..."
|
||||
echo 0 | ./afl-showmap -m none -Q -q -o .test-instr0 ./test-instr || exit 1
|
||||
echo 1 | ./afl-showmap -m none -Q -q -o .test-instr1 ./test-instr || exit 1
|
||||
|
||||
@ -267,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,30 +274,17 @@ 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) {
|
||||
|
||||
static unsigned char tmp[4];
|
||||
u32 map_size = 0;
|
||||
unsigned char tmp[4] = {0};
|
||||
|
||||
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
|
||||
|
||||
@ -306,6 +292,15 @@ void afl_forkserver(CPUState *cpu) {
|
||||
int t_fd[2];
|
||||
u8 child_stopped = 0;
|
||||
|
||||
// if in the future qemu has non-collding coverage then switch MAP_SIZE
|
||||
// with the max ID value
|
||||
if (MAP_SIZE <= 0x800000) {
|
||||
|
||||
map_size = (FS_OPT_ENABLED | FS_OPT_MAPSIZE | FS_OPT_SET_MAPSIZE(MAP_SIZE));
|
||||
memcpy(tmp, &map_size, 4);
|
||||
|
||||
}
|
||||
|
||||
/* Tell the parent that we're alive. If the parent doesn't want
|
||||
to talk, assume that we're not running in forkserver mode. */
|
||||
|
||||
|
@ -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,
|
||||
@ -53,12 +53,22 @@ static void afl_gen_compcov(target_ulong cur_loc, TCGv arg1, TCGv arg2,
|
||||
|
||||
TCGv cur_loc_v = tcg_const_tl(cur_loc);
|
||||
|
||||
switch (ot) {
|
||||
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;
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
@ -75,12 +85,19 @@ static void afl_gen_compcov(target_ulong cur_loc, TCGv arg1, TCGv arg2,
|
||||
|
||||
if (cur_loc >= afl_inst_rms) return;
|
||||
|
||||
switch (ot) {
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -107,18 +107,39 @@ void HELPER(afl_compcov_64)(target_ulong cur_loc, target_ulong arg1,
|
||||
|
||||
}
|
||||
|
||||
void HELPER(afl_cmplog_8)(target_ulong cur_loc, target_ulong arg1,
|
||||
target_ulong arg2) {
|
||||
|
||||
register uintptr_t k = (uintptr_t)cur_loc;
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
|
||||
u32 hits = __afl_cmp_map->headers[k].hits;
|
||||
__afl_cmp_map->headers[k].hits = hits + 1;
|
||||
// if (!__afl_cmp_map->headers[k].cnt)
|
||||
// __afl_cmp_map->headers[k].cnt = __afl_cmp_counter++;
|
||||
|
||||
__afl_cmp_map->headers[k].shape = 0;
|
||||
|
||||
hits &= CMP_MAP_H - 1;
|
||||
__afl_cmp_map->log[k][hits].v0 = arg1;
|
||||
__afl_cmp_map->log[k][hits].v1 = arg2;
|
||||
|
||||
}
|
||||
|
||||
void HELPER(afl_cmplog_16)(target_ulong cur_loc, target_ulong arg1,
|
||||
target_ulong arg2) {
|
||||
|
||||
register uintptr_t k = (uintptr_t)cur_loc;
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
|
||||
u32 hits = __afl_cmp_map->headers[k].hits;
|
||||
__afl_cmp_map->headers[k].hits = hits + 1;
|
||||
// if (!__afl_cmp_map->headers[k].cnt)
|
||||
// __afl_cmp_map->headers[k].cnt = __afl_cmp_counter++;
|
||||
|
||||
__afl_cmp_map->headers[k].shape = 1;
|
||||
//__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
|
||||
hits &= CMP_MAP_H - 1;
|
||||
__afl_cmp_map->log[k][hits].v0 = arg1;
|
||||
@ -131,6 +152,8 @@ void HELPER(afl_cmplog_32)(target_ulong cur_loc, target_ulong arg1,
|
||||
|
||||
register uintptr_t k = (uintptr_t)cur_loc;
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
|
||||
u32 hits = __afl_cmp_map->headers[k].hits;
|
||||
__afl_cmp_map->headers[k].hits = hits + 1;
|
||||
|
||||
@ -147,6 +170,8 @@ void HELPER(afl_cmplog_64)(target_ulong cur_loc, target_ulong arg1,
|
||||
|
||||
register uintptr_t k = (uintptr_t)cur_loc;
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
|
||||
u32 hits = __afl_cmp_map->headers[k].hits;
|
||||
__afl_cmp_map->headers[k].hits = hits + 1;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -2,7 +2,7 @@ diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h
|
||||
index 1bd39d13..81ef3973 100644
|
||||
--- a/accel/tcg/tcg-runtime.h
|
||||
+++ b/accel/tcg/tcg-runtime.h
|
||||
@@ -260,3 +260,13 @@ DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
@@ -260,3 +260,14 @@ DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
@ -12,6 +12,7 @@ index 1bd39d13..81ef3973 100644
|
||||
+DEF_HELPER_FLAGS_3(afl_compcov_16, TCG_CALL_NO_RWG, void, tl, tl, tl)
|
||||
+DEF_HELPER_FLAGS_3(afl_compcov_32, TCG_CALL_NO_RWG, void, tl, tl, tl)
|
||||
+DEF_HELPER_FLAGS_3(afl_compcov_64, TCG_CALL_NO_RWG, void, tl, tl, tl)
|
||||
+DEF_HELPER_FLAGS_3(afl_cmplog_8, TCG_CALL_NO_RWG, void, tl, tl, tl)
|
||||
+DEF_HELPER_FLAGS_3(afl_cmplog_16, TCG_CALL_NO_RWG, void, tl, tl, tl)
|
||||
+DEF_HELPER_FLAGS_3(afl_cmplog_32, TCG_CALL_NO_RWG, void, tl, tl, tl)
|
||||
+DEF_HELPER_FLAGS_3(afl_cmplog_64, TCG_CALL_NO_RWG, void, tl, tl, tl)
|
||||
|
@ -1,34 +0,0 @@
|
||||
#
|
||||
# american fuzzy lop++ - unsigaction
|
||||
# --------------------------------
|
||||
#
|
||||
# Written by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
#
|
||||
# Copyright 2019-2020 Andrea Fioraldi. 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
|
||||
#
|
||||
|
||||
ifndef AFL_NO_X86
|
||||
|
||||
all: lib_i386 lib_amd64
|
||||
|
||||
lib_i386:
|
||||
@$(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
|
||||
|
||||
lib_amd64:
|
||||
$(CC) -fPIC -shared unsigaction.c -o unsigaction64.so
|
||||
|
||||
clean:
|
||||
rm -f unsigaction32.so unsigaction64.so
|
||||
|
||||
else
|
||||
|
||||
all:
|
||||
@echo "[!] Note: skipping compilation of unsigaction (AFL_NO_X86 set)."
|
||||
|
||||
endif
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user