mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-24 14:43:22 +00:00
Compare commits
488 Commits
Author | SHA1 | Date | |
---|---|---|---|
ee206da389 | |||
fac108476c | |||
4f7a8a4c70 | |||
976ee9022c | |||
0625eb0a05 | |||
77b824d101 | |||
b7b38205d8 | |||
6c715f1a69 | |||
fc19aa96f7 | |||
50f61b64b1 | |||
809a7cffe2 | |||
6399f84ba2 | |||
8459bcdf85 | |||
6adaacbb3a | |||
6c846bcf2c | |||
e45ae8e5da | |||
cea2fadbf4 | |||
4c48d3a3ad | |||
020b8a4964 | |||
08f6e1d66a | |||
28e457e8d8 | |||
c7255f2e26 | |||
6340674a23 | |||
4538f689ed | |||
e4a86b40a5 | |||
75c38d6243 | |||
6f75100602 | |||
07cee6b750 | |||
651ad18e21 | |||
664daa2f3c | |||
ed6243df5a | |||
bd57784664 | |||
7f621509ee | |||
4261e17b3e | |||
8ca4414d70 | |||
6090bb1bca | |||
a552631d3b | |||
c552229c4d | |||
2dffed1cff | |||
e93f78eca5 | |||
9bbbec3fa8 | |||
338638b124 | |||
17e1a72b3b | |||
3e6471b949 | |||
e4de4e3500 | |||
bea76b346c | |||
53e63e9ded | |||
b1b5e21600 | |||
d765740707 | |||
192cadee34 | |||
d7d8afa512 | |||
01fcee1190 | |||
0805437222 | |||
4398b9b517 | |||
909262f6c5 | |||
155ef8875a | |||
58cf030546 | |||
18ea9a8447 | |||
ebd1e6bc4b | |||
45d866d548 | |||
8087cf7988 | |||
9e8b3f13e1 | |||
ce4700ca6e | |||
8253f90134 | |||
86421f3469 | |||
811ef13b20 | |||
7fb72f1038 | |||
d2c9e4baa7 | |||
81767287c3 | |||
6c980e2a02 | |||
e7db4d4fe0 | |||
567042d146 | |||
4697e4a5a5 | |||
92b1f9cf36 | |||
bbf00204ea | |||
a42b74b624 | |||
7ee255cbcf | |||
961ddfd7f8 | |||
4566bcf122 | |||
ca0105ddf6 | |||
41bb359428 | |||
146ede0f29 | |||
c0fd7ba6d1 | |||
b0b2a15891 | |||
ff3c9cbd73 | |||
6e839f0f6a | |||
a3cd523250 | |||
b44620f0b0 | |||
9a6a32775f | |||
3e8beaafc8 | |||
33e58c1d4e | |||
4be0ea596b | |||
96ef7083c8 | |||
78eaa6b203 | |||
1efc6e59b7 | |||
19eddbb0c7 | |||
6a34c5aa3e | |||
c7f0d30668 | |||
a7c3f252d5 | |||
b9b6f06429 | |||
a1442bd1ac | |||
4d9d52e3d9 | |||
6184832ea9 | |||
e2b54bfa05 | |||
425908a00c | |||
1301552101 | |||
c4f71ab201 | |||
42ef1968a5 | |||
5ec91fce23 | |||
47878f6974 | |||
d5c77a9e96 | |||
4d2694c114 | |||
017c8a6940 | |||
b0a783e86f | |||
714e4d2b46 | |||
85a4c5e724 | |||
182b8b3e14 | |||
4ce5ed370a | |||
f7bac482e9 | |||
bd074e9150 | |||
d52ea44c27 | |||
9c1b6cfb99 | |||
631d3f274a | |||
3cdaf4dcf2 | |||
572944d726 | |||
779d8f6b7e | |||
322847755a | |||
f9f28b9c7c | |||
c3bc0145e7 | |||
17d403b8f8 | |||
9faf7b6fc8 | |||
5c759953f4 | |||
1c64048d0f | |||
b504b9313a | |||
1a94cfe2af | |||
7470b475a9 | |||
0a6084f361 | |||
f92607cff1 | |||
9532499ef5 | |||
1d56de6c1d | |||
266b51a842 | |||
cc1fe2f2d2 | |||
43214d6b46 | |||
2f28ecd3a5 | |||
73a629d6f2 | |||
0a251f93e0 | |||
1cf4738487 | |||
af14acf2c1 | |||
a7537b5511 | |||
15e799f7ae | |||
5f0a9c90c8 | |||
9ff9ff2ad2 | |||
d86b13384f | |||
17a4e9fadf | |||
ce513c4f3e | |||
ce92adcb9b | |||
e94cc1fae0 | |||
32fe047894 | |||
d1bc0207cc | |||
69f8c62955 | |||
83df65a66b | |||
c3a6e7e870 | |||
d0ab83a202 | |||
b5d1a021ef | |||
e9fb5f4cbc | |||
212bb990b7 | |||
8e984c2aa0 | |||
7f435ec5f1 | |||
47faf3dd33 | |||
c4e52e20c9 | |||
2c5e103278 | |||
7a6867e2f8 | |||
8044ae28be | |||
b38837f4ff | |||
c25479264d | |||
e9b3da5d96 | |||
132b57cf03 | |||
ee548df05f | |||
052d74b16c | |||
83281503b3 | |||
b604f5eafc | |||
220dc4a43d | |||
457f627101 | |||
4f695b6f4c | |||
3ec1b23743 | |||
0ba09ee85a | |||
67dac15226 | |||
9cf8637fab | |||
50e76fce12 | |||
432638404f | |||
1e38c10efb | |||
701fb95d24 | |||
7b5a18428e | |||
7d7a8c7c39 | |||
a422fcaa40 | |||
fee58a4d1b | |||
3ecafde29d | |||
8428b18d2a | |||
9c953ab51f | |||
f181a8307b | |||
84b9d551fd | |||
8f8555dfdf | |||
464c27082a | |||
3aa7d8081d | |||
fb84103ffb | |||
c270646722 | |||
87da1e7af6 | |||
9b71f7e5e4 | |||
9945c1648b | |||
e5d24827de | |||
a6521e89fc | |||
5e36fb32a8 | |||
fb0181f5bc | |||
6fa2c213ef | |||
9ec223c844 | |||
558a82891a | |||
4fc16b542e | |||
ff40359a60 | |||
e99d7e9730 | |||
b60663c031 | |||
32db31b555 | |||
a1129b67c2 | |||
8a1cf3f0f9 | |||
0bb59ba116 | |||
e4a0237cbc | |||
d8f5502d83 | |||
45d0e4765e | |||
9a1d526ed4 | |||
ebc6f52868 | |||
a19b31bf82 | |||
28251a495a | |||
f4592a8fb4 | |||
b29d91edf5 | |||
986af28df2 | |||
27abecbff5 | |||
33141cf8a3 | |||
8551d8e48e | |||
32558bc807 | |||
934cdc32f4 | |||
699ebaa8e2 | |||
44ad516edd | |||
fd9a7e719d | |||
e51f1ea5a5 | |||
22d3a5e90a | |||
673ace2a4b | |||
4a6d66d8c5 | |||
1978629d87 | |||
6b1ad311da | |||
5b06166144 | |||
a0fab35bbf | |||
420b202124 | |||
fb14e55cc9 | |||
e2434cf8c6 | |||
d94681186d | |||
58a710d192 | |||
716eb226b2 | |||
cb3631a322 | |||
bd1d148f83 | |||
7e0c9a36ef | |||
bbfff7d472 | |||
e048d95660 | |||
970d75d681 | |||
51f3a81037 | |||
8190436f8f | |||
08bcaa135f | |||
c4e5f75728 | |||
1064c7114e | |||
0281872ddf | |||
c6bf23377d | |||
2d650f8c22 | |||
19631851f6 | |||
f30ca1476c | |||
0712d44cbc | |||
15f3210d93 | |||
9864d9c189 | |||
bd36aac60a | |||
4a859aff70 | |||
8fc727e597 | |||
585ec04503 | |||
a1c93f24d4 | |||
f6c89ec3a9 | |||
3d8f054580 | |||
6d364dd2cb | |||
8ed6207b5c | |||
c8354d7516 | |||
79f873a597 | |||
8850e1a5bf | |||
194188fe56 | |||
cc74efa35e | |||
e7f2770275 | |||
af277a0b56 | |||
4163f47e09 | |||
b2aa8b03d9 | |||
e1d20706ca | |||
76888fdf59 | |||
e6e38d1703 | |||
44060590b4 | |||
38bed607d1 | |||
ed63364a77 | |||
55bd24b0c7 | |||
f18c2eb8ae | |||
898353c87a | |||
d5d8d664d0 | |||
409e4ae945 | |||
f335c48686 | |||
9d82c3cf5e | |||
491cee669f | |||
e0d1529061 | |||
1cddd51662 | |||
6041b1c486 | |||
349fed3fcd | |||
b708cf7d45 | |||
a267ff1ab5 | |||
8e0c776137 | |||
4512377fa1 | |||
9439ba1dac | |||
9c9c4a6b2b | |||
6efe51a8a7 | |||
593940c39a | |||
8ea19d4266 | |||
b7bcc50c61 | |||
e939677726 | |||
ca17ec3fe9 | |||
54d9668580 | |||
16b674c652 | |||
25ad992c62 | |||
37f1b7cddb | |||
729445b64f | |||
185f443659 | |||
c101a3f5ab | |||
cf9cb73afe | |||
071fcac430 | |||
a74ec89461 | |||
630d2a934b | |||
d5758c138b | |||
149b0021b7 | |||
68f46f6178 | |||
cd576fa59d | |||
320f26d26f | |||
c661587128 | |||
486e5365d9 | |||
8e809d8593 | |||
ea9ba53cdb | |||
1ba48a5ba0 | |||
7cb00b69f0 | |||
cbe8f0a9d0 | |||
da8b464e67 | |||
13350bf22f | |||
5ce55d87ec | |||
fc401f1acc | |||
fe39e4dfdf | |||
49b77207dd | |||
35a448ee92 | |||
3f9f00a798 | |||
ffe5619a9d | |||
3b194e1690 | |||
45b6508339 | |||
22921c493f | |||
f32811922e | |||
6cfa27d78a | |||
8e3ca8eaa9 | |||
4550613f58 | |||
015fde3703 | |||
827ecd61f6 | |||
565da10a8f | |||
d64c0e8887 | |||
0b8c44cbb1 | |||
a22f4dd1ac | |||
952e5b47eb | |||
b3f5b566b0 | |||
0b3332d579 | |||
a76e375d5c | |||
8b21c2e472 | |||
23718e5198 | |||
031aa240bc | |||
7944009a65 | |||
4eb06bb54b | |||
bd5308d839 | |||
b508532c78 | |||
fb9888a068 | |||
11f25747a9 | |||
8ebed3471f | |||
85684cd8b7 | |||
2585a33005 | |||
1bbeef48e1 | |||
7f3317110e | |||
298ff5c7d0 | |||
c3f65bff5b | |||
2323c30b5b | |||
80f4b32f0b | |||
16e362d2b9 | |||
23da490f26 | |||
ff107714f1 | |||
7e4703c328 | |||
ae41cedafe | |||
a879f72131 | |||
131df8bec9 | |||
89557d1607 | |||
7959808384 | |||
ecb0601bc1 | |||
30c0991543 | |||
9cddbc0420 | |||
2fa31dab60 | |||
4898db80cb | |||
aa3856261d | |||
3e04dbd5a1 | |||
72b46a07d6 | |||
2ba88dcd8a | |||
1ddb70e0d9 | |||
024a88a6bb | |||
af10c05ac3 | |||
a46a733dbe | |||
b015e4f07a | |||
44928a0265 | |||
d90328f6be | |||
ce9b4698fe | |||
9a33a29b4a | |||
b6e65f9882 | |||
6c163910ee | |||
9151cb9ba2 | |||
204ae75d7b | |||
f2d9b0096e | |||
67d2e6319b | |||
5e10f660e8 | |||
0da0b5cba0 | |||
67d7c364f6 | |||
67d58e2437 | |||
c2b04bdf6c | |||
6513bca07e | |||
0b0366d9b4 | |||
f465a75b65 | |||
4314e59af9 | |||
a84c958647 | |||
1ec2615a3e | |||
2077309c8d | |||
08d3169df4 | |||
3cc0445e27 | |||
ee77fe4094 | |||
133dfc8b69 | |||
a8726b8254 | |||
c5963f707c | |||
383b280531 | |||
95276f7da6 | |||
e1d4621796 | |||
e137b40eb5 | |||
4d929f80fb | |||
6b79e1f76d | |||
5a26656ea1 | |||
abb0d47985 | |||
b126a5d5a8 | |||
571031a467 | |||
2981f2025f | |||
c3a6065a21 | |||
60bb1afc72 | |||
84a320f834 | |||
88bd460100 | |||
90adc2cb85 | |||
7c8d823396 | |||
83790d65af | |||
70bd0f799d | |||
cbe029664e | |||
cade0214db | |||
2f5cdb72c8 | |||
0aed549df1 | |||
75fa1ac3b0 | |||
b5a00312e0 | |||
37697127dc | |||
8acc8b5389 | |||
8644c42482 | |||
20e63078f0 | |||
95fd080ca1 | |||
7d0af01d8b | |||
0f0230b068 | |||
869c602b99 | |||
3144f72e1c | |||
147b0a151c | |||
29102d6bf1 | |||
4fd145c52e | |||
e6d4d29af5 | |||
139665c01d | |||
509b991607 | |||
c671ecb511 | |||
1aa7c87ea8 | |||
00abb999e3 | |||
fc5cfc6cb3 | |||
25c3a29004 | |||
118cc88429 | |||
0dc9967984 |
@ -32,7 +32,8 @@ if CLANG_FORMAT_BIN is None:
|
||||
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 = re.sub(r".*ersion ", "", o)
|
||||
#o = o[len("clang-format version "):].strip()
|
||||
o = o[:o.find(".")]
|
||||
o = int(o)
|
||||
except:
|
||||
|
65
.dockerignore
Normal file
65
.dockerignore
Normal file
@ -0,0 +1,65 @@
|
||||
.test
|
||||
.test2
|
||||
.sync_tmp
|
||||
*.o
|
||||
*.so
|
||||
*.pyc
|
||||
*.dSYM
|
||||
as
|
||||
ld
|
||||
in
|
||||
out
|
||||
core*
|
||||
afl-analyze
|
||||
afl-as
|
||||
afl-clang
|
||||
afl-clang\+\+
|
||||
afl-clang-fast
|
||||
afl-clang-fast\+\+
|
||||
afl-clang-lto
|
||||
afl-clang-lto\+\+
|
||||
afl-fuzz
|
||||
afl-g\+\+
|
||||
afl-gcc
|
||||
afl-gcc-fast
|
||||
afl-g\+\+-fast
|
||||
afl-gotcpu
|
||||
afl-ld
|
||||
afl-ld-lto
|
||||
afl-qemu-trace
|
||||
afl-showmap
|
||||
afl-tmin
|
||||
afl-analyze.8
|
||||
afl-as.8
|
||||
afl-clang-fast\+\+.8
|
||||
afl-clang-fast.8
|
||||
afl-clang-lto.8
|
||||
afl-clang-lto\+\+.8
|
||||
afl-cmin.8
|
||||
afl-cmin.bash.8
|
||||
afl-fuzz.8
|
||||
afl-gcc.8
|
||||
afl-gcc-fast.8
|
||||
afl-g\+\+-fast.8
|
||||
afl-gotcpu.8
|
||||
afl-plot.8
|
||||
afl-showmap.8
|
||||
afl-system-config.8
|
||||
afl-tmin.8
|
||||
afl-whatsup.8
|
||||
qemu_mode/libcompcov/compcovtest
|
||||
qemu_mode/qemu-*
|
||||
unicorn_mode/samples/*/\.test-*
|
||||
unicorn_mode/samples/*/output
|
||||
unicorn_mode/unicornafl
|
||||
test/unittests/unit_maybe_alloc
|
||||
test/unittests/unit_preallocable
|
||||
test/unittests/unit_list
|
||||
test/unittests/unit_rand
|
||||
test/unittests/unit_hash
|
||||
examples/afl_network_proxy/afl-network-server
|
||||
examples/afl_network_proxy/afl-network-client
|
||||
examples/afl_frida/afl-frida
|
||||
examples/afl_frida/libtestinstr.so
|
||||
examples/afl_frida/frida-gum-example.c
|
||||
examples/afl_frida/frida-gum.h
|
18
.gitignore
vendored
18
.gitignore
vendored
@ -1,8 +1,18 @@
|
||||
.test
|
||||
.test2
|
||||
.sync_tmp
|
||||
.vscode
|
||||
*.o
|
||||
*.so
|
||||
*.swp
|
||||
*.pyc
|
||||
*.dSYM
|
||||
as
|
||||
ld
|
||||
in
|
||||
out
|
||||
core*
|
||||
compile_commands.json
|
||||
afl-analyze
|
||||
afl-as
|
||||
afl-clang
|
||||
@ -32,6 +42,7 @@ afl-cmin.8
|
||||
afl-cmin.bash.8
|
||||
afl-fuzz.8
|
||||
afl-gcc.8
|
||||
afl-g++.8
|
||||
afl-gcc-fast.8
|
||||
afl-g++-fast.8
|
||||
afl-gotcpu.8
|
||||
@ -41,13 +52,10 @@ afl-system-config.8
|
||||
afl-tmin.8
|
||||
afl-whatsup.8
|
||||
qemu_mode/libcompcov/compcovtest
|
||||
as
|
||||
ld
|
||||
qemu_mode/qemu-*
|
||||
unicorn_mode/samples/*/\.test-*
|
||||
unicorn_mode/samples/*/output/
|
||||
unicorn_mode/unicornafl
|
||||
core\.*
|
||||
test/unittests/unit_maybe_alloc
|
||||
test/unittests/unit_preallocable
|
||||
test/unittests/unit_list
|
||||
@ -55,3 +63,7 @@ test/unittests/unit_rand
|
||||
test/unittests/unit_hash
|
||||
examples/afl_network_proxy/afl-network-server
|
||||
examples/afl_network_proxy/afl-network-client
|
||||
examples/afl_frida/afl-frida
|
||||
examples/afl_frida/libtestinstr.so
|
||||
examples/afl_frida/frida-gum-example.c
|
||||
examples/afl_frida/frida-gum.h
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,3 +1,7 @@
|
||||
[submodule "unicorn_mode/unicornafl"]
|
||||
path = unicorn_mode/unicornafl
|
||||
url = https://github.com/AFLplusplus/unicornafl.git
|
||||
url = https://github.com/AFLplusplus/unicornafl
|
||||
|
||||
[submodule "custom_mutators/Grammar-Mutator"]
|
||||
path = custom_mutators/Grammar-Mutator
|
||||
url = https://github.com/AFLplusplus/Grammar-Mutator
|
||||
|
@ -4,8 +4,9 @@ sudo: required
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- stable
|
||||
- dev
|
||||
- llvm_merge
|
||||
|
||||
matrix:
|
||||
include:
|
||||
@ -51,8 +52,9 @@ script:
|
||||
- gcc -v
|
||||
- clang -v
|
||||
- sudo -E ./afl-system-config
|
||||
- sudo sysctl -w kernel.shmmax=10000000000
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then export LLVM_CONFIG=`pwd`/"$NAME" ; make source-only ASAN_BUILD=1 ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$TRAVIS_CPU_ARCH" = "amd64" ]; then make distrib ASAN_BUILD=1 ; fi
|
||||
- if [ "$TRAVIS_CPU_ARCH" = "arm64" ] ; then echo DEBUG ; find / -name llvm-config.h 2>/dev/null; apt-cache search clang | grep clang- ; apt-cache search llvm | grep llvm- ; dpkg -l | egrep 'clang|llvm'; echo DEBUG ; export LLVM_CONFIG=llvm-config-6.0 ; make ASAN_BUILD=1 ; cd qemu_mode && sh ./build_qemu_support.sh ; cd .. ; fi
|
||||
- if [ "$TRAVIS_CPU_ARCH" = "arm64" ] ; then export LLVM_CONFIG=llvm-config-6.0 ; make ASAN_BUILD=1 ; cd qemu_mode && sh ./build_qemu_support.sh ; cd .. ; fi
|
||||
- make tests
|
||||
# - travis_terminate 0
|
||||
|
@ -1,5 +1,7 @@
|
||||
# How to submit a Pull Request to AFLplusplus
|
||||
|
||||
All contributions (pull requests) must be made against our `dev` branch.
|
||||
|
||||
Each modified source file, before merging, must be formatted.
|
||||
|
||||
```
|
||||
@ -18,5 +20,5 @@ No camel case at all and use the AFL's macros wherever possible
|
||||
(e.g. WARNF, FATAL, MAP_SIZE, ...).
|
||||
|
||||
Remember that AFLplusplus has to build and run on many platforms, so
|
||||
generalize your Makefiles (or your patches to our pre-existing Makefiles)
|
||||
to be as much generic as possible.
|
||||
generalize your Makefiles/GNUmakefile (or your patches to our pre-existing
|
||||
Makefiles) to be as much generic as possible.
|
||||
|
28
Dockerfile
28
Dockerfile
@ -5,7 +5,7 @@
|
||||
# has focal has gcc-10 but not g++-10 ...
|
||||
#
|
||||
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:20.04 AS aflplusplus
|
||||
MAINTAINER afl++ team <afl@aflplus.plus>
|
||||
LABEL "about"="AFLplusplus docker image"
|
||||
|
||||
@ -20,11 +20,11 @@ RUN apt-get update && apt-get upgrade -y && \
|
||||
python3 python3-dev python3-setuptools python-is-python3 \
|
||||
libtool libtool-bin \
|
||||
libglib2.0-dev \
|
||||
wget vim jupp nano \
|
||||
wget vim jupp nano bash-completion \
|
||||
apt-utils apt-transport-https ca-certificates gnupg dialog \
|
||||
libpixman-1-dev
|
||||
|
||||
RUN echo deb http://apt.llvm.org/focal/ llvm-toolchain-focal main >> /etc/apt/sources.list && \
|
||||
RUN echo deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main >> /etc/apt/sources.list && \
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
|
||||
RUN echo deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu focal main >> /etc/apt/sources.list && \
|
||||
@ -46,17 +46,19 @@ RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 0
|
||||
|
||||
RUN rm -rf /var/cache/apt/archives/*
|
||||
|
||||
ARG CC=gcc-10
|
||||
ARG CXX=g++-10
|
||||
ARG LLVM_CONFIG=llvm-config-11
|
||||
ENV LLVM_CONFIG=llvm-config-11
|
||||
ENV AFL_SKIP_CPUFREQ=1
|
||||
|
||||
RUN git clone https://github.com/AFLplusplus/AFLplusplus
|
||||
RUN cd AFLplusplus && export REAL_CXX=g++-10 && make distrib && \
|
||||
make install && make clean
|
||||
RUN git clone https://github.com/vanhauser-thc/afl-cov /afl-cov
|
||||
RUN cd /afl-cov && make install && cd ..
|
||||
|
||||
RUN git clone https://github.com/vanhauser-thc/afl-cov afl-cov
|
||||
RUN cd afl-cov && make install
|
||||
COPY . /AFLplusplus
|
||||
WORKDIR /AFLplusplus
|
||||
|
||||
RUN export REAL_CXX=g++-10 && export CC=gcc-10 && \
|
||||
export CXX=g++-10 && make clean && \
|
||||
make distrib && make install && make clean
|
||||
|
||||
RUN echo 'alias joe="jupp --wordwrap"' >> ~/.bashrc
|
||||
|
||||
ENV AFL_SKIP_CPUFREQ=1
|
||||
RUN echo 'export PS1="[afl++]$PS1"' >> ~/.bashrc
|
||||
ENV IS_DOCKER="1"
|
||||
|
116
GNUmakefile
116
GNUmakefile
@ -24,31 +24,30 @@ BIN_PATH = $(PREFIX)/bin
|
||||
HELPER_PATH = $(PREFIX)/lib/afl
|
||||
DOC_PATH = $(PREFIX)/share/doc/afl
|
||||
MISC_PATH = $(PREFIX)/share/afl
|
||||
MAN_PATH = $(PREFIX)/man/man8
|
||||
MAN_PATH = $(PREFIX)/share/man/man8
|
||||
|
||||
PROGNAME = afl
|
||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
||||
|
||||
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
||||
PROGS = afl-gcc afl-g++ afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
||||
SH_PROGS = afl-plot afl-cmin afl-cmin.bash afl-whatsup afl-system-config
|
||||
MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) afl-as.8
|
||||
ASAN_OPTIONS=detect_leaks=0
|
||||
|
||||
ifeq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" ""
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
CFLAGS_FLTO ?= -flto=full
|
||||
else
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
CFLAGS_FLTO ?= -flto=thin
|
||||
else
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
CFLAGS_FLTO ?= -flto=thin
|
||||
else
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
CFLAGS_FLTO ?= -flto
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -fno-move-loop-invariants -fdisable-tree-cunrolli -x c - -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
SPECIAL_PERFORMANCE += -fno-move-loop-invariants -fdisable-tree-cunrolli
|
||||
@ -56,16 +55,21 @@ 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"
|
||||
ifndef SOURCE_DATE_EPOCH
|
||||
#CFLAGS_OPT += -march=native
|
||||
SPECIAL_PERFORMANCE += -march=native
|
||||
endif
|
||||
endif
|
||||
# OS X does not like _FORTIFY_SOURCE=2
|
||||
CFLAGS_OPT += -D_FORTIFY_SOURCE=2
|
||||
# _FORTIFY_SOURCE=2 does not like -O0
|
||||
ifndef DEBUG
|
||||
CFLAGS_OPT += -D_FORTIFY_SOURCE=2
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname)" "SunOS"
|
||||
CFLAGS_OPT += -Wno-format-truncation
|
||||
LDFLAGS=-lkstat
|
||||
LDFLAGS=-lkstat -lrt
|
||||
endif
|
||||
|
||||
ifdef STATIC
|
||||
@ -95,8 +99,14 @@ ifneq "$(shell uname -m)" "x86_64"
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT)
|
||||
override CFLAGS += -Wall -g -Wno-pointer-sign -Wmissing-declarations\
|
||||
ifdef DEBUG
|
||||
$(info Compiling DEBUG version of binaries)
|
||||
CFLAGS += -ggdb3 -O0 -Wall -Wextra -Werror
|
||||
else
|
||||
CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT)
|
||||
endif
|
||||
|
||||
override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wpointer-arith \
|
||||
-I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \
|
||||
-DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\"
|
||||
|
||||
@ -196,7 +206,11 @@ else
|
||||
endif
|
||||
|
||||
ifneq "$(filter Linux GNU%,$(shell uname))" ""
|
||||
LDFLAGS += -ldl
|
||||
# _FORTIFY_SOURCE=2 does not like -O0
|
||||
ifndef DEBUG
|
||||
override CFLAGS += -D_FORTIFY_SOURCE=2
|
||||
endif
|
||||
LDFLAGS += -ldl -lrt
|
||||
endif
|
||||
|
||||
ifneq "$(findstring FreeBSD, $(shell uname))" ""
|
||||
@ -254,21 +268,21 @@ ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int ma
|
||||
else
|
||||
SHMAT_OK=0
|
||||
override CFLAGS+=-DUSEMMAP=1
|
||||
LDFLAGS += -Wno-deprecated-declarations -lrt
|
||||
LDFLAGS += -Wno-deprecated-declarations
|
||||
endif
|
||||
|
||||
ifdef TEST_MMAP
|
||||
SHMAT_OK=0
|
||||
override CFLAGS += -DUSEMMAP=1
|
||||
LDFLAGS += -Wno-deprecated-declarations -lrt
|
||||
LDFLAGS += -Wno-deprecated-declarations
|
||||
endif
|
||||
|
||||
all: test_x86 test_shm test_python ready $(PROGS) afl-as test_build all_done
|
||||
|
||||
man: $(MANPAGES)
|
||||
man: afl-gcc all $(MANPAGES)
|
||||
|
||||
tests: source-only
|
||||
@cd test ; ./test.sh
|
||||
@cd test ; ./test-all.sh
|
||||
@rm -f test/errors
|
||||
|
||||
performance-tests: performance-test
|
||||
@ -302,6 +316,7 @@ help:
|
||||
@echo "=========================================="
|
||||
@echo STATIC - compile AFL++ static
|
||||
@echo ASAN_BUILD - compiles with memory sanitizer for debug purposes
|
||||
@echo DEBUG - no optimization, -ggdb3, all warnings and -Werror
|
||||
@echo PROFILING - compile afl-fuzz with profiling information
|
||||
@echo AFL_NO_X86 - if compiling on non-intel/amd platforms
|
||||
@echo "=========================================="
|
||||
@ -357,79 +372,81 @@ endif
|
||||
ready:
|
||||
@echo "[+] Everything seems to be working, ready to compile."
|
||||
|
||||
afl-g++: afl-gcc
|
||||
|
||||
afl-gcc: src/afl-gcc.c $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done
|
||||
|
||||
afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
ln -sf afl-as as
|
||||
|
||||
src/afl-performance.o : $(COMM_HDR) src/afl-performance.c include/hash.h
|
||||
$(CC) -Iinclude $(SPECIAL_PERFORMANCE) -O3 -fno-unroll-loops -c src/afl-performance.c -o src/afl-performance.o
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -Iinclude $(SPECIAL_PERFORMANCE) -O3 -fno-unroll-loops -c src/afl-performance.c -o src/afl-performance.o
|
||||
|
||||
src/afl-common.o : $(COMM_HDR) src/afl-common.c include/common.h
|
||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-common.c -o src/afl-common.o
|
||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-common.c -o src/afl-common.o
|
||||
|
||||
src/afl-forkserver.o : $(COMM_HDR) src/afl-forkserver.c include/forkserver.h
|
||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-forkserver.c -o src/afl-forkserver.o
|
||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-forkserver.c -o src/afl-forkserver.o
|
||||
|
||||
src/afl-sharedmem.o : $(COMM_HDR) src/afl-sharedmem.c include/sharedmem.h
|
||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-sharedmem.c -o src/afl-sharedmem.o
|
||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-sharedmem.c -o src/afl-sharedmem.o
|
||||
|
||||
afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) $(CPPFLAGS) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(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)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) 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 src/afl-performance.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 src/afl-performance.o -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||
|
||||
afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||
|
||||
afl-gotcpu: src/afl-gotcpu.c src/afl-common.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o -o $@ $(LDFLAGS)
|
||||
|
||||
|
||||
# document all mutations and only do one run (use with only one input file!)
|
||||
document: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-performance.o | test_x86
|
||||
$(CC) -D_DEBUG=\"1\" -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.c src/afl-performance.o -o afl-fuzz-document $(PYFLAGS) $(LDFLAGS)
|
||||
$(CC) -D_DEBUG=\"1\" -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) $(CPPFLAGS) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.c src/afl-performance.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) $(CPPFLAGS) -c test/unittests/unit_maybe_alloc.c -o test/unittests/unit_maybe_alloc.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) $(CPPFLAGS) -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_hash.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_hash.c $(AFL_FUZZ_FILES) src/afl-performance.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_hash.c -o test/unittests/unit_hash.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_hash.c -o test/unittests/unit_hash.o
|
||||
|
||||
unit_hash: test/unittests/unit_hash.o src/afl-performance.o
|
||||
@$(CC) $(CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_hash $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
@$(CC) $(CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_hash $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
./test/unittests/unit_hash
|
||||
|
||||
test/unittests/unit_rand.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_rand.c $(AFL_FUZZ_FILES) src/afl-performance.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_rand.c -o test/unittests/unit_rand.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_rand.c -o test/unittests/unit_rand.o
|
||||
|
||||
unit_rand: test/unittests/unit_rand.o src/afl-common.o src/afl-performance.o
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_rand $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_rand $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||
./test/unittests/unit_rand
|
||||
|
||||
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) $(CPPFLAGS) -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) $(CPPFLAGS) -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/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) $(CPPFLAGS) -c test/unittests/unit_preallocable.c -o test/unittests/unit_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) $(CPPFLAGS) -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:
|
||||
@ -455,10 +472,10 @@ code-format:
|
||||
./.custom-format.py -i llvm_mode/*.h
|
||||
./.custom-format.py -i llvm_mode/*.cc
|
||||
./.custom-format.py -i gcc_plugin/*.c
|
||||
#./.custom-format.py -i gcc_plugin/*.h
|
||||
@#./.custom-format.py -i gcc_plugin/*.h
|
||||
./.custom-format.py -i gcc_plugin/*.cc
|
||||
./.custom-format.py -i custom_mutators/*/*.c
|
||||
./.custom-format.py -i custom_mutators/*/*.h
|
||||
@#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-(
|
||||
./.custom-format.py -i examples/*/*.c
|
||||
./.custom-format.py -i examples/*/*.h
|
||||
./.custom-format.py -i test/*.c
|
||||
@ -476,7 +493,7 @@ ifndef AFL_NO_X86
|
||||
|
||||
test_build: afl-gcc afl-as afl-showmap
|
||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||
@unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 )
|
||||
@unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_AS_FORCE_INSTRUMENT=1 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 )
|
||||
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
|
||||
@ -513,7 +530,7 @@ clean:
|
||||
$(MAKE) -C qemu_mode/libcompcov clean
|
||||
rm -rf qemu_mode/qemu-3.1.1
|
||||
ifeq "$(IN_REPO)" "1"
|
||||
test -d unicorn_mode/unicornafl && $(MAKE) -C unicorn_mode/unicornafl clean || true
|
||||
test -e unicorn_mode/unicornafl/Makefile && $(MAKE) -C unicorn_mode/unicornafl clean || true
|
||||
else
|
||||
rm -rf qemu_mode/qemu-3.1.1.tar.xz
|
||||
rm -rf unicorn_mode/unicornafl
|
||||
@ -549,14 +566,15 @@ source-only: all
|
||||
-$(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
|
||||
@#$(MAKE) -C examples/afl_network_proxy
|
||||
@#$(MAKE) -C examples/socket_fuzzing
|
||||
@#$(MAKE) -C examples/argv_fuzzing
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@
|
||||
@echo .SH NAME >> $@
|
||||
@echo .B $* >> $@
|
||||
@printf "%s" ".B $* \- " >> $@
|
||||
@./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> $@
|
||||
@echo >> $@
|
||||
@echo .SH SYNOPSIS >> $@
|
||||
@./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> $@
|
||||
@ -587,6 +605,8 @@ install: all $(MANPAGES)
|
||||
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
|
||||
if [ -f libAFLDriver.a ]; then install -m 644 libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f libAFLQemuDriver.a ]; then install -m 644 libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); 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
|
||||
|
26
TODO.md
26
TODO.md
@ -1,28 +1,26 @@
|
||||
# TODO list for AFL++
|
||||
|
||||
## Roadmap 2.66+
|
||||
## Roadmap 2.68+
|
||||
|
||||
- AFL_MAP_SIZE for qemu_mode and unicorn_mode
|
||||
- namespace for targets? e.g. network
|
||||
- learn from honggfuzz (mutations, maybe ptrace?)
|
||||
- CPU affinity for many cores? There seems to be an issue > 96 cores
|
||||
- afl-plot to support multiple plot_data
|
||||
- afl_custom_fuzz_splice_optin()
|
||||
- intel-pt tracer
|
||||
|
||||
## Further down the road
|
||||
|
||||
afl-fuzz:
|
||||
- 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
|
||||
- add __sanitizer_cov_trace_cmp* support via shmem
|
||||
|
||||
llvm_mode:
|
||||
- LTO - imitate sancov
|
||||
- add __sanitizer_cov_trace_cmp* support
|
||||
|
||||
gcc_plugin:
|
||||
- (wait for submission then decide)
|
||||
- laf-intel
|
||||
- better instrumentation (seems to be better with gcc-9+)
|
||||
|
||||
qemu_mode:
|
||||
- update to 5.x (if the performance bug if gone)
|
||||
- non colliding instrumentation
|
||||
- rename qemu specific envs to AFL_QEMU (AFL_ENTRYPOINT, AFL_CODE_START/END,
|
||||
AFL_COMPCOV_LEVEL?)
|
||||
@ -30,3 +28,15 @@ qemu_mode:
|
||||
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
|
||||
|
||||
## Ideas
|
||||
|
||||
- LTO/sancov: write current edge to prev_loc and use that information when
|
||||
using cmplog or __sanitizer_cov_trace_cmp*. maybe we can deduct by follow
|
||||
up edge numbers that both following cmp paths have been found and then
|
||||
disable working on this edge id -> cmplog_intelligence branch
|
||||
|
||||
- new tancov: use some lightweight taint analysis to see which parts of a
|
||||
new queue entry is accessed and only fuzz these bytes - or better, only
|
||||
fuzz those bytes that are newly in coverage compared to the queue entry
|
||||
the new one is based on -> taint branch, not useful :-(
|
||||
|
1
afl-cmin
1
afl-cmin
@ -120,6 +120,7 @@ function usage() {
|
||||
"AFL_PATH: path for the afl-showmap binary\n" \
|
||||
"AFL_SKIP_BIN_CHECK: skip check for target binary\n" \
|
||||
"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n"
|
||||
"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
14
afl-plot
14
afl-plot
@ -68,6 +68,15 @@ if [ ! -f "$inputdir/plot_data" ]; then
|
||||
|
||||
fi
|
||||
|
||||
LINES=`cat "$inputdir/plot_data" | wc -l`
|
||||
|
||||
if [ "$LINES" -lt 3 ]; then
|
||||
|
||||
echo "[-] Error: plot_data carries too little data, let it run longer." 1>&2
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
BANNER="`cat "$inputdir/fuzzer_stats" 2> /dev/null | grep '^afl_banner ' | cut -d: -f2- | cut -b2-`"
|
||||
|
||||
test "$BANNER" = "" && BANNER="(none)"
|
||||
@ -118,6 +127,9 @@ set key outside
|
||||
set autoscale xfixmin
|
||||
set autoscale xfixmax
|
||||
|
||||
set xlabel "all times in UTC" font "small"
|
||||
|
||||
set ytics auto
|
||||
plot '$inputdir/plot_data' using 1:4 with filledcurve x1 title 'total paths' linecolor rgb '#000000' fillstyle transparent solid 0.2 noborder, \\
|
||||
'' using 1:3 with filledcurve x1 title 'current path' linecolor rgb '#f0f0f0' fillstyle transparent solid 0.5 noborder, \\
|
||||
'' using 1:5 with lines title 'pending paths' linecolor rgb '#0090ff' linewidth 3, \\
|
||||
@ -127,6 +139,7 @@ plot '$inputdir/plot_data' using 1:4 with filledcurve x1 title 'total paths' lin
|
||||
set terminal png truecolor enhanced size 1000,200 butt
|
||||
set output '$outputdir/low_freq.png'
|
||||
|
||||
set ytics 1
|
||||
plot '$inputdir/plot_data' using 1:8 with filledcurve x1 title '' linecolor rgb '#c00080' fillstyle transparent solid 0.2 noborder, \\
|
||||
'' using 1:8 with lines title ' uniq crashes' linecolor rgb '#c00080' linewidth 3, \\
|
||||
'' using 1:9 with lines title 'uniq hangs' linecolor rgb '#c000f0' linewidth 3, \\
|
||||
@ -135,6 +148,7 @@ plot '$inputdir/plot_data' using 1:8 with filledcurve x1 title '' linecolor rgb
|
||||
set terminal png truecolor enhanced size 1000,200 butt
|
||||
set output '$outputdir/exec_speed.png'
|
||||
|
||||
set ytics auto
|
||||
plot '$inputdir/plot_data' using 1:11 with filledcurve x1 title '' linecolor rgb '#0090ff' fillstyle transparent solid 0.2 noborder, \\
|
||||
'$inputdir/plot_data' using 1:11 with lines title ' execs/sec' linecolor rgb '#0090ff' linewidth 3 smooth bezier;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
test "$1" = "-h" && {
|
||||
test "$1" = "-h" -o "$1" = "-hh" && {
|
||||
echo 'afl-system-config by Marc Heuse <mh@mh-sec.de>'
|
||||
echo
|
||||
echo $0
|
||||
|
17
afl-whatsup
17
afl-whatsup
@ -20,7 +20,7 @@
|
||||
|
||||
echo "$0 status check tool for afl-fuzz by Michal Zalewski"
|
||||
echo
|
||||
test "$1" = "-h" && {
|
||||
test "$1" = "-h" -o "$1" = "-hh" && {
|
||||
echo $0 [-s] output_directory
|
||||
echo
|
||||
echo Options:
|
||||
@ -99,7 +99,7 @@ fi
|
||||
fmt_duration()
|
||||
{
|
||||
DUR_STRING=
|
||||
if [ $1 -eq 0 ]; then
|
||||
if [ $1 -le 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
@ -109,7 +109,11 @@ fmt_duration()
|
||||
local minutes=$(((duration / 60) % 60))
|
||||
local seconds=$((duration % 60))
|
||||
|
||||
if [ $days -gt 0 ]; then
|
||||
if [ $duration -le 0 ]; then
|
||||
DUR_STRING="0 seconds"
|
||||
elif [ $duration -eq 1 ]; then
|
||||
DUR_STRING="1 second"
|
||||
elif [ $days -gt 0 ]; then
|
||||
DUR_STRING="$days days, $hours hours"
|
||||
elif [ $hours -gt 0 ]; then
|
||||
DUR_STRING="$hours hours, $minutes minutes"
|
||||
@ -162,7 +166,8 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
|
||||
|
||||
ALIVE_CNT=$((ALIVE_CNT + 1))
|
||||
|
||||
EXEC_SEC=$((execs_done / RUN_UNIX))
|
||||
EXEC_SEC=0
|
||||
test -z "$RUN_UNIX" -o "$RUN_UNIX" = 0 || EXEC_SEC=$((execs_done / RUN_UNIX))
|
||||
PATH_PERC=$((cur_path * 100 / paths_total))
|
||||
|
||||
TOTAL_TIME=$((TOTAL_TIME + RUN_UNIX))
|
||||
@ -184,7 +189,9 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
|
||||
echo " ${RED}timeout_ratio $TIMEOUT_PERC%${NC}"
|
||||
fi
|
||||
|
||||
if [ $EXEC_SEC -lt 100 ]; then
|
||||
if [ $EXEC_SEC -eq 0 ]; then
|
||||
echo " ${YELLOW}no data yet, 0 execs/sec${NC}"
|
||||
elif [ $EXEC_SEC -lt 100 ]; then
|
||||
echo " ${RED}slow execution, $EXEC_SEC execs/sec${NC}"
|
||||
fi
|
||||
|
||||
|
@ -68,7 +68,12 @@ else:
|
||||
argv = sys.argv[1:]
|
||||
for i in range(len(argv)):
|
||||
if ".cur_input" in argv[i]:
|
||||
argv[i] = subprocess.run([os.path.join(os.path.dirname(wine_path), "winepath"), "--windows", argv[i]], universal_newlines=True, stdout=subprocess.PIPE).stdout
|
||||
# Get the Wine translated path using the winepath tool
|
||||
arg_translated = subprocess.run([os.path.join(os.path.dirname(wine_path), "winepath"), "--windows", argv[i]], universal_newlines=True, stdout=subprocess.PIPE).stdout
|
||||
# Remove the spurious LF at the end of the path
|
||||
if len(arg_translated) > 0 and arg_translated[-1] == '\n':
|
||||
arg_translated = arg_translated[:-1]
|
||||
argv[i] = arg_translated
|
||||
break
|
||||
|
||||
print("[afl-wine-trace] exec:", " ".join([qemu_path, wine_path] + argv))
|
||||
|
@ -1,4 +1,25 @@
|
||||
# production ready custom mutators
|
||||
# Custom Mutators
|
||||
|
||||
Custom mutators enhance and alter the mutation strategies of afl++.
|
||||
For further information and documentation on how to write your own, read [the docs](../docs/custom_mutators.md).
|
||||
|
||||
## The afl++ Grammar Mutator
|
||||
|
||||
If you use git to clone afl++, then the following will incorporate our
|
||||
excellent grammar custom mutator:
|
||||
```
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
otherwise just checkout the repository here with either
|
||||
`git clone https://github.com/AFLplusplus/Grammar-Mutator` or
|
||||
`svn co https://github.com/AFLplusplus/Grammar-Mutator`.
|
||||
|
||||
Read the [Grammar-Mutator/README.md](Grammar-Mutator/README.md) on how to use
|
||||
it.
|
||||
|
||||
## Production-Ready Custom Mutators
|
||||
|
||||
This directory holds ready to use custom mutators.
|
||||
Just type "make" in the individual subdirectories.
|
||||
@ -10,3 +31,22 @@ Use with e.g.
|
||||
and add `AFL_CUSTOM_MUTATOR_ONLY=1` if you only want to use the custom mutator.
|
||||
|
||||
Multiple custom mutators can be used by separating their paths with `:` in the environment variable.
|
||||
|
||||
## 3rd Party Custom Mutators
|
||||
|
||||
### Superion Mutators
|
||||
|
||||
Adrian Tiron ported the Superion grammar fuzzer to afl++, it is WIP and
|
||||
requires cmake (among other things):
|
||||
[https://github.com/adrian-rt/superion-mutator](https://github.com/adrian-rt/superion-mutator)
|
||||
|
||||
### libprotobuf Mutators
|
||||
|
||||
There are two WIP protobuf projects, that require work to be working though:
|
||||
|
||||
transforms protobuf raw:
|
||||
https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/tree/master/4_libprotobuf_aflpp_custom_mutator
|
||||
|
||||
has a transform function you need to fill for your protobuf format, however
|
||||
needs to be ported to the updated afl++ custom mutator API (not much work):
|
||||
https://github.com/thebabush/afl-libprotobuf-mutator
|
||||
|
17
custom_mutators/honggfuzz/Makefile
Normal file
17
custom_mutators/honggfuzz/Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
CFLAGS = -O3 -funroll-loops -fPIC -Wl,-Bsymbolic
|
||||
|
||||
all: honggfuzz.so
|
||||
|
||||
honggfuzz.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c
|
||||
$(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz.so honggfuzz.c mangle.c ../../src/afl-performance.c
|
||||
|
||||
update:
|
||||
@# seriously? --unlink is a dud option? sigh ...
|
||||
rm -f mangle.c mangle.h honggfuzz.h
|
||||
wget --unlink https://github.com/google/honggfuzz/raw/master/mangle.c
|
||||
wget --unlink https://github.com/google/honggfuzz/raw/master/mangle.h
|
||||
wget --unlink https://github.com/google/honggfuzz/raw/master/honggfuzz.h
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ *.so core
|
12
custom_mutators/honggfuzz/README.md
Normal file
12
custom_mutators/honggfuzz/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# custum mutator: honggfuzz mangle
|
||||
|
||||
this is the very good honggfuzz mutator in mangle.c as a custom mutator
|
||||
module for afl++. It is the original mangle.c, mangle.h and honggfuzz.h
|
||||
with a lot of mocking around it :-)
|
||||
|
||||
just type `make` to build
|
||||
|
||||
```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz.so afl-fuzz ...```
|
||||
|
||||
> Original repository: https://github.com/google/honggfuzz
|
||||
> Source commit: d0fbcb0373c32436b8fb922e6937da93b17291f5
|
0
custom_mutators/honggfuzz/common.h
Normal file
0
custom_mutators/honggfuzz/common.h
Normal file
22
custom_mutators/honggfuzz/custom_mutator_helpers.h
Normal file
22
custom_mutators/honggfuzz/custom_mutator_helpers.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef CUSTOM_MUTATOR_HELPERS
|
||||
#define CUSTOM_MUTATOR_HELPERS
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "afl-fuzz.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INITIAL_GROWTH_SIZE (64)
|
||||
|
||||
/* Use in a struct: creates a name_buf and a name_size variable. */
|
||||
#define BUF_VAR(type, name) \
|
||||
type * name##_buf; \
|
||||
size_t name##_size;
|
||||
/* this filles in `&structptr->something_buf, &structptr->something_size`. */
|
||||
#define BUF_PARAMS(struct, name) \
|
||||
(void **)&struct->name##_buf, &struct->name##_size
|
||||
|
||||
#undef INITIAL_GROWTH_SIZE
|
||||
|
||||
#endif
|
||||
|
143
custom_mutators/honggfuzz/honggfuzz.c
Normal file
143
custom_mutators/honggfuzz/honggfuzz.c
Normal file
@ -0,0 +1,143 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "custom_mutator_helpers.h"
|
||||
#include "mangle.h"
|
||||
|
||||
#define NUMBER_OF_MUTATIONS 5
|
||||
|
||||
uint8_t * queue_input;
|
||||
size_t queue_input_size;
|
||||
afl_state_t * afl_struct;
|
||||
run_t run;
|
||||
honggfuzz_t global;
|
||||
struct _dynfile_t dynfile;
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_state_t *afl;
|
||||
run_t * run;
|
||||
u8 * mutator_buf;
|
||||
unsigned int seed;
|
||||
unsigned int extras_cnt, a_extras_cnt;
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||
|
||||
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||
if (!data) {
|
||||
|
||||
perror("afl_custom_init alloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) {
|
||||
|
||||
perror("mutator_buf alloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
run.dynfile = &dynfile;
|
||||
run.global = &global;
|
||||
data->afl = afl;
|
||||
data->seed = seed;
|
||||
data->run = &run;
|
||||
afl_struct = afl;
|
||||
|
||||
run.global->mutate.maxInputSz = MAX_FILE;
|
||||
run.global->mutate.mutationsPerRun = NUMBER_OF_MUTATIONS;
|
||||
run.mutationsPerRun = NUMBER_OF_MUTATIONS;
|
||||
run.global->timing.lastCovUpdate = 6;
|
||||
|
||||
// global->feedback.cmpFeedback
|
||||
// global->feedback.cmpFeedbackMap
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/* When a new queue entry is added we check if there are new dictionary
|
||||
entries to add to honggfuzz structure */
|
||||
|
||||
void afl_custom_queue_new_entry(my_mutator_t * data,
|
||||
const uint8_t *filename_new_queue,
|
||||
const uint8_t *filename_orig_queue) {
|
||||
|
||||
if (run.global->mutate.dictionaryCnt >= 1024) return;
|
||||
|
||||
while (data->extras_cnt < data->afl->extras_cnt &&
|
||||
run.global->mutate.dictionaryCnt < 1024) {
|
||||
|
||||
memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val,
|
||||
data->afl->extras[data->extras_cnt].data,
|
||||
data->afl->extras[data->extras_cnt].len);
|
||||
run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len =
|
||||
data->afl->extras[data->extras_cnt].len;
|
||||
run.global->mutate.dictionaryCnt++;
|
||||
data->extras_cnt++;
|
||||
|
||||
}
|
||||
|
||||
while (data->a_extras_cnt < data->afl->a_extras_cnt &&
|
||||
run.global->mutate.dictionaryCnt < 1024) {
|
||||
|
||||
memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val,
|
||||
data->afl->a_extras[data->a_extras_cnt].data,
|
||||
data->afl->a_extras[data->a_extras_cnt].len);
|
||||
run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len =
|
||||
data->afl->a_extras[data->a_extras_cnt].len;
|
||||
run.global->mutate.dictionaryCnt++;
|
||||
data->a_extras_cnt++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* we could set only_printable if is_ascii is set ... let's see
|
||||
uint8_t afl_custom_queue_get(void *data, const uint8_t *filename) {
|
||||
|
||||
//run.global->cfg.only_printable = ...
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/* here we run the honggfuzz mutator, which is really good */
|
||||
|
||||
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,
|
||||
size_t max_size) {
|
||||
|
||||
/* set everything up, costly ... :( */
|
||||
memcpy(data->mutator_buf, buf, buf_size);
|
||||
queue_input = data->mutator_buf;
|
||||
run.dynfile->data = data->mutator_buf;
|
||||
queue_input_size = buf_size;
|
||||
run.dynfile->size = buf_size;
|
||||
*out_buf = data->mutator_buf;
|
||||
|
||||
/* the mutation */
|
||||
mangle_mangleContent(&run, NUMBER_OF_MUTATIONS);
|
||||
|
||||
/* return size of mutated data */
|
||||
return run.dynfile->size;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitialize everything
|
||||
*
|
||||
* @param data The data ptr from afl_custom_init
|
||||
*/
|
||||
void afl_custom_deinit(my_mutator_t *data) {
|
||||
|
||||
free(data->mutator_buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
385
custom_mutators/honggfuzz/honggfuzz.h
Normal file
385
custom_mutators/honggfuzz/honggfuzz.h
Normal file
@ -0,0 +1,385 @@
|
||||
/*
|
||||
*
|
||||
* honggfuzz - core structures and macros
|
||||
* -----------------------------------------
|
||||
*
|
||||
* Author: Robert Swiecki <swiecki@google.com>
|
||||
*
|
||||
* Copyright 2010-2018 by 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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HF_HONGGFUZZ_H_
|
||||
#define _HF_HONGGFUZZ_H_
|
||||
|
||||
#include <dirent.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "libhfcommon/util.h"
|
||||
|
||||
#define PROG_NAME "honggfuzz"
|
||||
#define PROG_VERSION "2.3"
|
||||
|
||||
/* Name of the template which will be replaced with the proper name of the file */
|
||||
#define _HF_FILE_PLACEHOLDER "___FILE___"
|
||||
|
||||
/* Default name of the report created with some architectures */
|
||||
#define _HF_REPORT_FILE "HONGGFUZZ.REPORT.TXT"
|
||||
|
||||
/* Default stack-size of created threads. */
|
||||
#define _HF_PTHREAD_STACKSIZE (1024ULL * 1024ULL * 2ULL) /* 2MB */
|
||||
|
||||
/* Name of envvar which indicates sequential number of fuzzer */
|
||||
#define _HF_THREAD_NO_ENV "HFUZZ_THREAD_NO"
|
||||
|
||||
/* Name of envvar which indicates that the netDriver should be used */
|
||||
#define _HF_THREAD_NETDRIVER_ENV "HFUZZ_USE_NETDRIVER"
|
||||
|
||||
/* Name of envvar which indicates honggfuzz's log level in use */
|
||||
#define _HF_LOG_LEVEL_ENV "HFUZZ_LOG_LEVEL"
|
||||
|
||||
/* Number of crash verifier iterations before tag crash as stable */
|
||||
#define _HF_VERIFIER_ITER 5
|
||||
|
||||
/* Size (in bytes) for report data to be stored in stack before written to file */
|
||||
#define _HF_REPORT_SIZE 32768
|
||||
|
||||
/* Perf bitmap size */
|
||||
#define _HF_PERF_BITMAP_SIZE_16M (1024U * 1024U * 16U)
|
||||
#define _HF_PERF_BITMAP_BITSZ_MASK 0x7FFFFFFULL
|
||||
/* Maximum number of PC guards (=trace-pc-guard) we support */
|
||||
#define _HF_PC_GUARD_MAX (1024ULL * 1024ULL * 64ULL)
|
||||
|
||||
/* Maximum size of the input file in bytes (1 MiB) */
|
||||
#define _HF_INPUT_MAX_SIZE (1024ULL * 1024ULL)
|
||||
|
||||
/* Default maximum size of produced inputs */
|
||||
#define _HF_INPUT_DEFAULT_SIZE (1024ULL * 8)
|
||||
|
||||
/* Per-thread bitmap */
|
||||
#define _HF_PERTHREAD_BITMAP_FD 1018
|
||||
/* FD used to report back used int/str constants from the fuzzed process */
|
||||
#define _HF_CMP_BITMAP_FD 1019
|
||||
/* FD used to log inside the child process */
|
||||
#define _HF_LOG_FD 1020
|
||||
/* FD used to represent the input file */
|
||||
#define _HF_INPUT_FD 1021
|
||||
/* FD used to pass coverage feedback from the fuzzed process */
|
||||
#define _HF_COV_BITMAP_FD 1022
|
||||
#define _HF_BITMAP_FD _HF_COV_BITMAP_FD /* Old name for _HF_COV_BITMAP_FD */
|
||||
/* FD used to pass data to a persistent process */
|
||||
#define _HF_PERSISTENT_FD 1023
|
||||
|
||||
/* Input file as a string */
|
||||
#define _HF_INPUT_FILE_PATH "/dev/fd/" HF_XSTR(_HF_INPUT_FD)
|
||||
|
||||
/* Maximum number of supported execve() args */
|
||||
#define _HF_ARGS_MAX 2048
|
||||
|
||||
/* Message indicating that the fuzzed process is ready for new data */
|
||||
static const uint8_t HFReadyTag = 'R';
|
||||
|
||||
/* Maximum number of active fuzzing threads */
|
||||
#define _HF_THREAD_MAX 1024U
|
||||
|
||||
/* Persistent-binary signature - if found within file, it means it's a persistent mode binary */
|
||||
#define _HF_PERSISTENT_SIG "\x01_LIBHFUZZ_PERSISTENT_BINARY_SIGNATURE_\x02\xFF"
|
||||
/* HF NetDriver signature - if found within file, it means it's a NetDriver-based binary */
|
||||
#define _HF_NETDRIVER_SIG "\x01_LIBHFUZZ_NETDRIVER_BINARY_SIGNATURE_\x02\xFF"
|
||||
|
||||
/* printf() nonmonetary separator. According to MacOSX's man it's supported there as well */
|
||||
#define _HF_NONMON_SEP "'"
|
||||
|
||||
typedef enum {
|
||||
_HF_DYNFILE_NONE = 0x0,
|
||||
_HF_DYNFILE_INSTR_COUNT = 0x1,
|
||||
_HF_DYNFILE_BRANCH_COUNT = 0x2,
|
||||
_HF_DYNFILE_BTS_EDGE = 0x10,
|
||||
_HF_DYNFILE_IPT_BLOCK = 0x20,
|
||||
_HF_DYNFILE_SOFT = 0x40,
|
||||
} dynFileMethod_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t cpuInstrCnt;
|
||||
uint64_t cpuBranchCnt;
|
||||
uint64_t bbCnt;
|
||||
uint64_t newBBCnt;
|
||||
uint64_t softCntPc;
|
||||
uint64_t softCntEdge;
|
||||
uint64_t softCntCmp;
|
||||
} hwcnt_t;
|
||||
|
||||
typedef enum {
|
||||
_HF_STATE_UNSET = 0,
|
||||
_HF_STATE_STATIC,
|
||||
_HF_STATE_DYNAMIC_DRY_RUN,
|
||||
_HF_STATE_DYNAMIC_MAIN,
|
||||
_HF_STATE_DYNAMIC_MINIMIZE,
|
||||
} fuzzState_t;
|
||||
|
||||
typedef enum {
|
||||
HF_MAYBE = -1,
|
||||
HF_NO = 0,
|
||||
HF_YES = 1,
|
||||
} tristate_t;
|
||||
|
||||
struct _dynfile_t {
|
||||
size_t size;
|
||||
uint64_t cov[4];
|
||||
size_t idx;
|
||||
int fd;
|
||||
uint64_t timeExecUSecs;
|
||||
char path[PATH_MAX];
|
||||
struct _dynfile_t* src;
|
||||
uint32_t refs;
|
||||
uint8_t* data;
|
||||
TAILQ_ENTRY(_dynfile_t) pointers;
|
||||
};
|
||||
|
||||
typedef struct _dynfile_t dynfile_t;
|
||||
|
||||
struct strings_t {
|
||||
size_t len;
|
||||
TAILQ_ENTRY(strings_t) pointers;
|
||||
char s[];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t pcGuardMap[_HF_PC_GUARD_MAX];
|
||||
uint8_t bbMapPc[_HF_PERF_BITMAP_SIZE_16M];
|
||||
uint32_t bbMapCmp[_HF_PERF_BITMAP_SIZE_16M];
|
||||
uint64_t pidNewPC[_HF_THREAD_MAX];
|
||||
uint64_t pidNewEdge[_HF_THREAD_MAX];
|
||||
uint64_t pidNewCmp[_HF_THREAD_MAX];
|
||||
uint64_t guardNb;
|
||||
uint64_t pidTotalPC[_HF_THREAD_MAX];
|
||||
uint64_t pidTotalEdge[_HF_THREAD_MAX];
|
||||
uint64_t pidTotalCmp[_HF_THREAD_MAX];
|
||||
} feedback_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t cnt;
|
||||
struct {
|
||||
uint8_t val[32];
|
||||
uint32_t len;
|
||||
} valArr[1024 * 16];
|
||||
} cmpfeedback_t;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
size_t threadsMax;
|
||||
size_t threadsFinished;
|
||||
uint32_t threadsActiveCnt;
|
||||
pthread_t mainThread;
|
||||
pid_t mainPid;
|
||||
pthread_t threads[_HF_THREAD_MAX];
|
||||
} threads;
|
||||
struct {
|
||||
const char* inputDir;
|
||||
const char* outputDir;
|
||||
DIR* inputDirPtr;
|
||||
size_t fileCnt;
|
||||
size_t testedFileCnt;
|
||||
const char* fileExtn;
|
||||
size_t maxFileSz;
|
||||
size_t newUnitsAdded;
|
||||
char workDir[PATH_MAX];
|
||||
const char* crashDir;
|
||||
const char* covDirNew;
|
||||
bool saveUnique;
|
||||
size_t dynfileqMaxSz;
|
||||
size_t dynfileqCnt;
|
||||
dynfile_t* dynfileqCurrent;
|
||||
dynfile_t* dynfileq2Current;
|
||||
TAILQ_HEAD(dyns_t, _dynfile_t) dynfileq;
|
||||
bool exportFeedback;
|
||||
} io;
|
||||
struct {
|
||||
int argc;
|
||||
const char* const* cmdline;
|
||||
bool nullifyStdio;
|
||||
bool fuzzStdin;
|
||||
const char* externalCommand;
|
||||
const char* postExternalCommand;
|
||||
const char* feedbackMutateCommand;
|
||||
bool netDriver;
|
||||
bool persistent;
|
||||
uint64_t asLimit;
|
||||
uint64_t rssLimit;
|
||||
uint64_t dataLimit;
|
||||
uint64_t coreLimit;
|
||||
uint64_t stackLimit;
|
||||
bool clearEnv;
|
||||
char* env_ptrs[128];
|
||||
char env_vals[128][4096];
|
||||
sigset_t waitSigSet;
|
||||
} exe;
|
||||
struct {
|
||||
time_t timeStart;
|
||||
time_t runEndTime;
|
||||
time_t tmOut;
|
||||
time_t lastCovUpdate;
|
||||
int64_t timeOfLongestUnitUSecs;
|
||||
bool tmoutVTALRM;
|
||||
} timing;
|
||||
struct {
|
||||
struct {
|
||||
uint8_t val[256];
|
||||
size_t len;
|
||||
} dictionary[1024];
|
||||
size_t dictionaryCnt;
|
||||
const char* dictionaryFile;
|
||||
size_t mutationsMax;
|
||||
unsigned mutationsPerRun;
|
||||
size_t maxInputSz;
|
||||
} mutate;
|
||||
struct {
|
||||
bool useScreen;
|
||||
char cmdline_txt[65];
|
||||
int64_t lastDisplayUSecs;
|
||||
} display;
|
||||
struct {
|
||||
bool useVerifier;
|
||||
bool exitUponCrash;
|
||||
const char* reportFile;
|
||||
size_t dynFileIterExpire;
|
||||
bool only_printable;
|
||||
bool minimize;
|
||||
bool switchingToFDM;
|
||||
} cfg;
|
||||
struct {
|
||||
bool enable;
|
||||
bool del_report;
|
||||
} sanitizer;
|
||||
struct {
|
||||
fuzzState_t state;
|
||||
feedback_t* covFeedbackMap;
|
||||
int covFeedbackFd;
|
||||
cmpfeedback_t* cmpFeedbackMap;
|
||||
int cmpFeedbackFd;
|
||||
bool cmpFeedback;
|
||||
const char* blacklistFile;
|
||||
uint64_t* blacklist;
|
||||
size_t blacklistCnt;
|
||||
bool skipFeedbackOnTimeout;
|
||||
uint64_t maxCov[4];
|
||||
dynFileMethod_t dynFileMethod;
|
||||
hwcnt_t hwCnts;
|
||||
} feedback;
|
||||
struct {
|
||||
size_t mutationsCnt;
|
||||
size_t crashesCnt;
|
||||
size_t uniqueCrashesCnt;
|
||||
size_t verifiedCrashesCnt;
|
||||
size_t blCrashesCnt;
|
||||
size_t timeoutedCnt;
|
||||
} cnts;
|
||||
struct {
|
||||
bool enabled;
|
||||
int serverSocket;
|
||||
int clientSocket;
|
||||
} socketFuzzer;
|
||||
struct {
|
||||
pthread_rwlock_t dynfileq;
|
||||
pthread_mutex_t feedback;
|
||||
pthread_mutex_t report;
|
||||
pthread_mutex_t state;
|
||||
pthread_mutex_t input;
|
||||
pthread_mutex_t timing;
|
||||
} mutex;
|
||||
|
||||
/* For the Linux code */
|
||||
struct {
|
||||
int exeFd;
|
||||
uint64_t dynamicCutOffAddr;
|
||||
bool disableRandomization;
|
||||
void* ignoreAddr;
|
||||
const char* symsBlFile;
|
||||
char** symsBl;
|
||||
size_t symsBlCnt;
|
||||
const char* symsWlFile;
|
||||
char** symsWl;
|
||||
size_t symsWlCnt;
|
||||
uintptr_t cloneFlags;
|
||||
tristate_t useNetNs;
|
||||
bool kernelOnly;
|
||||
bool useClone;
|
||||
} arch_linux;
|
||||
/* For the NetBSD code */
|
||||
struct {
|
||||
void* ignoreAddr;
|
||||
const char* symsBlFile;
|
||||
char** symsBl;
|
||||
size_t symsBlCnt;
|
||||
const char* symsWlFile;
|
||||
char** symsWl;
|
||||
size_t symsWlCnt;
|
||||
} arch_netbsd;
|
||||
} honggfuzz_t;
|
||||
|
||||
typedef enum {
|
||||
_HF_RS_UNKNOWN = 0,
|
||||
_HF_RS_WAITING_FOR_INITIAL_READY = 1,
|
||||
_HF_RS_WAITING_FOR_READY = 2,
|
||||
_HF_RS_SEND_DATA = 3,
|
||||
} runState_t;
|
||||
|
||||
typedef struct {
|
||||
honggfuzz_t* global;
|
||||
pid_t pid;
|
||||
int64_t timeStartedUSecs;
|
||||
char crashFileName[PATH_MAX];
|
||||
uint64_t pc;
|
||||
uint64_t backtrace;
|
||||
uint64_t access;
|
||||
int exception;
|
||||
char report[_HF_REPORT_SIZE];
|
||||
bool mainWorker;
|
||||
unsigned mutationsPerRun;
|
||||
dynfile_t* dynfile;
|
||||
bool staticFileTryMore;
|
||||
uint32_t fuzzNo;
|
||||
int persistentSock;
|
||||
runState_t runState;
|
||||
bool tmOutSignaled;
|
||||
char* args[_HF_ARGS_MAX + 1];
|
||||
int perThreadCovFeedbackFd;
|
||||
unsigned triesLeft;
|
||||
dynfile_t* current;
|
||||
#if !defined(_HF_ARCH_DARWIN)
|
||||
timer_t timerId;
|
||||
#endif // !defined(_HF_ARCH_DARWIN)
|
||||
hwcnt_t hwCnts;
|
||||
|
||||
struct {
|
||||
/* For Linux code */
|
||||
uint8_t* perfMmapBuf;
|
||||
uint8_t* perfMmapAux;
|
||||
int cpuInstrFd;
|
||||
int cpuBranchFd;
|
||||
int cpuIptBtsFd;
|
||||
} arch_linux;
|
||||
} run_t;
|
||||
|
||||
#endif
|
106
custom_mutators/honggfuzz/input.h
Normal file
106
custom_mutators/honggfuzz/input.h
Normal file
@ -0,0 +1,106 @@
|
||||
#ifndef _HG_INPUT_
|
||||
#define _HG_INPUT_
|
||||
|
||||
#include <stdarg.h>
|
||||
#ifdef __clang__
|
||||
#include <stdatomic.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "honggfuzz.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/*
|
||||
* Go-style defer scoped implementation
|
||||
*
|
||||
* If compiled with clang, use: -fblocks -lBlocksRuntime
|
||||
*
|
||||
* Example of use:
|
||||
*
|
||||
* {
|
||||
* int fd = open(fname, O_RDONLY);
|
||||
* if (fd == -1) {
|
||||
* error(....);
|
||||
* return;
|
||||
* }
|
||||
* defer { close(fd); };
|
||||
* ssize_t sz = read(fd, buf, sizeof(buf));
|
||||
* ...
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
#define __STRMERGE(a, b) a##b
|
||||
#define _STRMERGE(a, b) __STRMERGE(a, b)
|
||||
#ifdef __clang__
|
||||
#if __has_extension(blocks)
|
||||
static void __attribute__((unused)) __clang_cleanup_func(void (^*dfunc)(void)) {
|
||||
(*dfunc)();
|
||||
}
|
||||
|
||||
#define defer \
|
||||
void (^_STRMERGE(__defer_f_, __COUNTER__))(void) \
|
||||
__attribute__((cleanup(__clang_cleanup_func))) __attribute__((unused)) = ^
|
||||
|
||||
#else /* __has_extension(blocks) */
|
||||
#define defer UNIMPLEMENTED - NO - SUPPORT - FOR - BLOCKS - IN - YOUR - CLANG - ENABLED
|
||||
#endif /* __has_extension(blocks) */
|
||||
#else /* !__clang__, e.g.: gcc */
|
||||
|
||||
#define __block
|
||||
#define _DEFER(a, count) \
|
||||
auto void _STRMERGE(__defer_f_, count)(void* _defer_arg __attribute__((unused))); \
|
||||
int _STRMERGE(__defer_var_, count) __attribute__((cleanup(_STRMERGE(__defer_f_, count)))) \
|
||||
__attribute__((unused)); \
|
||||
void _STRMERGE(__defer_f_, count)(void* _defer_arg __attribute__((unused)))
|
||||
#define defer _DEFER(a, __COUNTER__)
|
||||
#endif /* ifdef __clang__ */
|
||||
|
||||
#define HF_MIN(x, y) (x <= y ? x : y)
|
||||
#define HF_MAX(x, y) (x >= y ? x : y)
|
||||
#define ATOMIC_GET
|
||||
#define ARRAYSIZE(x) (sizeof(x) / sizeof(*x))
|
||||
#define HF_ATTR_UNUSED __attribute__((unused))
|
||||
#define util_Malloc(x) malloc(x)
|
||||
|
||||
extern uint8_t * queue_input;
|
||||
extern size_t queue_input_size;
|
||||
extern afl_state_t * afl_struct;
|
||||
|
||||
inline void wmb() { }
|
||||
inline void LOG_F(const char *format, ...) { }
|
||||
static inline uint64_t util_rndGet(uint64_t min, uint64_t max) {
|
||||
return min + rand_below(afl_struct, max - min + 1);
|
||||
}
|
||||
static inline uint64_t util_rnd64() { return rand_below(afl_struct, 1 << 30); }
|
||||
|
||||
static inline size_t input_getRandomInputAsBuf(run_t *run, const uint8_t **buf) {
|
||||
*buf = queue_input;
|
||||
run->dynfile->data = queue_input;
|
||||
run->dynfile->size = queue_input_size;
|
||||
return queue_input_size;
|
||||
}
|
||||
static inline void input_setSize(run_t* run, size_t sz) {
|
||||
run->dynfile->size = sz;
|
||||
}
|
||||
static inline void util_turnToPrintable(uint8_t* buf, size_t sz) {
|
||||
for (size_t i = 0; i < sz; i++)
|
||||
buf[i] = buf[i] % 95 + 32;
|
||||
}
|
||||
static inline void util_rndBuf(uint8_t* buf, size_t sz) {
|
||||
if (sz == 0) return;
|
||||
for (size_t i = 0; i < sz; i++)
|
||||
buf[i] = (uint8_t)rand_below(afl_struct, 256);
|
||||
}
|
||||
static inline uint8_t util_rndPrintable() {
|
||||
return 32 + rand_below(afl_struct, 127 - 32);
|
||||
}
|
||||
static inline void util_rndBufPrintable(uint8_t* buf, size_t sz) {
|
||||
for (size_t i = 0; i < sz; i++)
|
||||
buf[i] = util_rndPrintable();
|
||||
}
|
||||
|
||||
#endif
|
1
custom_mutators/honggfuzz/libhfcommon
Symbolic link
1
custom_mutators/honggfuzz/libhfcommon
Symbolic link
@ -0,0 +1 @@
|
||||
.
|
1
custom_mutators/honggfuzz/log.h
Symbolic link
1
custom_mutators/honggfuzz/log.h
Symbolic link
@ -0,0 +1 @@
|
||||
common.h
|
1037
custom_mutators/honggfuzz/mangle.c
Normal file
1037
custom_mutators/honggfuzz/mangle.c
Normal file
File diff suppressed because it is too large
Load Diff
31
custom_mutators/honggfuzz/mangle.h
Normal file
31
custom_mutators/honggfuzz/mangle.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
*
|
||||
* honggfuzz - buffer mangling routines
|
||||
* -----------------------------------------
|
||||
*
|
||||
* Author: Robert Swiecki <swiecki@google.com>
|
||||
*
|
||||
* Copyright 2010-2018 by 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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HF_MANGLE_H_
|
||||
#define _HF_MANGLE_H_
|
||||
|
||||
#include "honggfuzz.h"
|
||||
|
||||
extern void mangle_mangleContent(run_t* run, int speed_factor);
|
||||
|
||||
#endif
|
1
custom_mutators/honggfuzz/util.h
Symbolic link
1
custom_mutators/honggfuzz/util.h
Symbolic link
@ -0,0 +1 @@
|
||||
common.h
|
@ -15,14 +15,14 @@ libradamsa.a: libradamsa.c radamsa.h
|
||||
@echo " ***************************************************************"
|
||||
@echo " * Compiling libradamsa, wait some minutes (~3 on modern CPUs) *"
|
||||
@echo " ***************************************************************"
|
||||
$(CC) -fPIC $(CFLAGS) -I $(CUR_DIR) -o libradamsa.a -c libradamsa.c
|
||||
$(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I $(CUR_DIR) -o libradamsa.a -c libradamsa.c
|
||||
|
||||
radamsa-mutator.so: radamsa-mutator.c libradamsa.a
|
||||
$(CC) $(CFLAGS) -g -I. -I../../include -shared -fPIC -c radamsa-mutator.c
|
||||
$(CC) $(CFLAGS) -shared -fPIC -o radamsa-mutator.so radamsa-mutator.o libradamsa.a
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -g -I. -I../../include -shared -fPIC -c radamsa-mutator.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC -o radamsa-mutator.so radamsa-mutator.o libradamsa.a
|
||||
|
||||
test: libradamsa.a libradamsa-test.c
|
||||
$(CC) $(CFLAGS) -I $(CUR_DIR) -o libradamsa-test libradamsa-test.c libradamsa.a
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I $(CUR_DIR) -o libradamsa-test libradamsa-test.c libradamsa.a
|
||||
./libradamsa-test libradamsa-test.c | grep "library test passed"
|
||||
rm /tmp/libradamsa-*.fuzz
|
||||
|
||||
|
@ -324,7 +324,7 @@ static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
|
||||
}
|
||||
|
||||
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
|
||||
static inline void swap_bufs(void **buf1, size_t *size1, void **buf2,
|
||||
static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2,
|
||||
size_t *size2) {
|
||||
|
||||
void * scratch_buf = *buf1;
|
||||
|
@ -3,10 +3,12 @@
|
||||
# charset
|
||||
"\\ansi"
|
||||
"\\mac"
|
||||
"\\pc"
|
||||
"\\pca"
|
||||
|
||||
# font table
|
||||
"\\fnil"
|
||||
"\\fRoman"
|
||||
"\\fswiss"
|
||||
"\\fmodern"
|
||||
"\\fscript"
|
||||
@ -28,6 +30,7 @@
|
||||
"\\cb"
|
||||
|
||||
# pictures
|
||||
"\\pict"
|
||||
"\\macpict"
|
||||
"\\pmmetafile"
|
||||
"\\wmetafile"
|
||||
@ -40,36 +43,15 @@
|
||||
"\\pich"
|
||||
"\\picwgoal"
|
||||
"\\pichgoal"
|
||||
"\\picscalex"
|
||||
"\\picscaley"
|
||||
"\\picscaled"
|
||||
"\\piccropt"
|
||||
"\\piccropb"
|
||||
"\\piccropl"
|
||||
"\\piccropr"
|
||||
"\\brdrs"
|
||||
"\\brdrdb"
|
||||
"\\brdrth"
|
||||
"\\brdrsh"
|
||||
"\\brdrdot"
|
||||
"\\brdrhair"
|
||||
"\\brdrw"
|
||||
"\\brdrcf"
|
||||
"\\shading"
|
||||
"\\bghoriz"
|
||||
"\\bgvert"
|
||||
"\\bgfdiag"
|
||||
"\\bgbdiag"
|
||||
"\\bgcross"
|
||||
"\\bgdcross"
|
||||
"\\bgdkhoriz"
|
||||
"\\bgdkvert"
|
||||
"\\bgdkfdiag"
|
||||
"\\bgdkbdiag"
|
||||
"\\bgdkcross"
|
||||
"\\bgdkdcross"
|
||||
"\\cfpat"
|
||||
"\\cbpat"
|
||||
"\\bin"
|
||||
# these strings are probably not necessary
|
||||
"MM_TEXT"
|
||||
"MM_LOMETRIC"
|
||||
"MM_HIMETRIC"
|
||||
@ -86,25 +68,31 @@
|
||||
"PU_HIENGLISH"
|
||||
"PU_TWIPS"
|
||||
|
||||
# headers and gooters
|
||||
"\headerr"
|
||||
"\headerf"
|
||||
"\footerl"
|
||||
"\footerr"
|
||||
"\footerf"
|
||||
# headers and footers
|
||||
"\\headerl"
|
||||
"\\headerr"
|
||||
"\\headerf"
|
||||
"\\footerl"
|
||||
"\\footerr"
|
||||
"\\footerf"
|
||||
|
||||
# misc
|
||||
"\\chftn"
|
||||
"\\*\\footnote"
|
||||
"\\*\\annotation"
|
||||
"\\xe"
|
||||
"\\txe"
|
||||
"\\rxe"
|
||||
"\\bxe"
|
||||
"\\ixe"
|
||||
"\\tcf"
|
||||
"\\tcl"
|
||||
"\\*\\bkmkstart"
|
||||
"\\*\\bkmkend"
|
||||
"\\bkmkcolf"
|
||||
"\\bkmkcoll"
|
||||
|
||||
# metadata
|
||||
"\\info"
|
||||
"\\title"
|
||||
"\\subject"
|
||||
"\\author"
|
||||
@ -128,10 +116,13 @@
|
||||
"\\nofwords"
|
||||
"\\nofchars"
|
||||
"\\id"
|
||||
"\\field"
|
||||
"\\flddirty"
|
||||
"\\fldedit"
|
||||
"\\fldlock"
|
||||
"\\fldpriv"
|
||||
"\\*\\fldinst"
|
||||
"\\fldrslt"
|
||||
|
||||
# objects
|
||||
"\\objemb"
|
||||
@ -166,16 +157,14 @@
|
||||
# macintosh editor
|
||||
"\\bkmkpub"
|
||||
"\\pubauto"
|
||||
"\\objalias"
|
||||
"\\objsect"
|
||||
|
||||
# formating
|
||||
"\\deftab"
|
||||
"\\hyphhotz"
|
||||
"\\linestart"
|
||||
"\\fracwidth"
|
||||
"\\*\nextfile"
|
||||
"\\*\template"
|
||||
"\\*\\nextfile"
|
||||
"\\*\\template"
|
||||
"\\makebackup"
|
||||
"\\defformat"
|
||||
"\\psover"
|
||||
|
@ -9,6 +9,83 @@ 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.68c (release)
|
||||
- added the GSoC excellent afl++ grammar mutator by Shengtuo to our
|
||||
custom_mutators/ (see custom_mutators/README.md) - or get it here:
|
||||
https://github.com/AFLplusplus/Grammar-Mutator
|
||||
- a few QOL changes for Apple and its outdated gmake
|
||||
- afl-fuzz:
|
||||
- fix for auto dictionary entries found during fuzzing to not throw out
|
||||
a -x dictionary
|
||||
- added total execs done to plot file
|
||||
- AFL_MAX_DET_EXTRAS env variable added to control the amount of
|
||||
deterministic dict entries without recompiling.
|
||||
- AFL_FORKSRV_INIT_TMOUT env variable added to control the time to wait
|
||||
for the forkserver to come up without the need to increase the overall
|
||||
timeout.
|
||||
- bugfix for cmplog that results in a heap overflow based on target data
|
||||
(thanks to the magma team for reporting!)
|
||||
- write fuzzing setup into out/fuzzer_setup (environment variables and
|
||||
command line)
|
||||
- custom mutators:
|
||||
- added afl_custom_fuzz_count/fuzz_count function to allow specifying
|
||||
the number of fuzz attempts for custom_fuzz
|
||||
- llvm_mode:
|
||||
- ported SanCov to LTO, and made it the default for LTO. better
|
||||
instrumentation locations
|
||||
- Further llvm 12 support (fast moving target like afl++ :-) )
|
||||
- deprecated LLVM SKIPSINGLEBLOCK env environment
|
||||
|
||||
|
||||
### Version ++2.67c (release)
|
||||
- Support for improved afl++ snapshot module:
|
||||
https://github.com/AFLplusplus/AFL-Snapshot-LKM
|
||||
- Due to the instrumentation needing more memory, the initial memory sizes
|
||||
for -m have been increased
|
||||
- afl-fuzz:
|
||||
- added -F option to allow -M main fuzzers to sync to foreign fuzzers,
|
||||
e.g. honggfuzz or libfuzzer
|
||||
- added -b option to bind to a specific CPU
|
||||
- eliminated CPU affinity race condition for -S/-M runs
|
||||
- expanded havoc mode added, on no cycle finds add extra splicing and
|
||||
MOpt into the mix
|
||||
- fixed a bug in redqueen for strings and made deterministic with -s
|
||||
- Compiletime autodictionary fixes
|
||||
- llvm_mode:
|
||||
- now supports llvm 12
|
||||
- support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous
|
||||
AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and
|
||||
are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm
|
||||
sancov, and also supports function matching :)
|
||||
- added neverzero counting to trace-pc/pcgard
|
||||
- fixes for laf-intel float splitting (thanks to mark-griffin for
|
||||
reporting)
|
||||
- fixes for llvm 4.0
|
||||
- skipping ctors and ifuncs for instrumentation
|
||||
- LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR
|
||||
for a fixed map address (eg. 0x10000)
|
||||
- LTO: improved stability for persistent mode, no other instrumentation
|
||||
has that advantage
|
||||
- LTO: fixed autodict for long strings
|
||||
- LTO: laf-intel and redqueen/cmplog are now applied at link time
|
||||
to prevent llvm optimizing away the splits
|
||||
- LTO: autodictionary mode is a fixed default now
|
||||
- LTO: instrim instrumentation disabled, only classic support used
|
||||
as it is always better
|
||||
- LTO: env var AFL_LLVM_DOCUMENT_IDS=file will document which edge ID
|
||||
was given to which function during compilation
|
||||
- LTO: single block functions were not implemented by default, fixed
|
||||
- LTO: AFL_LLVM_SKIP_NEVERZERO behaviour was inversed, fixed
|
||||
- setting AFL_LLVM_LAF_SPLIT_FLOATS now activates
|
||||
AFL_LLVM_LAF_SPLIT_COMPARES
|
||||
- support for -E and -shared compilation runs
|
||||
- added honggfuzz mangle as a custom mutator in custom_mutators/honggfuzz
|
||||
- added afl-frida gum solution to examples/afl_frida (mostly imported
|
||||
from https://github.com/meme/hotwax/)
|
||||
- small fixes to afl-plot, afl-whatsup and man page creation
|
||||
- new README, added FAQ
|
||||
|
||||
|
||||
### Version ++2.66c (release)
|
||||
- renamed the main branch on Github to "stable"
|
||||
- renamed master/slave to main/secondary
|
||||
@ -348,7 +425,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
- big code refactoring:
|
||||
* all includes are now in include/
|
||||
* all afl sources are now in src/ - see src/README.md
|
||||
* afl-fuzz was splitted up in various individual files for including
|
||||
* afl-fuzz was split up in various individual files for including
|
||||
functionality in other programs (e.g. forkserver, memory map, etc.)
|
||||
for better readability.
|
||||
* new code indention everywhere
|
||||
|
220
docs/FAQ.md
Normal file
220
docs/FAQ.md
Normal file
@ -0,0 +1,220 @@
|
||||
# Frequently asked questions about afl++
|
||||
|
||||
## Contents
|
||||
|
||||
* [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl)
|
||||
* [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed)
|
||||
* [How do I fuzz a network service?](#how-do-i-fuzz-a-network-service)
|
||||
* [How do I fuzz a GUI program?](#how-do-i-fuzz-a-gui-program)
|
||||
* [What is an edge?](#what-is-an-edge)
|
||||
* [Why is my stability below 100%?](#why-is-my-stability-below-100)
|
||||
* [How can I improve the stability value?](#how-can-i-improve-the-stability-value)
|
||||
|
||||
If you find an interesting or important question missing, submit it via
|
||||
[https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues)
|
||||
|
||||
## What is the difference between afl and afl++?
|
||||
|
||||
American Fuzzy Lop (AFL) was developed by Michał "lcamtuf" Zalewski starting in
|
||||
2013/2014, and when he left Google end of 2017 he stopped developing it.
|
||||
|
||||
At the end of 2019 the Google fuzzing team took over maintenance of AFL, however
|
||||
it is only accepting PRs from the community and is not developing enhancements
|
||||
anymore.
|
||||
|
||||
In the second quarter of 2019, 1 1/2 year later when no further development of
|
||||
AFL had happened and it became clear there would none be coming, afl++
|
||||
was born, where initially community patches were collected and applied
|
||||
for bug fixes and enhancements. Then from various AFL spin-offs - mostly academic
|
||||
research - features were integrated. This already resulted in a much advanced
|
||||
AFL.
|
||||
|
||||
Until the end of 2019 the afl++ team had grown to four active developers which
|
||||
then implemented their own research and features, making it now by far the most
|
||||
flexible and feature rich guided fuzzer available as open source.
|
||||
And in independent fuzzing benchmarks it is one of the best fuzzers available,
|
||||
e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html)
|
||||
|
||||
## How to improve the fuzzing speed?
|
||||
|
||||
1. Use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended)
|
||||
2. Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase)
|
||||
3. Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase)
|
||||
4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to put the input file directory on a tempfs location, see [docs/env_variables.md](docs/env_variables.md)
|
||||
5. Improve Linux kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system less secure)
|
||||
6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem
|
||||
7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads)
|
||||
|
||||
## How do I fuzz a network service?
|
||||
|
||||
The short answer is - you cannot, at least not "out of the box".
|
||||
|
||||
Using a network channel is inadequate for several reasons:
|
||||
- it has a slow-down of x10-20 on the fuzzing speed
|
||||
- it does not scale to fuzzing multiple instances easily,
|
||||
- instead of one initial data packet often a back-and-forth interplay of packets is needed for stateful protocols (which is totally unsupported by most coverage aware fuzzers).
|
||||
|
||||
The established method to fuzz network services is to modify the source code
|
||||
to read from a file or stdin (fd 0) (or even faster via shared memory, combine
|
||||
this with persistent mode [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md)
|
||||
and you have a performance gain of x10 instead of a performance loss of over
|
||||
x10 - that is a x100 difference!).
|
||||
|
||||
If modifying the source is not an option (e.g. because you only have a binary
|
||||
and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD
|
||||
to emulate the network. This is also much faster than the real network would be.
|
||||
See [examples/socket_fuzzing/](../examples/socket_fuzzing/).
|
||||
|
||||
There is an outdated afl++ branch that implements networking if you are
|
||||
desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) -
|
||||
however a better option is AFLnet ([https://github.com/aflnet/aflnet](https://github.com/aflnet/aflnet))
|
||||
which allows you to define network state with different type of data packets.
|
||||
|
||||
## How do I fuzz a GUI program?
|
||||
|
||||
If the GUI program can read the fuzz data from a file (via the command line,
|
||||
a fixed location or via an environment variable) without needing any user
|
||||
interaction then it would be suitable for fuzzing.
|
||||
|
||||
Otherwise it is not possible without modifying the source code - which is a
|
||||
very good idea anyway as the GUI functionality is a huge CPU/time overhead
|
||||
for the fuzzing.
|
||||
|
||||
So create a new `main()` that just reads the test case and calls the
|
||||
functionality for processing the input that the GUI program is using.
|
||||
|
||||
## What is an "edge"?
|
||||
|
||||
A program contains `functions`, `functions` contain the compiled machine code.
|
||||
The compiled machine code in a `function` can be in a single or many `basic blocks`.
|
||||
A `basic block` is the largest possible number of subsequent machine code
|
||||
instructions that has exactly one entrypoint (which can be be entered by multiple other basic blocks)
|
||||
and runs linearly without branching or jumping to other addresses (except at the end).
|
||||
```
|
||||
function() {
|
||||
A:
|
||||
some
|
||||
code
|
||||
B:
|
||||
if (x) goto C; else goto D;
|
||||
C:
|
||||
some code
|
||||
goto E
|
||||
D:
|
||||
some code
|
||||
goto B
|
||||
E:
|
||||
return
|
||||
}
|
||||
```
|
||||
Every code block between two jump locations is a `basic block`.
|
||||
|
||||
An `edge` is then the unique relationship between two directly connected `basic blocks` (from the
|
||||
code example above):
|
||||
```
|
||||
Block A
|
||||
|
|
||||
v
|
||||
Block B <------+
|
||||
/ \ |
|
||||
v v |
|
||||
Block C Block D --+
|
||||
\
|
||||
v
|
||||
Block E
|
||||
```
|
||||
Every line between two blocks is an `edge`.
|
||||
Note that a few basic block loop to itself, this too would be an edge.
|
||||
|
||||
## Why is my stability below 100%?
|
||||
|
||||
Stability is measured by how many percent of the edges in the target are
|
||||
"stable". Sending the same input again and again should take the exact same
|
||||
path through the target every time. If that is the case, the stability is 100%.
|
||||
|
||||
If however randomness happens, e.g. a thread reading other external data,
|
||||
reaction to timing, etc. then in some of the re-executions with the same data
|
||||
the edge coverage result will be different accross runs.
|
||||
Those edges that change are then flagged "unstable".
|
||||
|
||||
The more "unstable" edges, the more difficult for afl++ to identify valid new
|
||||
paths.
|
||||
|
||||
A value above 90% is usually fine and a value above 80% is also still ok, and
|
||||
even a value above 20% can still result in successful finds of bugs.
|
||||
However, it is recommended that for values below 90% or 80% you should take
|
||||
countermeasures to improve stability.
|
||||
|
||||
## How can I improve the stability value?
|
||||
|
||||
For fuzzing a 100% stable target that covers all edges is the best case.
|
||||
A 90% stable target that covers all edges is however better than a 100% stable
|
||||
target that ignores 10% of the edges.
|
||||
|
||||
With instability you basically have a partial coverage loss on an edge, with
|
||||
ignored functions you have a full loss on that edges.
|
||||
|
||||
There are functions that are unstable, but also provide value to coverage, eg
|
||||
init functions that use fuzz data as input for example.
|
||||
If however a function that has nothing to do with the input data is the
|
||||
source of instability, e.g. checking jitter, or is a hash map function etc.
|
||||
then it should not be instrumented.
|
||||
|
||||
To be able to exclude these functions (based on AFL++'s measured stability)
|
||||
the following process will allow to identify functions with variable edges.
|
||||
|
||||
Four steps are required to do this and it also requires quite some knowledge
|
||||
of coding and/or disassembly and is effectively possible only with
|
||||
afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation.
|
||||
|
||||
1. First step: Identify which edge ID numbers are unstable
|
||||
|
||||
run the target with `export AFL_DEBUG=1` for a few minutes then terminate.
|
||||
The out/fuzzer_stats file will then show the edge IDs that were identified
|
||||
as unstable.
|
||||
|
||||
2. Second step: Find the responsible function(s).
|
||||
|
||||
a) For LTO instrumented binaries this can be documented during compile
|
||||
time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`.
|
||||
This file will have one assigned edge ID and the corresponding
|
||||
function per line.
|
||||
|
||||
b) For PCGUARD instrumented binaries it is much more difficult. Here you
|
||||
can either modify the __sanitizer_cov_trace_pc_guard function in
|
||||
llvm_mode/afl-llvm-rt.o.c to write a backtrace to a file if the ID in
|
||||
__afl_area_ptr[*guard] is one of the unstable edge IDs.
|
||||
(Example code is already there).
|
||||
Then recompile and reinstall llvm_mode and rebuild your target.
|
||||
Run the recompiled target with afl-fuzz for a while and then check the
|
||||
file that you wrote with the backtrace information.
|
||||
Alternatively you can use `gdb` to hook __sanitizer_cov_trace_pc_guard_init
|
||||
on start, check to which memory address the edge ID value is written
|
||||
and set a write breakpoint to that address (`watch 0x.....`).
|
||||
|
||||
c) in all other instrumentation types this is not possible. So just
|
||||
recompile with the two mentioned above. This is just for
|
||||
identifying the functions that have unstable edges.
|
||||
|
||||
3. Third step: create a text file with the filenames/functions
|
||||
|
||||
Identify which source code files contain the functions that you need to
|
||||
remove from instrumentation, or just specify the functions you want to
|
||||
skip for instrumentation. Note that optimization might inline functions!
|
||||
|
||||
Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md)
|
||||
If PCGUARD is used, then you need to follow this guide (needs llvm 12+!):
|
||||
[http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation)
|
||||
|
||||
Only exclude those functions from instrumentation that provide no value
|
||||
for coverage - that is if it does not process any fuzz data directly
|
||||
or indirectly (e.g. hash maps, thread management etc.).
|
||||
If however a function directly or indirectly handles fuzz data then you
|
||||
should not put the function in a deny instrumentation list and rather
|
||||
live with the instability it comes with.
|
||||
|
||||
4. Fourth step: recompile the target
|
||||
|
||||
Recompile, fuzz it, be happy :)
|
||||
|
||||
This link explains this process for [Fuzzbench](https://github.com/google/fuzzbench/issues/677)
|
@ -6,13 +6,18 @@
|
||||
However, if there is only the binary program and no source code available,
|
||||
then standard `afl-fuzz -n` (non-instrumented mode) is not effective.
|
||||
|
||||
The following is a description of how these binaries can be fuzzed with afl++
|
||||
The following is a description of how these binaries can be fuzzed with afl++.
|
||||
|
||||
|
||||
## TL;DR:
|
||||
|
||||
qemu_mode in persistent mode is the fastest - if the stability is
|
||||
high enough. Otherwise try retrowrite, afl-dyninst and if these
|
||||
fail too then standard qemu_mode with AFL_ENTRYPOINT to where you need it.
|
||||
fail too then try standard qemu_mode with AFL_ENTRYPOINT to where you need it.
|
||||
|
||||
If your target is a library use examples/afl_frida/.
|
||||
|
||||
If your target is non-linux then use unicorn_mode/.
|
||||
|
||||
|
||||
## QEMU
|
||||
@ -24,10 +29,10 @@
|
||||
|
||||
The speed decrease is at about 50%.
|
||||
However various options exist to increase the speed:
|
||||
- using AFL_ENTRYPOINT to move the forkserver to a later basic block in
|
||||
- using AFL_ENTRYPOINT to move the forkserver entry to a later basic block in
|
||||
the binary (+5-10% speed)
|
||||
- using persistent mode [qemu_mode/README.persistent.md](../qemu_mode/README.persistent.md)
|
||||
this will result in 150-300% overall speed - so 3-8x the original
|
||||
this will result in 150-300% overall speed increase - so 3-8x the original
|
||||
qemu_mode speed!
|
||||
- using AFL_CODE_START/AFL_CODE_END to only instrument specific parts
|
||||
|
||||
@ -57,6 +62,20 @@
|
||||
As it is included in afl++ this needs no URL.
|
||||
|
||||
|
||||
## AFL FRIDA
|
||||
|
||||
If you want to fuzz a binary-only shared library then you can fuzz it with
|
||||
frida-gum via examples/afl_frida/, you will have to write a harness to
|
||||
call the target function in the library, use afl-frida.c as a template.
|
||||
|
||||
|
||||
## AFL UNTRACER
|
||||
|
||||
If you want to fuzz a binary-only shared library then you can fuzz it with
|
||||
examples/afl_untracer/, use afl-untracer.c as a template.
|
||||
It is slower than AFL FRIDA (see above).
|
||||
|
||||
|
||||
## DYNINST
|
||||
|
||||
Dyninst is a binary instrumentation framework similar to Pintool and
|
||||
@ -85,7 +104,7 @@
|
||||
|
||||
## RETROWRITE
|
||||
|
||||
If you have an x86/x86_64 binary that still has it's symbols, is compiled
|
||||
If you have an x86/x86_64 binary that still has its symbols, is compiled
|
||||
with position independant code (PIC/PIE) and does not use most of the C++
|
||||
features then the retrowrite solution might be for you.
|
||||
It decompiles to ASM files which can then be instrumented with afl-gcc.
|
||||
@ -129,7 +148,7 @@
|
||||
## CORESIGHT
|
||||
|
||||
Coresight is ARM's answer to Intel's PT.
|
||||
There is no implementation so far which handle coresight and getting
|
||||
There is no implementation so far which handles coresight and getting
|
||||
it working on an ARM Linux is very difficult due to custom kernel building
|
||||
on embedded systems is difficult. And finding one that has coresight in
|
||||
the ARM chip is difficult too.
|
||||
|
@ -32,11 +32,12 @@ performed with the custom mutator.
|
||||
C/C++:
|
||||
```c
|
||||
void *afl_custom_init(afl_t *afl, unsigned int seed);
|
||||
uint32_t afl_custom_fuzz_count(void *data, const u8 *buf, size_t buf_size);
|
||||
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_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) {
|
||||
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);
|
||||
@ -49,6 +50,9 @@ Python:
|
||||
def init(seed):
|
||||
pass
|
||||
|
||||
def fuzz_count(buf, add_buf, max_size):
|
||||
return cnt
|
||||
|
||||
def fuzz(buf, add_buf, max_size):
|
||||
return mutated_out
|
||||
|
||||
@ -88,6 +92,14 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
|
||||
This method determines whether the custom fuzzer should fuzz the current
|
||||
queue entry or not
|
||||
|
||||
- `fuzz_count` (optional):
|
||||
|
||||
When a queue entry is selected to be fuzzed, afl-fuzz selects the number
|
||||
of fuzzing attempts with this input based on a few factors.
|
||||
If however the custom mutator wants to set this number instead on how often
|
||||
it is called for a specific queue entry, use this function.
|
||||
This function in mostly useful if **not** `AFL_CUSTOM_MUTATOR_ONLY` is used.
|
||||
|
||||
- `fuzz` (optional):
|
||||
|
||||
This method performs custom mutations on a given input. It also accepts an
|
||||
|
@ -10,8 +10,8 @@
|
||||
Because they can't directly accept command-line options, the compile-time
|
||||
tools make fairly broad use of environmental variables:
|
||||
|
||||
- Most afl tools do not print any ouput if stout/stderr are redirected.
|
||||
If you want to have the output into a file then set the AFL_DEBUG
|
||||
- Most afl tools do not print any output if stdout/stderr are redirected.
|
||||
If you want to save the output in a file then set the AFL_DEBUG
|
||||
environment variable.
|
||||
This is sadly necessary for various build processes which fail otherwise.
|
||||
|
||||
@ -44,7 +44,7 @@ tools make fairly broad use of environmental variables:
|
||||
you instrument hand-written assembly when compiling clang code by plugging
|
||||
a normalizer into the chain. (There is no equivalent feature for GCC.)
|
||||
|
||||
- Setting AFL_INST_RATIO to a percentage between 0 and 100% controls the
|
||||
- Setting AFL_INST_RATIO to a percentage between 0% and 100% controls the
|
||||
probability of instrumenting every branch. This is (very rarely) useful
|
||||
when dealing with exceptionally complex programs that saturate the output
|
||||
bitmap. Examples include v8, ffmpeg, and perl.
|
||||
@ -83,17 +83,12 @@ 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
|
||||
created.
|
||||
|
||||
- AFL_INST_RATIO, as we switched for instrim instrumentation which
|
||||
is more effective but makes not much sense together with this option.
|
||||
- AFL_INST_RATIO, as we by default use collision free instrumentation.
|
||||
|
||||
Then there are a few specific features that are only available in llvm_mode:
|
||||
|
||||
@ -121,18 +116,17 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
built if LLVM 11 or newer is used.
|
||||
|
||||
- AFL_LLVM_INSTRUMENT=CFG will use Control Flow Graph instrumentation.
|
||||
(recommended)
|
||||
|
||||
- AFL_LLVM_LTO_AUTODICTIONARY will generate a dictionary in the target
|
||||
binary based on string compare and memory compare functions.
|
||||
afl-fuzz will automatically get these transmitted when starting to
|
||||
fuzz.
|
||||
(not recommended for afl-clang-fast, default for afl-clang-lto as there
|
||||
it is a different and better kind of instrumentation.)
|
||||
|
||||
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
|
||||
These are used if several seperated instrumentations are performed which
|
||||
are then later combined.
|
||||
|
||||
- AFL_LLVM_DOCUMENT_IDS=file will document to a file which edge ID was given
|
||||
to which function. This helps to identify functions with variable bytes
|
||||
or which functions were touched by an input.
|
||||
- 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)
|
||||
@ -204,14 +198,15 @@ Then there are a few specific features that are only available in llvm_mode:
|
||||
|
||||
See llvm_mode/README.laf-intel.md for more information.
|
||||
|
||||
### INSTRUMENT_FILE
|
||||
### INSTRUMENT LIST (selectively instrument files and functions)
|
||||
|
||||
This feature allows selectively instrumentation of the source
|
||||
This feature allows selective instrumentation of the source
|
||||
|
||||
- Setting AFL_LLVM_INSTRUMENT_FILE with a filename will only instrument those
|
||||
files that match the names listed in this file.
|
||||
- Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or
|
||||
function will only instrument (or skip) those files that match the names
|
||||
listed in the specified file.
|
||||
|
||||
See llvm_mode/README.instrument_file.md for more information.
|
||||
See llvm_mode/README.instrument_list.md for more information.
|
||||
|
||||
### NOT_ZERO
|
||||
|
||||
@ -243,7 +238,7 @@ Then there are a few specific features that are only available in the gcc_plugin
|
||||
- Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those
|
||||
files that match the names listed in this file (one filename per line).
|
||||
|
||||
See gcc_plugin/README.instrument_file.md for more information.
|
||||
See gcc_plugin/README.instrument_list.md for more information.
|
||||
|
||||
## 3) Settings for afl-fuzz
|
||||
|
||||
@ -254,15 +249,6 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
useful if you can't change the defaults (e.g., no root access to the
|
||||
system) and are OK with some performance loss.
|
||||
|
||||
- Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to
|
||||
fork + execve() call for every tested input. This is useful mostly when
|
||||
working with unruly libraries that create threads or do other crazy
|
||||
things when initializing (before the instrumentation has a chance to run).
|
||||
|
||||
Note that this setting inhibits some of the user-friendly diagnostics
|
||||
normally done when starting up the forkserver and causes a pretty
|
||||
significant performance drop.
|
||||
|
||||
- AFL_EXIT_WHEN_DONE causes afl-fuzz to terminate when all existing paths
|
||||
have been fuzzed and there were no new finds for a while. This would be
|
||||
normally indicated by the cycle counter in the UI turning green. May be
|
||||
@ -273,6 +259,9 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
the target. This must be equal or larger than the size the target was
|
||||
compiled with.
|
||||
|
||||
- Setting AFL_DISABLE_TRIM tells afl-fuzz to no trim test cases. This is
|
||||
usually a bad idea!
|
||||
|
||||
- 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).
|
||||
@ -289,6 +278,14 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
don't want AFL to spend too much time classifying that stuff and just
|
||||
rapidly put all timeouts in that bin.
|
||||
|
||||
- Setting AFL_FORKSRV_INIT_TMOUT allows yout to specify a different timeout
|
||||
to wait for the forkserver to spin up. The default is the `-t` value times
|
||||
`FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the
|
||||
default would wait `1000` milis. Setting a different time here is useful
|
||||
if the target has a very slow startup time, for example when doing
|
||||
full-system fuzzing or emulation, but you don't want the actual runs
|
||||
to wait too long for timeouts.
|
||||
|
||||
- AFL_NO_ARITH causes AFL to skip most of the deterministic arithmetics.
|
||||
This can be useful to speed up the fuzzing of text-based file formats.
|
||||
|
||||
@ -338,6 +335,13 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
|
||||
- In QEMU mode (-Q), AFL_PATH will be searched for afl-qemu-trace.
|
||||
|
||||
- Setting AFL_CYCLE_SCHEDULES will switch to a different schedule everytime
|
||||
a cycle is finished.
|
||||
|
||||
- Setting AFL_EXPAND_HAVOC_NOW will start in the extended havoc mode that
|
||||
includes costly mutations. afl-fuzz automatically enables this mode when
|
||||
deemed useful otherwise.
|
||||
|
||||
- Setting AFL_PRELOAD causes AFL to set LD_PRELOAD for the target binary
|
||||
without disrupting the afl-fuzz process itself. This is useful, among other
|
||||
things, for bootstrapping libdislocator.so.
|
||||
@ -365,6 +369,25 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
for an existing out folder, even if a different `-i` was provided.
|
||||
Without this setting, afl-fuzz will refuse execution for a long-fuzzed out dir.
|
||||
|
||||
- Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to
|
||||
fork + execve() call for every tested input. This is useful mostly when
|
||||
working with unruly libraries that create threads or do other crazy
|
||||
things when initializing (before the instrumentation has a chance to run).
|
||||
|
||||
Note that this setting inhibits some of the user-friendly diagnostics
|
||||
normally done when starting up the forkserver and causes a pretty
|
||||
significant performance drop.
|
||||
|
||||
- Setting AFL_MAX_DET_EXTRAS changes the count of dictionary entries/extras
|
||||
(default 200), after which the entries will be used probabilistically.
|
||||
So, if the dict/extras file (`-x`) contains more tokens than this threshold,
|
||||
not all of the tokens will be used in each fuzzing step, every time.
|
||||
Instead, there is a chance that the entry will be skipped during fuzzing.
|
||||
This makes sure that the fuzzer doesn't spend all its time only inserting
|
||||
the extras, but will still do other mutations. However, it decreases the
|
||||
likelihood for each token to be inserted, before the next queue entry is fuzzed.
|
||||
Either way, all tokens will be used eventually, in a longer fuzzing campaign.
|
||||
|
||||
- Outdated environment variables that are that not supported anymore:
|
||||
AFL_DEFER_FORKSRV
|
||||
AFL_PERSISTENT
|
||||
|
@ -10,8 +10,8 @@ n-core system, you can almost always run around n concurrent fuzzing jobs with
|
||||
virtually no performance hit (you can use the afl-gotcpu tool to make sure).
|
||||
|
||||
In fact, if you rely on just a single job on a multi-core system, you will
|
||||
be underutilizing the hardware. So, parallelization is usually the right
|
||||
way to go.
|
||||
be underutilizing the hardware. So, parallelization is always the right way to
|
||||
go.
|
||||
|
||||
When targeting multiple unrelated binaries or using the tool in
|
||||
"non-instrumented" (-n) mode, it is perfectly fine to just start up several
|
||||
@ -65,22 +65,7 @@ still perform deterministic checks; while the secondary instances will
|
||||
proceed straight to random tweaks.
|
||||
|
||||
Note that you must always have one -M main instance!
|
||||
|
||||
Note that running multiple -M instances is wasteful, although there is an
|
||||
experimental support for parallelizing the deterministic checks. To leverage
|
||||
that, you need to create -M instances like so:
|
||||
|
||||
```
|
||||
./afl-fuzz -i testcase_dir -o sync_dir -M mainA:1/3 [...]
|
||||
./afl-fuzz -i testcase_dir -o sync_dir -M mainB:2/3 [...]
|
||||
./afl-fuzz -i testcase_dir -o sync_dir -M mainC:3/3 [...]
|
||||
```
|
||||
|
||||
...where the first value after ':' is the sequential ID of a particular main
|
||||
instance (starting at 1), and the second value is the total number of fuzzers to
|
||||
distribute the deterministic fuzzing across. Note that if you boot up fewer
|
||||
fuzzers than indicated by the second number passed to -M, you may end up with
|
||||
poor coverage.
|
||||
Running multiple -M instances is wasteful!
|
||||
|
||||
You can also monitor the progress of your jobs from the command line with the
|
||||
provided afl-whatsup tool. When the instances are no longer finding new paths,
|
||||
@ -99,53 +84,88 @@ example may be:
|
||||
This is not a concern if you use @@ without -f and let afl-fuzz come up with the
|
||||
file name.
|
||||
|
||||
## 3) Multi-system parallelization
|
||||
## 3) Multiple -M mains
|
||||
|
||||
|
||||
There is support for parallelizing the deterministic checks.
|
||||
This is only needed where
|
||||
|
||||
1. many new paths are found fast over a long time and it looks unlikely that
|
||||
main node will ever catch up, and
|
||||
2. deterministic fuzzing is actively helping path discovery (you can see this
|
||||
in the main node for the first for lines in the "fuzzing strategy yields"
|
||||
section. If the ration `found/attemps` is high, then it is effective. It
|
||||
most commonly isn't.)
|
||||
|
||||
Only if both are true it is beneficial to have more than one main.
|
||||
You can leverage this by creating -M instances like so:
|
||||
|
||||
```
|
||||
./afl-fuzz -i testcase_dir -o sync_dir -M mainA:1/3 [...]
|
||||
./afl-fuzz -i testcase_dir -o sync_dir -M mainB:2/3 [...]
|
||||
./afl-fuzz -i testcase_dir -o sync_dir -M mainC:3/3 [...]
|
||||
```
|
||||
|
||||
... where the first value after ':' is the sequential ID of a particular main
|
||||
instance (starting at 1), and the second value is the total number of fuzzers to
|
||||
distribute the deterministic fuzzing across. Note that if you boot up fewer
|
||||
fuzzers than indicated by the second number passed to -M, you may end up with
|
||||
poor coverage.
|
||||
|
||||
## 4) Syncing with non-afl fuzzers or independant instances
|
||||
|
||||
A -M main node can be told with the `-F other_fuzzer_queue_directory` option
|
||||
to sync results from other fuzzers, e.g. libfuzzer or honggfuzz.
|
||||
|
||||
Only the specified directory will by synced into afl, not subdirectories.
|
||||
The specified directory does not need to exist yet at the start of afl.
|
||||
|
||||
The `-F` option can be passed to the main node several times.
|
||||
|
||||
## 5) Multi-system parallelization
|
||||
|
||||
The basic operating principle for multi-system parallelization is similar to
|
||||
the mechanism explained in section 2. The key difference is that you need to
|
||||
write a simple script that performs two actions:
|
||||
|
||||
- Uses SSH with authorized_keys to connect to every machine and retrieve
|
||||
a tar archive of the /path/to/sync_dir/<fuzzer_id>/queue/ directories for
|
||||
every <fuzzer_id> local to the machine. It's best to use a naming scheme
|
||||
that includes host name in the fuzzer ID, so that you can do something
|
||||
like:
|
||||
a tar archive of the /path/to/sync_dir/<main_node(s)> directory local to
|
||||
the machine.
|
||||
It is best to use a naming scheme that includes host name and it's being
|
||||
a main node (e.g. main1, main2) in the fuzzer ID, so that you can do
|
||||
something like:
|
||||
|
||||
```sh
|
||||
for s in {1..10}; do
|
||||
ssh user@host${s} "tar -czf - sync/host${s}_fuzzid*/[qf]*" >host${s}.tgz
|
||||
for host in `cat HOSTLIST`; do
|
||||
ssh user@$host "tar -czf - sync/$host_main*/" > $host.tgz
|
||||
done
|
||||
```
|
||||
|
||||
- Distributes and unpacks these files on all the remaining machines, e.g.:
|
||||
|
||||
```sh
|
||||
for s in {1..10}; do
|
||||
for d in {1..10}; do
|
||||
test "$s" = "$d" && continue
|
||||
ssh user@host${d} 'tar -kxzf -' <host${s}.tgz
|
||||
for srchost in `cat HOSTLIST`; do
|
||||
for dsthost in `cat HOSTLIST`; do
|
||||
test "$srchost" = "$dsthost" && continue
|
||||
ssh user@$srchost 'tar -kxzf -' < $dsthost.tgz
|
||||
done
|
||||
done
|
||||
```
|
||||
|
||||
There is an example of such a script in examples/distributed_fuzzing/;
|
||||
you can also find a more featured, experimental tool developed by
|
||||
Martijn Bogaard at:
|
||||
There is an example of such a script in examples/distributed_fuzzing/.
|
||||
|
||||
https://github.com/MartijnB/disfuzz-afl
|
||||
There are other (older) more featured, experimental tools:
|
||||
* https://github.com/richo/roving
|
||||
* https://github.com/MartijnB/disfuzz-afl
|
||||
|
||||
Another client-server implementation from Richo Healey is:
|
||||
|
||||
https://github.com/richo/roving
|
||||
|
||||
Note that these third-party tools are unsafe to run on systems exposed to the
|
||||
Internet or to untrusted users.
|
||||
However these do not support syncing just main nodes (yet).
|
||||
|
||||
When developing custom test case sync code, there are several optimizations
|
||||
to keep in mind:
|
||||
|
||||
- The synchronization does not have to happen very often; running the
|
||||
task every 30 minutes or so may be perfectly fine.
|
||||
task every 60 minutes or even less often at later fuzzing stages is
|
||||
fine
|
||||
|
||||
- There is no need to synchronize crashes/ or hangs/; you only need to
|
||||
copy over queue/* (and ideally, also fuzzer_stats).
|
||||
@ -171,19 +191,24 @@ to keep in mind:
|
||||
- You do not want a "main" instance of afl-fuzz on every system; you should
|
||||
run them all with -S, and just designate a single process somewhere within
|
||||
the fleet to run with -M.
|
||||
|
||||
- Syncing is only necessary for the main nodes on a system. It is possible
|
||||
to run main-less with only secondaries. However then you need to find out
|
||||
which secondary took over the temporary role to be the main node. Look for
|
||||
the `is_main_node` file in the fuzzer directories, eg. `sync-dir/hostname-*/is_main_node`
|
||||
|
||||
It is *not* advisable to skip the synchronization script and run the fuzzers
|
||||
directly on a network filesystem; unexpected latency and unkillable processes
|
||||
in I/O wait state can mess things up.
|
||||
|
||||
## 4) Remote monitoring and data collection
|
||||
## 6) Remote monitoring and data collection
|
||||
|
||||
You can use screen, nohup, tmux, or something equivalent to run remote
|
||||
instances of afl-fuzz. If you redirect the program's output to a file, it will
|
||||
automatically switch from a fancy UI to more limited status reports. There is
|
||||
also basic machine-readable information always written to the fuzzer_stats file
|
||||
in the output directory. Locally, that information can be interpreted with
|
||||
afl-whatsup.
|
||||
also basic machine-readable information which is always written to the
|
||||
fuzzer_stats file in the output directory. Locally, that information can be
|
||||
interpreted with afl-whatsup.
|
||||
|
||||
In principle, you can use the status screen of the main (-M) instance to
|
||||
monitor the overall fuzzing progress and decide when to stop. In this
|
||||
@ -200,7 +225,7 @@ Keep in mind that crashing inputs are *not* automatically propagated to the
|
||||
main instance, so you may still want to monitor for crashes fleet-wide
|
||||
from within your synchronization or health checking scripts (see afl-whatsup).
|
||||
|
||||
## 5) Asymmetric setups
|
||||
## 7) Asymmetric setups
|
||||
|
||||
It is perhaps worth noting that all of the following is permitted:
|
||||
|
||||
@ -216,7 +241,7 @@ It is perhaps worth noting that all of the following is permitted:
|
||||
the discovered test cases can have synergistic effects and improve the
|
||||
overall coverage.
|
||||
|
||||
(In this case, running one -M instance per each binary is a good plan.)
|
||||
(In this case, running one -M instance per target is necessary.)
|
||||
|
||||
- Having some of the fuzzers invoke the binary in different ways.
|
||||
For example, 'djpeg' supports several DCT modes, configurable with
|
||||
|
@ -67,7 +67,7 @@ to get to the important parts in the code.
|
||||
|
||||
If you are only interested in specific parts of the code being fuzzed, you can
|
||||
instrument_files the files that are actually relevant. This improves the speed and
|
||||
accuracy of afl. See llvm_mode/README.instrument_file.md
|
||||
accuracy of afl. See llvm_mode/README.instrument_list.md
|
||||
|
||||
Also use the InsTrim mode on larger binaries, this improves performance and
|
||||
coverage a lot.
|
||||
|
BIN
docs/screenshot.png
Normal file
BIN
docs/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
10
dynamic_list.txt
Normal file
10
dynamic_list.txt
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"__afl_area_ptr";
|
||||
"__afl_manual_init";
|
||||
"__afl_persistent_loop";
|
||||
"__afl_auto_init";
|
||||
"__afl_area_initial";
|
||||
"__afl_prev_loc";
|
||||
"__sanitizer_cov_trace_pc_guard";
|
||||
"__sanitizer_cov_trace_pc_guard_init";
|
||||
};
|
23
examples/afl_frida/GNUmakefile
Normal file
23
examples/afl_frida/GNUmakefile
Normal file
@ -0,0 +1,23 @@
|
||||
ifdef DEBUG
|
||||
OPT=-O0 -D_DEBUG=\"1\"
|
||||
else
|
||||
OPT=-O3 -funroll-loops
|
||||
endif
|
||||
|
||||
all: afl-frida libtestinstr.so
|
||||
|
||||
libfrida-gum.a:
|
||||
@echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest
|
||||
@exit 1
|
||||
|
||||
afl-frida: afl-frida.c libfrida-gum.a
|
||||
$(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread
|
||||
|
||||
libtestinstr.so: libtestinstr.c
|
||||
$(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c
|
||||
|
||||
clean:
|
||||
rm -f afl-frida *~ core *.o libtestinstr.so
|
||||
|
||||
deepclean: clean
|
||||
rm -f libfrida-gum.a frida-gum*
|
2
examples/afl_frida/Makefile
Normal file
2
examples/afl_frida/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
all:
|
||||
@echo please use GNU make, thanks!
|
34
examples/afl_frida/README.md
Normal file
34
examples/afl_frida/README.md
Normal file
@ -0,0 +1,34 @@
|
||||
# afl-frida - faster fuzzing of binary-only libraries
|
||||
|
||||
## Introduction
|
||||
|
||||
afl-frida is an example skeleton file which can easily be used to fuzz
|
||||
a closed source library.
|
||||
|
||||
It requires less memory and is x5-10 faster than qemu_mode but does not
|
||||
provide interesting features like compcov or cmplog.
|
||||
|
||||
## How-to
|
||||
|
||||
### Modify afl-frida.c
|
||||
|
||||
Read and modify afl-frida.c then `make`.
|
||||
To adapt afl-frida.c to your needs, read the header of the file and then
|
||||
search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations.
|
||||
|
||||
### Fuzzing
|
||||
|
||||
Example (after modifying afl-frida.c to your needs and compile it):
|
||||
```
|
||||
LD_LIBRARY_PATH=/path/to/the/target/library afl-fuzz -i in -o out -- ./afl-frida
|
||||
```
|
||||
(or even remote via afl-network-proxy).
|
||||
|
||||
# Speed and stability
|
||||
|
||||
The speed is very good, about x12 of fork() qemu_mode.
|
||||
However the stability is low. Reason is currently unknown.
|
||||
|
||||
# Background
|
||||
|
||||
This code is copied for a larger part from https://github.com/meme/hotwax
|
541
examples/afl_frida/afl-frida.c
Normal file
541
examples/afl_frida/afl-frida.c
Normal file
@ -0,0 +1,541 @@
|
||||
/*
|
||||
american fuzzy lop++ - afl-frida skeleton example
|
||||
-------------------------------------------------
|
||||
|
||||
Copyright 2020 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Written mostly by meme -> https://github.com/meme/hotwax
|
||||
|
||||
Modificationy by Marc Heuse <mh@mh-sec.de>
|
||||
|
||||
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. set the defines and function call parameters.
|
||||
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:"
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/shm.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <sys/wait.h>
|
||||
#include <sys/personality.h>
|
||||
#endif
|
||||
|
||||
int debug = 0;
|
||||
|
||||
// STEP 1:
|
||||
|
||||
// The presets are for the example libtestinstr.so:
|
||||
|
||||
/* What is the name of the library to fuzz */
|
||||
#define TARGET_LIBRARY "libtestinstr.so"
|
||||
|
||||
/* What is the name of the function to fuzz */
|
||||
#define TARGET_FUNCTION "testinstr"
|
||||
|
||||
/* here you need to specify the parameter for the target function */
|
||||
static void *(*o_function)(uint8_t *, int);
|
||||
|
||||
// END STEP 1
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type())
|
||||
G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM,
|
||||
FAKE_EVENT_SINK, GObject)
|
||||
|
||||
struct _GumFakeEventSink {
|
||||
|
||||
GObject parent;
|
||||
GumEventType mask;
|
||||
|
||||
};
|
||||
|
||||
GumEventSink *gum_fake_event_sink_new(void);
|
||||
void gum_fake_event_sink_reset(GumFakeEventSink *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
static void gum_fake_event_sink_iface_init(gpointer g_iface,
|
||||
gpointer iface_data);
|
||||
static void gum_fake_event_sink_finalize(GObject *obj);
|
||||
static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink);
|
||||
static void gum_fake_event_sink_process(GumEventSink *sink, const GumEvent *ev);
|
||||
void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output,
|
||||
gpointer user_data);
|
||||
void afl_setup(void);
|
||||
void afl_start_forkserver(void);
|
||||
int __afl_persistent_loop(unsigned int max_cnt);
|
||||
|
||||
static void gum_fake_event_sink_class_init(GumFakeEventSinkClass *klass) {
|
||||
|
||||
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||
object_class->finalize = gum_fake_event_sink_finalize;
|
||||
|
||||
}
|
||||
|
||||
static void gum_fake_event_sink_iface_init(gpointer g_iface,
|
||||
gpointer iface_data) {
|
||||
|
||||
GumEventSinkInterface *iface = (GumEventSinkInterface *)g_iface;
|
||||
iface->query_mask = gum_fake_event_sink_query_mask;
|
||||
iface->process = gum_fake_event_sink_process;
|
||||
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED(GumFakeEventSink, gum_fake_event_sink, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE(GUM_TYPE_EVENT_SINK,
|
||||
gum_fake_event_sink_iface_init))
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
// Shared memory fuzzing.
|
||||
int __afl_sharedmem_fuzzing = 1;
|
||||
extern unsigned int * __afl_fuzz_len;
|
||||
extern unsigned char *__afl_fuzz_ptr;
|
||||
|
||||
// Notify AFL about persistent mode.
|
||||
static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
|
||||
int __afl_persistent_loop(unsigned int);
|
||||
|
||||
// Notify AFL about deferred forkserver.
|
||||
static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
|
||||
void __afl_manual_init();
|
||||
|
||||
// Because we do our own logging.
|
||||
extern uint8_t * __afl_area_ptr;
|
||||
static __thread guint64 previous_pc;
|
||||
|
||||
// Frida stuff below.
|
||||
typedef struct {
|
||||
|
||||
GumAddress base_address;
|
||||
guint64 code_start, code_end;
|
||||
|
||||
} range_t;
|
||||
|
||||
inline static void afl_maybe_log(guint64 current_pc) {
|
||||
|
||||
// fprintf(stderr, "PC: %p ^ %p\n", current_pc, previous_pc);
|
||||
|
||||
current_pc = (current_pc >> 4) ^ (current_pc << 8);
|
||||
current_pc &= MAP_SIZE - 1;
|
||||
|
||||
__afl_area_ptr[current_pc ^ previous_pc]++;
|
||||
previous_pc = current_pc >> 1;
|
||||
|
||||
}
|
||||
|
||||
static void on_basic_block(GumCpuContext *context, gpointer user_data) {
|
||||
|
||||
afl_maybe_log((guint64)user_data);
|
||||
|
||||
}
|
||||
|
||||
void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output,
|
||||
gpointer user_data) {
|
||||
|
||||
range_t *range = (range_t *)user_data;
|
||||
|
||||
const cs_insn *instr;
|
||||
gboolean begin = TRUE;
|
||||
while (gum_stalker_iterator_next(iterator, &instr)) {
|
||||
|
||||
if (begin) {
|
||||
|
||||
if (instr->address >= range->code_start &&
|
||||
instr->address <= range->code_end) {
|
||||
|
||||
gum_stalker_iterator_put_callout(iterator, on_basic_block,
|
||||
(gpointer)instr->address, NULL);
|
||||
begin = FALSE;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gum_stalker_iterator_keep(iterator);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void gum_fake_event_sink_init(GumFakeEventSink *self) {
|
||||
|
||||
}
|
||||
|
||||
static void gum_fake_event_sink_finalize(GObject *obj) {
|
||||
|
||||
G_OBJECT_CLASS(gum_fake_event_sink_parent_class)->finalize(obj);
|
||||
|
||||
}
|
||||
|
||||
GumEventSink *gum_fake_event_sink_new(void) {
|
||||
|
||||
GumFakeEventSink *sink;
|
||||
sink = (GumFakeEventSink *)g_object_new(GUM_TYPE_FAKE_EVENT_SINK, NULL);
|
||||
return GUM_EVENT_SINK(sink);
|
||||
|
||||
}
|
||||
|
||||
void gum_fake_event_sink_reset(GumFakeEventSink *self) {
|
||||
|
||||
}
|
||||
|
||||
static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
typedef struct library_list {
|
||||
|
||||
uint8_t *name;
|
||||
uint64_t addr_start, addr_end;
|
||||
|
||||
} library_list_t;
|
||||
|
||||
#define MAX_LIB_COUNT 256
|
||||
static library_list_t liblist[MAX_LIB_COUNT];
|
||||
static u32 liblist_cnt;
|
||||
|
||||
void read_library_information() {
|
||||
|
||||
#if defined(__linux__)
|
||||
FILE *f;
|
||||
u8 buf[1024], *b, *m, *e, *n;
|
||||
|
||||
if ((f = fopen("/proc/self/maps", "r")) == NULL) {
|
||||
|
||||
fprintf(stderr, "Error: cannot open /proc/self/maps\n");
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
if (debug) fprintf(stderr, "Library list:\n");
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
|
||||
if (strstr(buf, " r-x")) {
|
||||
|
||||
if (liblist_cnt >= MAX_LIB_COUNT) {
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
"Warning: too many libraries to old, maximum count of %d reached\n",
|
||||
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;
|
||||
|
||||
if (rindex(n, '/') != NULL) {
|
||||
|
||||
n = rindex(n, '/');
|
||||
n++;
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
char *filename = rindex(name, '/');
|
||||
|
||||
if (filename)
|
||||
filename++;
|
||||
else
|
||||
filename = name;
|
||||
|
||||
#if defined(__linux__)
|
||||
u32 i;
|
||||
for (i = 0; i < liblist_cnt; i++)
|
||||
if (strcmp(liblist[i].name, filename) == 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;
|
||||
|
||||
}
|
||||
|
||||
static void gum_fake_event_sink_process(GumEventSink * sink,
|
||||
const GumEvent *ev) {
|
||||
|
||||
}
|
||||
|
||||
/* Because this CAN be called more than once, it will return the LAST range */
|
||||
static int enumerate_ranges(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
GumMemoryRange *code_range = (GumMemoryRange *)user_data;
|
||||
memcpy(code_range, details->range, sizeof(*code_range));
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
#ifndef __APPLE__
|
||||
(void)personality(ADDR_NO_RANDOMIZE); // disable ASLR
|
||||
#endif
|
||||
|
||||
// STEP 2: load the library you want to fuzz and lookup the functions,
|
||||
// inclusive of the cleanup functions.
|
||||
// If there is just one function, then there is nothing to change
|
||||
// or add here.
|
||||
|
||||
void *dl = dlopen(TARGET_LIBRARY, RTLD_LAZY);
|
||||
if (!dl) {
|
||||
|
||||
fprintf(stderr, "Could not load %s\n", TARGET_LIBRARY);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
if (!(o_function = dlsym(dl, TARGET_FUNCTION))) {
|
||||
|
||||
fprintf(stderr, "Could not find function %s\n", TARGET_FUNCTION);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
// END STEP 2
|
||||
|
||||
read_library_information();
|
||||
library_list_t *lib = find_library(TARGET_LIBRARY);
|
||||
|
||||
if (lib == NULL) {
|
||||
|
||||
fprintf(stderr, "Could not find target library\n");
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
gum_init_embedded();
|
||||
if (!gum_stalker_is_supported()) {
|
||||
|
||||
gum_deinit_embedded();
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
GumStalker *stalker = gum_stalker_new();
|
||||
|
||||
/*
|
||||
This does not work here as we load a shared library. pretty sure this
|
||||
would also be easily solvable with frida gum, but I already have all the
|
||||
code I need from afl-untracer
|
||||
|
||||
GumAddress base_address = gum_module_find_base_address(TARGET_LIBRARY);
|
||||
GumMemoryRange code_range;
|
||||
gum_module_enumerate_ranges(TARGET_LIBRARY, GUM_PAGE_RX, enumerate_ranges,
|
||||
&code_range);
|
||||
guint64 code_start = code_range.base_address - base_address;
|
||||
guint64 code_end = (code_range.base_address + code_range.size) - base_address;
|
||||
range_t instr_range = {base_address, code_start, code_end};
|
||||
*/
|
||||
range_t instr_range = {0, lib->addr_start, lib->addr_end};
|
||||
|
||||
GumStalkerTransformer *transformer =
|
||||
gum_stalker_transformer_make_from_callback(instr_basic_block,
|
||||
&instr_range, NULL);
|
||||
|
||||
GumEventSink *event_sink = gum_fake_event_sink_new();
|
||||
|
||||
// to ensure that the signatures are not optimized out
|
||||
memcpy(__afl_area_ptr, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT) + 1);
|
||||
memcpy(__afl_area_ptr + 32, (void *)AFL_DEFER_FORKSVR,
|
||||
sizeof(AFL_DEFER_FORKSVR) + 1);
|
||||
__afl_manual_init();
|
||||
|
||||
//
|
||||
// any expensive target library initialization that has to be done just once
|
||||
// - put that here
|
||||
//
|
||||
|
||||
gum_stalker_follow_me(stalker, transformer, event_sink);
|
||||
|
||||
while (__afl_persistent_loop(UINT32_MAX) != 0) {
|
||||
|
||||
previous_pc = 0; // Required!
|
||||
|
||||
#ifdef _DEBUG
|
||||
fprintf(stderr, "CLIENT crc: %016llx len: %u\n", hash64(__afl_fuzz_ptr, *__a
|
||||
fprintf(stderr, "RECV:");
|
||||
for (int i = 0; i < *__afl_fuzz_len; i++)
|
||||
fprintf(stderr, "%02x", __afl_fuzz_ptr[i]);
|
||||
fprintf(stderr,"\n");
|
||||
#endif
|
||||
|
||||
// STEP 3: ensure the minimum length is present and setup the target
|
||||
// function to fuzz.
|
||||
|
||||
if (*__afl_fuzz_len > 0) {
|
||||
|
||||
__afl_fuzz_ptr[*__afl_fuzz_len] = 0; // if you need to null terminate
|
||||
(*o_function)(__afl_fuzz_ptr, *__afl_fuzz_len);
|
||||
|
||||
}
|
||||
|
||||
// END STEP 3
|
||||
|
||||
}
|
||||
|
||||
gum_stalker_unfollow_me(stalker);
|
||||
|
||||
while (gum_stalker_garbage_collect(stalker))
|
||||
g_usleep(10000);
|
||||
|
||||
g_object_unref(stalker);
|
||||
g_object_unref(transformer);
|
||||
g_object_unref(event_sink);
|
||||
gum_deinit_embedded();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
53
examples/afl_frida/afl-frida.h
Normal file
53
examples/afl_frida/afl-frida.h
Normal file
@ -0,0 +1,53 @@
|
||||
extern int is_persistent;
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM,
|
||||
FAKE_EVENT_SINK, GObject)
|
||||
|
||||
struct _GumFakeEventSink {
|
||||
|
||||
GObject parent;
|
||||
GumEventType mask;
|
||||
|
||||
};
|
||||
|
||||
GumEventSink *gum_fake_event_sink_new(void);
|
||||
void gum_fake_event_sink_reset(GumFakeEventSink *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
typedef struct {
|
||||
|
||||
GumAddress base_address;
|
||||
guint64 code_start, code_end;
|
||||
|
||||
} range_t;
|
||||
|
||||
void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output,
|
||||
gpointer user_data);
|
||||
#pragma once
|
||||
|
||||
void afl_setup(void);
|
||||
void afl_start_forkserver(void);
|
||||
int __afl_persistent_loop(unsigned int max_cnt);
|
||||
|
||||
inline static inline void afl_maybe_log(guint64 current_pc) {
|
||||
|
||||
extern unsigned int afl_instr_rms;
|
||||
extern uint8_t * afl_area_ptr;
|
||||
|
||||
static __thread guint64 previous_pc;
|
||||
|
||||
current_pc = (current_pc >> 4) ^ (current_pc << 8);
|
||||
current_pc &= MAP_SIZE - 1;
|
||||
|
||||
if (current_pc >= afl_instr_rms) return;
|
||||
|
||||
afl_area_ptr[current_pc ^ previous_pc]++;
|
||||
previous_pc = current_pc >> 1;
|
||||
|
||||
}
|
||||
|
35
examples/afl_frida/libtestinstr.c
Normal file
35
examples/afl_frida/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");
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,9 @@
|
||||
#include <netinet/ip6.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/shm.h>
|
||||
#ifndef USEMMAP
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
@ -73,9 +73,9 @@ static u8 *in_file, /* Minimizer input test case */
|
||||
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 s32 in_len;
|
||||
static s32 buf2_len;
|
||||
static u32 map_size = MAP_SIZE;
|
||||
|
||||
static volatile u8 stop_soon; /* Ctrl-C pressed? */
|
||||
|
||||
@ -272,7 +272,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
|
||||
|
||||
setenv("QEMU_SET_ENV", buf, 1);
|
||||
|
||||
ck_free(buf);
|
||||
afl_free(buf);
|
||||
|
||||
} else {
|
||||
|
||||
@ -343,7 +343,7 @@ static void usage(u8 *argv0) {
|
||||
|
||||
}
|
||||
|
||||
int recv_testcase(int s, void **buf, size_t *max_len) {
|
||||
int recv_testcase(int s, void **buf) {
|
||||
|
||||
u32 size;
|
||||
s32 ret;
|
||||
@ -358,7 +358,8 @@ int recv_testcase(int s, void **buf, size_t *max_len) {
|
||||
|
||||
if ((size & 0xff000000) != 0xff000000) {
|
||||
|
||||
*buf = ck_maybe_grow(buf, max_len, size);
|
||||
*buf = afl_realloc((void **)&buf, size);
|
||||
if (unlikely(!buf)) { PFATAL("Alloc"); }
|
||||
received = 0;
|
||||
// fprintf(stderr, "unCOMPRESS (%u)\n", size);
|
||||
while (received < size &&
|
||||
@ -370,7 +371,8 @@ int recv_testcase(int s, void **buf, size_t *max_len) {
|
||||
#ifdef USE_DEFLATE
|
||||
u32 clen;
|
||||
size -= 0xff000000;
|
||||
*buf = ck_maybe_grow(buf, max_len, size);
|
||||
*buf = afl_realloc((void **)&buf, size);
|
||||
if (unlikely(!buf)) { PFATAL("Alloc"); }
|
||||
received = 0;
|
||||
while (received < 4 &&
|
||||
(ret = recv(s, &clen + received, 4 - received, 0)) > 0)
|
||||
@ -379,15 +381,16 @@ int recv_testcase(int s, void **buf, size_t *max_len) {
|
||||
// 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);
|
||||
buf2 = afl_realloc((void **)&buf2, clen);
|
||||
buf2_len = clen;
|
||||
if (unlikely(!buf2)) { PFATAL("Alloc"); }
|
||||
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)
|
||||
size, &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]);
|
||||
@ -413,7 +416,6 @@ int recv_testcase(int s, void **buf, size_t *max_len) {
|
||||
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;
|
||||
@ -568,7 +570,8 @@ int main(int argc, char **argv_orig, char **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);
|
||||
in_data = afl_realloc((void **)&in_data, 65536);
|
||||
if (unlikely(!in_data)) { PFATAL("Alloc"); }
|
||||
|
||||
atexit(at_exit_handler);
|
||||
setup_signal_handlers();
|
||||
@ -639,7 +642,9 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
#ifdef USE_DEFLATE
|
||||
compressor = libdeflate_alloc_compressor(1);
|
||||
decompressor = libdeflate_alloc_decompressor();
|
||||
buf2 = ck_maybe_grow((void **)&buf2, &buf2_len, map_size + 16);
|
||||
buf2 = afl_realloc((void **)&buf2, map_size + 16);
|
||||
buf2_len = map_size + 16;
|
||||
if (unlikely(!buf2)) { PFATAL("alloc"); }
|
||||
lenptr = (u32 *)(buf2 + 4);
|
||||
fprintf(stderr, "Compiled with compression support\n");
|
||||
#endif
|
||||
@ -664,7 +669,7 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
#endif
|
||||
|
||||
while ((in_len = recv_testcase(s, (void **)&in_data, &max_len)) > 0) {
|
||||
while ((in_len = recv_testcase(s, (void **)&in_data)) > 0) {
|
||||
|
||||
// fprintf(stderr, "received %u\n", in_len);
|
||||
(void)run_target(fsrv, use_argv, in_data, in_len, 1);
|
||||
@ -697,9 +702,9 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
afl_shm_deinit(&shm);
|
||||
afl_fsrv_deinit(fsrv);
|
||||
if (fsrv->target_path) { ck_free(fsrv->target_path); }
|
||||
if (in_data) { ck_free(in_data); }
|
||||
afl_free(in_data);
|
||||
#if USE_DEFLATE
|
||||
if (buf2) { ck_free(buf2); }
|
||||
afl_free(buf2);
|
||||
libdeflate_free_compressor(compressor);
|
||||
libdeflate_free_decompressor(decompressor);
|
||||
#endif
|
||||
|
@ -32,13 +32,14 @@ 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$$
|
||||
```
|
||||
The file is created at `~/Desktop/patches.txt`
|
||||
|
||||
### 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
|
||||
LD_LIBRARY_PATH=/path/to/target/library AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer
|
||||
```
|
||||
(or even remote via afl-network-proxy).
|
||||
|
||||
|
2
examples/afl_untracer/TODO
Normal file
2
examples/afl_untracer/TODO
Normal file
@ -0,0 +1,2 @@
|
||||
* add shmem fuzzing
|
||||
* add snapshot feature?
|
@ -56,6 +56,7 @@
|
||||
#include <sys/shm.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/personality.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/ucontext.h>
|
||||
@ -73,6 +74,9 @@
|
||||
|
||||
// STEP 1:
|
||||
|
||||
/* here you need to specify the parameter for the target function */
|
||||
static void *(*o_function)(u8 *buf, int len);
|
||||
|
||||
/* use stdin (1) or a file on the commandline (0) */
|
||||
static u32 use_stdin = 1;
|
||||
|
||||
@ -111,10 +115,10 @@ 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();
|
||||
static void fuzz(void);
|
||||
|
||||
/* read the library information */
|
||||
void read_library_information() {
|
||||
void read_library_information(void) {
|
||||
|
||||
#if defined(__linux__)
|
||||
FILE *f;
|
||||
@ -280,7 +284,7 @@ library_list_t *find_library(char *name) {
|
||||
// this seems to work for clang too. nice :) requires gcc 4.4+
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("O0")
|
||||
void breakpoint() {
|
||||
void breakpoint(void) {
|
||||
|
||||
if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n");
|
||||
|
||||
@ -395,7 +399,7 @@ static void __afl_map_shm(void) {
|
||||
}
|
||||
|
||||
/* Fork server logic. */
|
||||
static void __afl_start_forkserver(void) {
|
||||
inline static void __afl_start_forkserver(void) {
|
||||
|
||||
u8 tmp[4] = {0, 0, 0, 0};
|
||||
u32 status = 0;
|
||||
@ -411,7 +415,7 @@ static void __afl_start_forkserver(void) {
|
||||
|
||||
}
|
||||
|
||||
static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
|
||||
inline static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
|
||||
|
||||
s32 status;
|
||||
|
||||
@ -433,11 +437,13 @@ static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
|
||||
if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1;
|
||||
// fprintf(stderr, "write1 %d\n", do_exit);
|
||||
|
||||
__afl_area_ptr[0] = 1; // put something in the map
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static void __afl_end_testcase(int status) {
|
||||
inline 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);
|
||||
@ -457,7 +463,7 @@ static void __afl_end_testcase(int status) {
|
||||
((uintptr_t)addr & 0x3) * 0x10000000000))
|
||||
#endif
|
||||
|
||||
void setup_trap_instrumentation() {
|
||||
void setup_trap_instrumentation(void) {
|
||||
|
||||
library_list_t *lib_base = NULL;
|
||||
size_t lib_size = 0;
|
||||
@ -667,12 +673,11 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
|
||||
|
||||
}
|
||||
|
||||
/* 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[]) {
|
||||
|
||||
(void)personality(ADDR_NO_RANDOMIZE); // disable ASLR
|
||||
|
||||
pid = getpid();
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
|
||||
@ -706,6 +711,9 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
while (1) {
|
||||
|
||||
// instead of fork() we could also use the snapshot lkm or do our own mini
|
||||
// snapshot feature like in https://github.com/marcinguy/fuzzer
|
||||
// -> snapshot.c
|
||||
if ((pid = fork()) == -1) PFATAL("fork failed");
|
||||
|
||||
if (pid) {
|
||||
@ -738,7 +746,11 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
}
|
||||
|
||||
static void fuzz() {
|
||||
#ifndef _DEBUG
|
||||
inline
|
||||
#endif
|
||||
static void
|
||||
fuzz(void) {
|
||||
|
||||
// STEP 3: call the function to fuzz, also the functions you might
|
||||
// need to call to prepare the function and - important! -
|
||||
|
BIN
examples/afl_untracer/libtestinstr.so
Executable file
BIN
examples/afl_untracer/libtestinstr.so
Executable file
Binary file not shown.
@ -1,23 +1,34 @@
|
||||
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
|
||||
libtestinstr.so:0x1000
|
||||
0x10
|
||||
0x12
|
||||
0x20
|
||||
0x36
|
||||
0x30
|
||||
0x40
|
||||
0x50
|
||||
0x63
|
||||
0x6f
|
||||
0x78
|
||||
0x80
|
||||
0xa4
|
||||
0xb0
|
||||
0xb8
|
||||
0x100
|
||||
0xc0
|
||||
0xc9
|
||||
0xd7
|
||||
0xe3
|
||||
0xe8
|
||||
0xf8
|
||||
0x105
|
||||
0x11a
|
||||
0x135
|
||||
0x141
|
||||
0x143
|
||||
0x14e
|
||||
0x15a
|
||||
0x15c
|
||||
0x168
|
||||
0x16a
|
||||
0x16b
|
||||
0x170
|
||||
|
@ -7,38 +7,40 @@ ifneq "" "$(LLVM_BINDIR)"
|
||||
LLVM_BINDIR := $(LLVM_BINDIR)/
|
||||
endif
|
||||
|
||||
FLAGS=-O3 -funroll-loops -g
|
||||
CFLAGS := -O3 -funroll-loops -g
|
||||
|
||||
all: libAFLDriver.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so
|
||||
|
||||
aflpp_driver.o: aflpp_driver.cpp
|
||||
$(LLVM_BINDIR)clang++ $(FLAGS) -stdlib=libc++ -std=c++11 -c aflpp_driver.cpp
|
||||
aflpp_driver.o: aflpp_driver.c
|
||||
$(LLVM_BINDIR)clang -I. -I../../include $(CFLAGS) -c aflpp_driver.c
|
||||
|
||||
libAFLDriver.a: aflpp_driver.o
|
||||
ar ru libAFLDriver.a aflpp_driver.o
|
||||
cp -vf libAFLDriver.a ../../
|
||||
|
||||
debug:
|
||||
$(LLVM_BINDIR)clang++ -Wno-deprecated -I../../include $(FLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c
|
||||
$(LLVM_BINDIR)clang++ -I../../include -D_DEBUG=\"1\" -g -stdlib=libc++ -funroll-loops -std=c++11 -c aflpp_driver.cpp
|
||||
#$(LLVM_BINDIR)clang++ -S -emit-llvm -Wno-deprecated -I../../include $(FLAGS) -D_DEBUG=\"1\" -c -o afl-performance.ll ../../src/afl-performance.c
|
||||
#$(LLVM_BINDIR)clang++ -S -emit-llvm -I../../include -D_DEBUG=\"1\" -g -stdlib=libc++ -funroll-loops -std=c++11 -c aflpp_driver.cpp
|
||||
$(LLVM_BINDIR)clang -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c
|
||||
$(LLVM_BINDIR)clang -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c
|
||||
#$(LLVM_BINDIR)clang -S -emit-llvm -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.ll ../../src/afl-performance.c
|
||||
#$(LLVM_BINDIR)clang -S -emit-llvm -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c
|
||||
ar ru libAFLDriver.a afl-performance.o aflpp_driver.o
|
||||
|
||||
aflpp_qemu_driver.o: aflpp_qemu_driver.c
|
||||
$(LLVM_BINDIR)clang $(FLAGS) -O0 -funroll-loops -c aflpp_qemu_driver.c
|
||||
$(LLVM_BINDIR)clang $(CFLAGS) -O0 -funroll-loops -c aflpp_qemu_driver.c
|
||||
|
||||
libAFLQemuDriver.a: aflpp_qemu_driver.o
|
||||
ar ru libAFLQemuDriver.a aflpp_qemu_driver.o
|
||||
cp -vf libAFLQemuDriver.a ../../
|
||||
|
||||
aflpp_qemu_driver_hook.so: aflpp_qemu_driver_hook.o
|
||||
$(LLVM_BINDIR)clang -shared aflpp_qemu_driver_hook.o -o aflpp_qemu_driver_hook.so
|
||||
|
||||
aflpp_qemu_driver_hook.o: aflpp_qemu_driver_hook.c
|
||||
$(LLVM_BINDIR)clang -fPIC $(FLAGS) -funroll-loops -c aflpp_qemu_driver_hook.c
|
||||
$(LLVM_BINDIR)clang -fPIC $(CFLAGS) -funroll-loops -c aflpp_qemu_driver_hook.c
|
||||
|
||||
test: debug
|
||||
#clang++ -S -emit-llvm -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -stdlib=libc++ -funroll-loops -std=c++11 -o aflpp_driver_test.ll aflpp_driver_test.cpp
|
||||
afl-clang-fast++ -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -stdlib=libc++ -funroll-loops -std=c++11 -o aflpp_driver_test aflpp_driver_test.cpp libAFLDriver.a
|
||||
#clang -S -emit-llvm -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test.ll aflpp_driver_test.c
|
||||
afl-clang-fast -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test aflpp_driver_test.c libAFLDriver.a afl-performance.o
|
||||
|
||||
clean:
|
||||
rm -f *.o libAFLDriver*.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so *~ core aflpp_driver_test
|
||||
|
@ -14,12 +14,15 @@ cat << EOF > test_fuzzer.cc
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
|
||||
if (size > 0 && data[0] == 'H')
|
||||
if (size > 1 && data[1] == 'I')
|
||||
if (size > 2 && data[2] == '!')
|
||||
__builtin_trap();
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
EOF
|
||||
# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang.
|
||||
clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c
|
||||
@ -49,225 +52,247 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both.
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "config.h"
|
||||
#include "cmplog.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include "hash.h"
|
||||
#include "hash.h"
|
||||
#endif
|
||||
|
||||
#ifndef MAP_FIXED_NOREPLACE
|
||||
#define MAP_FIXED_NOREPLACE 0x100000
|
||||
#endif
|
||||
|
||||
#define MAX_DUMMY_SIZE 256000
|
||||
|
||||
// Platform detection. Copied from FuzzerInternal.h
|
||||
#ifdef __linux__
|
||||
#define LIBFUZZER_LINUX 1
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_LINUX 1
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#elif __APPLE__
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 1
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 1
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#elif __NetBSD__
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_NETBSD 1
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_NETBSD 1
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#elif __FreeBSD__
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 1
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 1
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#elif __OpenBSD__
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 1
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 1
|
||||
#else
|
||||
#error "Support for your platform has not been implemented"
|
||||
#error "Support for your platform has not been implemented"
|
||||
#endif
|
||||
|
||||
int __afl_sharedmem_fuzzing = 1;
|
||||
extern unsigned int *__afl_fuzz_len;
|
||||
int __afl_sharedmem_fuzzing = 1;
|
||||
extern unsigned int * __afl_fuzz_len;
|
||||
extern unsigned char *__afl_fuzz_ptr;
|
||||
|
||||
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
|
||||
extern "C" {
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
}
|
||||
|
||||
// Notify AFL about persistent mode.
|
||||
static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
|
||||
extern "C" int __afl_persistent_loop(unsigned int);
|
||||
static volatile char suppress_warning2 = AFL_PERSISTENT[0];
|
||||
int __afl_persistent_loop(unsigned int);
|
||||
|
||||
// Notify AFL about deferred forkserver.
|
||||
static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
|
||||
extern "C" void __afl_manual_init();
|
||||
static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
|
||||
|
||||
// Input buffer.
|
||||
static const size_t kMaxAflInputSize = 1 << 20;
|
||||
static uint8_t AflInputBuf[kMaxAflInputSize];
|
||||
void __afl_manual_init();
|
||||
|
||||
// Use this optionally defined function to output sanitizer messages even if
|
||||
// user asks to close stderr.
|
||||
__attribute__((weak)) extern "C" void __sanitizer_set_report_fd(void *);
|
||||
__attribute__((weak)) void __sanitizer_set_report_fd(void *);
|
||||
|
||||
// Keep track of where stderr content is being written to, so that
|
||||
// dup_and_close_stderr can use the correct one.
|
||||
static FILE *output_file = stderr;
|
||||
static FILE *output_file;
|
||||
|
||||
// Experimental feature to use afl_driver without AFL's deferred mode.
|
||||
// Needs to run before __afl_auto_init.
|
||||
__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) {
|
||||
|
||||
if (getenv("AFL_DRIVER_DONT_DEFER")) {
|
||||
|
||||
if (unsetenv("__AFL_DEFER_FORKSRV")) {
|
||||
|
||||
perror("Failed to unset __AFL_DEFER_FORKSRV");
|
||||
abort();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If the user asks us to duplicate stderr, then do it.
|
||||
static void maybe_duplicate_stderr() {
|
||||
|
||||
char *stderr_duplicate_filename =
|
||||
getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
|
||||
|
||||
if (!stderr_duplicate_filename)
|
||||
return;
|
||||
if (!stderr_duplicate_filename) return;
|
||||
|
||||
FILE *stderr_duplicate_stream =
|
||||
freopen(stderr_duplicate_filename, "a+", stderr);
|
||||
|
||||
if (!stderr_duplicate_stream) {
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
"Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
|
||||
abort();
|
||||
|
||||
}
|
||||
|
||||
output_file = stderr_duplicate_stream;
|
||||
|
||||
}
|
||||
|
||||
// Most of these I/O functions were inspired by/copied from libFuzzer's code.
|
||||
static void discard_output(int fd) {
|
||||
|
||||
FILE *temp = fopen("/dev/null", "w");
|
||||
if (!temp)
|
||||
abort();
|
||||
if (!temp) abort();
|
||||
dup2(fileno(temp), fd);
|
||||
fclose(temp);
|
||||
|
||||
}
|
||||
|
||||
static void close_stdout() { discard_output(STDOUT_FILENO); }
|
||||
static void close_stdout() {
|
||||
|
||||
discard_output(STDOUT_FILENO);
|
||||
|
||||
}
|
||||
|
||||
// Prevent the targeted code from writing to "stderr" but allow sanitizers and
|
||||
// this driver to do so.
|
||||
static void dup_and_close_stderr() {
|
||||
|
||||
int output_fileno = fileno(output_file);
|
||||
int output_fd = dup(output_fileno);
|
||||
if (output_fd <= 0)
|
||||
abort();
|
||||
if (output_fd <= 0) abort();
|
||||
FILE *new_output_file = fdopen(output_fd, "w");
|
||||
if (!new_output_file)
|
||||
abort();
|
||||
if (!__sanitizer_set_report_fd)
|
||||
return;
|
||||
__sanitizer_set_report_fd(reinterpret_cast<void *>(output_fd));
|
||||
if (!new_output_file) abort();
|
||||
if (!__sanitizer_set_report_fd) return;
|
||||
__sanitizer_set_report_fd((void *)output_fd);
|
||||
discard_output(output_fileno);
|
||||
}
|
||||
|
||||
static void Printf(const char *Fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, Fmt);
|
||||
vfprintf(output_file, Fmt, ap);
|
||||
va_end(ap);
|
||||
fflush(output_file);
|
||||
}
|
||||
|
||||
// Close stdout and/or stderr if user asks for it.
|
||||
static void maybe_close_fd_mask() {
|
||||
|
||||
char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK");
|
||||
if (!fd_mask_str)
|
||||
return;
|
||||
if (!fd_mask_str) return;
|
||||
int fd_mask = atoi(fd_mask_str);
|
||||
if (fd_mask & 2)
|
||||
dup_and_close_stderr();
|
||||
if (fd_mask & 1)
|
||||
close_stdout();
|
||||
if (fd_mask & 2) dup_and_close_stderr();
|
||||
if (fd_mask & 1) close_stdout();
|
||||
|
||||
}
|
||||
|
||||
// Define LLVMFuzzerMutate to avoid link failures for targets that use it
|
||||
// with libFuzzer's LLVMFuzzerCustomMutator.
|
||||
extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
assert(false && "LLVMFuzzerMutate should not be called from afl_driver");
|
||||
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
|
||||
// assert(false && "LLVMFuzzerMutate should not be called from afl_driver");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
// Execute any files provided as parameters.
|
||||
static int ExecuteFilesOnyByOne(int argc, char **argv) {
|
||||
|
||||
unsigned char *buf = malloc(MAX_FILE);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::ifstream in(argv[i], std::ios::binary);
|
||||
in.seekg(0, in.end);
|
||||
size_t length = in.tellg();
|
||||
in.seekg (0, in.beg);
|
||||
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
||||
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
||||
std::vector<char> bytes(length);
|
||||
in.read(bytes.data(), bytes.size());
|
||||
assert(in);
|
||||
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
||||
bytes.size());
|
||||
std::cout << "Execution successful" << std::endl;
|
||||
|
||||
int fd = open(argv[i], O_RDONLY);
|
||||
if (fd == -1) continue;
|
||||
ssize_t length = read(fd, buf, MAX_FILE);
|
||||
if (length > 0) {
|
||||
|
||||
printf("Reading %zu bytes from %s\n", length, argv[i]);
|
||||
LLVMFuzzerTestOneInput(buf, length);
|
||||
printf("Execution successful.\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Printf(
|
||||
|
||||
printf(
|
||||
"======================= INFO =========================\n"
|
||||
"This binary is built for AFL-fuzz.\n"
|
||||
"This binary is built for afl++.\n"
|
||||
"To run the target function on individual input(s) execute this:\n"
|
||||
" %s < INPUT_FILE\n"
|
||||
"or\n"
|
||||
" %s INPUT_FILE1 [INPUT_FILE2 ... ]\n"
|
||||
"To fuzz with afl-fuzz execute this:\n"
|
||||
" afl-fuzz [afl-flags] %s [-N]\n"
|
||||
"afl-fuzz will run N iterations before "
|
||||
"re-spawning the process (default: 1000)\n"
|
||||
" afl-fuzz [afl-flags] -- %s [-N]\n"
|
||||
"afl-fuzz will run N iterations before re-spawning the process (default: "
|
||||
"1000)\n"
|
||||
"======================================================\n",
|
||||
argv[0], argv[0], argv[0]);
|
||||
argv[0], argv[0]);
|
||||
|
||||
output_file = stderr;
|
||||
maybe_duplicate_stderr();
|
||||
maybe_close_fd_mask();
|
||||
if (LLVMFuzzerInitialize)
|
||||
if (LLVMFuzzerInitialize) {
|
||||
|
||||
fprintf(stderr, "Running LLVMFuzzerInitialize ...\n");
|
||||
LLVMFuzzerInitialize(&argc, &argv);
|
||||
fprintf(stderr, "continue...\n");
|
||||
|
||||
}
|
||||
|
||||
// Do any other expensive one-time initialization here.
|
||||
|
||||
uint8_t dummy_input[1] = {0};
|
||||
int N = 100000;
|
||||
uint8_t dummy_input[64] = {0};
|
||||
memcpy(dummy_input, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT));
|
||||
memcpy(dummy_input + 32, (void *)AFL_DEFER_FORKSVR,
|
||||
sizeof(AFL_DEFER_FORKSVR));
|
||||
int N = INT_MAX;
|
||||
if (argc == 2 && argv[1][0] == '-')
|
||||
N = atoi(argv[1] + 1);
|
||||
else if(argc == 2 && (N = atoi(argv[1])) > 0)
|
||||
Printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N);
|
||||
N = atoi(argv[1] + 1);
|
||||
else if (argc == 2 && (N = atoi(argv[1])) > 0)
|
||||
printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N);
|
||||
else if (argc > 1) {
|
||||
// if (!getenv("AFL_DRIVER_DONT_DEFER")) {
|
||||
__afl_sharedmem_fuzzing = 0;
|
||||
__afl_manual_init();
|
||||
// }
|
||||
|
||||
__afl_sharedmem_fuzzing = 0;
|
||||
__afl_manual_init();
|
||||
return ExecuteFilesOnyByOne(argc, argv);
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
||||
assert(N > 0);
|
||||
|
||||
// if (!getenv("AFL_DRIVER_DONT_DEFER"))
|
||||
// if (!getenv("AFL_DRIVER_DONT_DEFER"))
|
||||
__afl_manual_init();
|
||||
|
||||
// Call LLVMFuzzerTestOneInput here so that coverage caused by initialization
|
||||
@ -276,17 +301,26 @@ int main(int argc, char **argv) {
|
||||
|
||||
int num_runs = 0;
|
||||
while (__afl_persistent_loop(N)) {
|
||||
|
||||
#ifdef _DEBUG
|
||||
fprintf(stderr, "CLIENT crc: %016llx len: %u\n", hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705), *__afl_fuzz_len);
|
||||
fprintf(stderr, "CLIENT crc: %016llx len: %u\n",
|
||||
hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705),
|
||||
*__afl_fuzz_len);
|
||||
fprintf(stderr, "RECV:");
|
||||
for (int i = 0; i < *__afl_fuzz_len; i++)
|
||||
fprintf(stderr, "%02x", __afl_fuzz_ptr[i]);
|
||||
fprintf(stderr,"\n");
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
if (*__afl_fuzz_len) {
|
||||
|
||||
num_runs++;
|
||||
LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Printf("%s: successfully executed %d input(s)\n", argv[0], num_runs);
|
||||
|
||||
printf("%s: successfully executed %d input(s)\n", argv[0], num_runs);
|
||||
|
||||
}
|
||||
|
32
examples/aflpp_driver/aflpp_driver_test.c
Normal file
32
examples/aflpp_driver/aflpp_driver_test.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) {
|
||||
|
||||
if (Size < 5) return;
|
||||
|
||||
if (Data[0] == 'F')
|
||||
if (Data[1] == 'A')
|
||||
if (Data[2] == '$')
|
||||
if (Data[3] == '$')
|
||||
if (Data[4] == '$') abort();
|
||||
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
|
||||
if (Size)
|
||||
fprintf(stderr, "FUNC crc: %016llx len: %lu\n",
|
||||
hash64((u8 *)Data, (unsigned int)Size,
|
||||
(unsigned long long int)0xa5b35705),
|
||||
Size);
|
||||
|
||||
crashme(Data, Size);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "hash.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
|
||||
fprintf(stderr, "FUNC crc: %016llx len: %lu\n", hash64((u8*)Data, (unsigned int) Size, (unsigned long long int) 0xa5b35705), Size);
|
||||
|
||||
if (Size < 5)
|
||||
return 0;
|
||||
|
||||
if (Data[0] == 'F')
|
||||
if (Data[1] == 'A')
|
||||
if (Data[2] == '$')
|
||||
if (Data[3] == '$')
|
||||
if (Data[4] == '$')
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
@ -324,8 +324,8 @@ static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
|
||||
}
|
||||
|
||||
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
|
||||
static inline void swap_bufs(void **buf1, size_t *size1, void **buf2,
|
||||
size_t *size2) {
|
||||
static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2,
|
||||
size_t *size2) {
|
||||
|
||||
void * scratch_buf = *buf1;
|
||||
size_t scratch_size = *size1;
|
||||
|
@ -83,7 +83,7 @@ typedef struct post_state {
|
||||
|
||||
} post_state_t;
|
||||
|
||||
void *afl afl_custom_init(void *afl) {
|
||||
void *afl_custom_init(void *afl) {
|
||||
|
||||
post_state_t *state = malloc(sizeof(post_state_t));
|
||||
if (!state) {
|
||||
|
64
examples/defork/Makefile
Normal file
64
examples/defork/Makefile
Normal file
@ -0,0 +1,64 @@
|
||||
#
|
||||
# american fuzzy lop++ - defork
|
||||
# ----------------------------------
|
||||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
BIN_PATH = $(PREFIX)/bin
|
||||
HELPER_PATH = $(PREFIX)/lib/afl
|
||||
|
||||
CFLAGS = -fPIC -Wall -Wextra
|
||||
LDFLAGS = -shared
|
||||
|
||||
UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?)
|
||||
UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?
|
||||
|
||||
_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=)
|
||||
LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl)
|
||||
LDFLAGS += $(LDFLAGS_ADD)
|
||||
|
||||
# on gcc for arm there is no -m32, but -mbe32
|
||||
M32FLAG = -m32
|
||||
M64FLAG = -m64
|
||||
|
||||
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)
|
||||
___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32)
|
||||
M32FLAG=$(___M32FLAG)
|
||||
#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
|
||||
# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)"))
|
||||
# M32FLAG = -mbe32
|
||||
# endif
|
||||
#endif
|
||||
|
||||
all: defork32.so defork64.so
|
||||
|
||||
defork32.so: defork.c
|
||||
-@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork32 build failure (that's fine)"
|
||||
|
||||
defork64.so: defork.c
|
||||
-@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork64 build failure (that's fine)"
|
||||
|
||||
install: defork32.so defork64.so
|
||||
install -d -m 755 $(DESTDIR)$(HELPER_PATH)/
|
||||
if [ -f defork32.so ]; then set -e; install -m 755 defork32.so $(DESTDIR)$(HELPER_PATH)/; fi
|
||||
if [ -f defork64.so ]; then set -e; install -m 755 defork64.so $(DESTDIR)$(HELPER_PATH)/; fi
|
||||
|
||||
target:
|
||||
../../afl-clang forking_target.c -o forking_target -Wall -Wextra -Werror
|
||||
|
||||
clean:
|
||||
rm -f defork32.so defork64.so forking_target
|
11
examples/defork/README.md
Normal file
11
examples/defork/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# defork
|
||||
|
||||
when the target forks, this breaks all normal fuzzing runs.
|
||||
Sometimes, though, it is enough to just run the child process.
|
||||
If this is the case, then this LD_PRELOAD library will always return 0 on fork,
|
||||
the target will belive it is running as the child, post-fork.
|
||||
|
||||
This is defork.c from the amazing preeny project
|
||||
https://github.com/zardus/preeny
|
||||
|
||||
It is altered for afl++ to work with its fork-server: the initial fork will go through, the second fork will be blocked.
|
50
examples/defork/defork.c
Normal file
50
examples/defork/defork.c
Normal file
@ -0,0 +1,50 @@
|
||||
#define __GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "../../include/config.h"
|
||||
|
||||
/* we want to fork once (for the afl++ forkserver),
|
||||
then immediately return as child on subsequent forks. */
|
||||
static bool forked = 0;
|
||||
|
||||
pid_t (*original_fork)(void);
|
||||
|
||||
/* In case we are not running in afl, we use a dummy original_fork */
|
||||
static pid_t nop(void) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void preeny_fork_orig() {
|
||||
|
||||
if (getenv(SHM_ENV_VAR)) {
|
||||
|
||||
printf("defork: running in AFL++. Allowing forkserver.\n");
|
||||
original_fork = dlsym(RTLD_NEXT, "socket");
|
||||
|
||||
} else {
|
||||
|
||||
printf("defork: no AFL++ detected. Disabling fork from the start.\n");
|
||||
original_fork = &nop;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pid_t fork(void) {
|
||||
|
||||
/* If we forked before, or if we're in the child (pid==0),
|
||||
we don't want to fork anymore, else, we are still in the forkserver.
|
||||
The forkserver parent needs to fork infinite times, each child should never
|
||||
fork again. This can be written without branches and I hate myself for it.
|
||||
*/
|
||||
pid_t ret = !forked && original_fork();
|
||||
forked = !ret;
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
48
examples/defork/forking_target.c
Normal file
48
examples/defork/forking_target.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* This is an example target for defork.c - fuzz using
|
||||
```
|
||||
mkdir in; echo a > ./in/a
|
||||
AFL_PRELOAD=./defork64.so ../../afl-fuzz -i in -o out -- ./forking_target @@
|
||||
```
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (argc < 2) {
|
||||
|
||||
printf("Example tool to test defork.\nUsage ./forking_target <input>\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
|
||||
printf("We're in the child.\n");
|
||||
FILE *f = fopen(argv[1], "r");
|
||||
char buf[4096];
|
||||
fread(buf, 1, 4096, f);
|
||||
uint32_t offset = buf[100] + (buf[101] << 8);
|
||||
char test_val = buf[offset];
|
||||
return test_val < 100;
|
||||
|
||||
} else if (pid < 0) {
|
||||
|
||||
perror("fork");
|
||||
return -1;
|
||||
|
||||
} else {
|
||||
|
||||
printf("We are in the parent - defork didn't work! :( (pid=%d)\n",
|
||||
(int)pid);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
@ -39,8 +39,11 @@ FUZZ_USER=bob
|
||||
# Directory to synchronize
|
||||
SYNC_DIR='/home/bob/sync_dir'
|
||||
|
||||
# Interval (seconds) between sync attempts
|
||||
SYNC_INTERVAL=$((30 * 60))
|
||||
# We only capture -M main nodes, set the name to your chosen naming scheme
|
||||
MAIN_NAME='main'
|
||||
|
||||
# Interval (seconds) between sync attempts (eg one hour)
|
||||
SYNC_INTERVAL=$((60 * 60))
|
||||
|
||||
if [ "$AFL_ALLOW_TMP" = "" ]; then
|
||||
|
||||
@ -63,7 +66,7 @@ while :; do
|
||||
echo "[*] Retrieving data from ${host}.${FUZZ_DOMAIN}..."
|
||||
|
||||
ssh -o 'passwordauthentication no' ${FUZZ_USER}@${host}.$FUZZ_DOMAIN \
|
||||
"cd '$SYNC_DIR' && tar -czf - ${host}_*/[qf]*" >".sync_tmp/${host}.tgz"
|
||||
"cd '$SYNC_DIR' && tar -czf - ${host}_${MAIN_NAME}*/" > ".sync_tmp/${host}.tgz"
|
||||
|
||||
done
|
||||
|
||||
@ -80,7 +83,7 @@ while :; do
|
||||
echo " Sending fuzzer data from ${src_host}.${FUZZ_DOMAIN}..."
|
||||
|
||||
ssh -o 'passwordauthentication no' ${FUZZ_USER}@$dst_host \
|
||||
"cd '$SYNC_DIR' && tar -xkzf -" <".sync_tmp/${src_host}.tgz"
|
||||
"cd '$SYNC_DIR' && tar -xkzf - " < ".sync_tmp/${src_host}.tgz"
|
||||
|
||||
done
|
||||
|
||||
|
@ -28,6 +28,20 @@
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
/* this lets the source compile without afl-clang-fast/lto */
|
||||
#ifndef __AFL_FUZZ_TESTCASE_LEN
|
||||
|
||||
ssize_t fuzz_len;
|
||||
unsigned char fuzz_buf[1024000];
|
||||
|
||||
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
|
||||
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
|
||||
#define __AFL_FUZZ_INIT() void sync(void);
|
||||
#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ?
|
||||
#define __AFL_INIT() sync()
|
||||
|
||||
#endif
|
||||
|
||||
__AFL_FUZZ_INIT();
|
||||
|
||||
/* Main entry point. */
|
||||
|
@ -24,7 +24,7 @@ PREFIX ?= /usr/local
|
||||
HELPER_PATH ?= $(PREFIX)/lib/afl
|
||||
BIN_PATH ?= $(PREFIX)/bin
|
||||
DOC_PATH ?= $(PREFIX)/share/doc/afl
|
||||
MAN_PATH ?= $(PREFIX)/man/man8
|
||||
MAN_PATH ?= $(PREFIX)/share/man/man8
|
||||
|
||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
@ -70,9 +70,16 @@ ifeq "$(TEST_MMAP)" "1"
|
||||
endif
|
||||
|
||||
ifneq "$(shell uname -s)" "Haiku"
|
||||
LDFLAGS += -lrt
|
||||
LDFLAGS += -lrt
|
||||
else
|
||||
CFLAGS_SAFE += -DUSEMMAP=1
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname -s)" "SunOS"
|
||||
PLUGIN_FLAGS += -I/usr/include/gmp
|
||||
endif
|
||||
|
||||
|
||||
PROGS = ../afl-gcc-fast ../afl-gcc-pass.so ../afl-gcc-rt.o
|
||||
|
||||
|
||||
@ -104,21 +111,21 @@ test_deps:
|
||||
@echo "[+] All set and ready to build."
|
||||
|
||||
afl-common.o: ../src/afl-common.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS)
|
||||
|
||||
../afl-gcc-fast: afl-gcc-fast.c afl-common.o | test_deps
|
||||
$(CC) -DAFL_GCC_CC=\"$(CC)\" -DAFL_GCC_CXX=\"$(CXX)\" $(CFLAGS) $< afl-common.o -o $@ $(LDFLAGS)
|
||||
$(CC) -DAFL_GCC_CC=\"$(CC)\" -DAFL_GCC_CXX=\"$(CXX)\" $(CFLAGS) $(CPPFLAGS) $< afl-common.o -o $@ $(LDFLAGS)
|
||||
ln -sf afl-gcc-fast ../afl-g++-fast
|
||||
|
||||
../afl-gcc-pass.so: afl-gcc-pass.so.cc | test_deps
|
||||
$(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@
|
||||
|
||||
../afl-gcc-rt.o: afl-gcc-rt.o.c | test_deps
|
||||
$(CC) $(CFLAGS_SAFE) -fPIC -c $< -o $@
|
||||
$(CC) $(CFLAGS_SAFE) $(CPPFLAGS) -fPIC -c $< -o $@
|
||||
|
||||
test_build: $(PROGS)
|
||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
||||
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) $(CPPFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
||||
# unset AFL_USE_ASAN AFL_USE_MSAN; AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-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
|
||||
@ -156,7 +163,7 @@ install: all
|
||||
install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH)
|
||||
install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md
|
||||
install -m 644 -T README.instrument_file.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md
|
||||
install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md
|
||||
|
||||
clean:
|
||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2
|
||||
|
@ -24,7 +24,7 @@ PREFIX ?= /usr/local
|
||||
HELPER_PATH ?= $(PREFIX)/lib/afl
|
||||
BIN_PATH ?= $(PREFIX)/bin
|
||||
DOC_PATH ?= $(PREFIX)/share/doc/afl
|
||||
MAN_PATH ?= $(PREFIX)/man/man8
|
||||
MAN_PATH ?= $(PREFIX)/share/man/man8
|
||||
|
||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
VERSION:sh= grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2
|
||||
@ -152,7 +152,7 @@ install: all
|
||||
install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH)
|
||||
install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md
|
||||
install -m 644 -T README.instrument_file.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md
|
||||
install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md
|
||||
|
||||
clean:
|
||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2
|
||||
|
@ -379,7 +379,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
u32 map_size = atoi(ptr);
|
||||
if (map_size != MAP_SIZE)
|
||||
FATAL("AFL_MAP_SIZE is not supported by afl-gcc-fast");
|
||||
WARNF("AFL_MAP_SIZE is not supported by afl-gcc-fast");
|
||||
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,11 @@
|
||||
#include <sys/sysctl.h>
|
||||
#endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
#include <kernel/OS.h>
|
||||
#include <kernel/scheduler.h>
|
||||
#endif
|
||||
|
||||
/* For systems that have sched_setaffinity; right now just Linux, but one
|
||||
can hope... */
|
||||
|
||||
@ -121,6 +126,9 @@
|
||||
|
||||
#define STAGE_BUF_SIZE (64) /* usable size for stage name buf in afl_state */
|
||||
|
||||
// Little helper to access the ptr to afl->##name_buf - for use in afl_realloc.
|
||||
#define AFL_BUF_PARAM(name) ((void **)&afl->name##_buf)
|
||||
|
||||
extern s8 interesting_8[INTERESTING_8_LEN];
|
||||
extern s16 interesting_16[INTERESTING_8_LEN + INTERESTING_16_LEN];
|
||||
extern s32
|
||||
@ -139,7 +147,8 @@ struct queue_entry {
|
||||
var_behavior, /* Variable behavior? */
|
||||
favored, /* Currently favored? */
|
||||
fs_redundant, /* Marked as redundant in the fs? */
|
||||
fully_colorized; /* Do not run redqueen stage again */
|
||||
fully_colorized, /* Do not run redqueen stage again */
|
||||
is_ascii; /* Is the input just ascii text? */
|
||||
|
||||
u32 bitmap_size, /* Number of bits set in bitmap */
|
||||
fuzz_level; /* Number of fuzzing iterations */
|
||||
@ -166,6 +175,14 @@ struct extra_data {
|
||||
|
||||
};
|
||||
|
||||
struct auto_extra_data {
|
||||
|
||||
u8 data[MAX_AUTO_EXTRA]; /* Dictionary token data */
|
||||
u32 len; /* Dictionary token length */
|
||||
u32 hit_cnt; /* Use count in the corpus */
|
||||
|
||||
};
|
||||
|
||||
/* Fuzzing stages */
|
||||
|
||||
enum {
|
||||
@ -271,16 +288,19 @@ enum {
|
||||
enum {
|
||||
|
||||
/* 00 */ PY_FUNC_INIT,
|
||||
/* 01 */ PY_FUNC_FUZZ,
|
||||
/* 02 */ PY_FUNC_POST_PROCESS,
|
||||
/* 03 */ PY_FUNC_INIT_TRIM,
|
||||
/* 04 */ PY_FUNC_POST_TRIM,
|
||||
/* 05 */ PY_FUNC_TRIM,
|
||||
/* 06 */ PY_FUNC_HAVOC_MUTATION,
|
||||
/* 07 */ PY_FUNC_HAVOC_MUTATION_PROBABILITY,
|
||||
/* 08 */ PY_FUNC_QUEUE_GET,
|
||||
/* 09 */ PY_FUNC_QUEUE_NEW_ENTRY,
|
||||
/* 10 */ PY_FUNC_DEINIT,
|
||||
/* 01 */ PY_FUNC_DEINIT,
|
||||
/* FROM HERE ON BELOW ALL ARE OPTIONAL */
|
||||
/* 02 */ PY_OPTIONAL = 2,
|
||||
/* 02 */ PY_FUNC_FUZZ = 2,
|
||||
/* 03 */ PY_FUNC_FUZZ_COUNT,
|
||||
/* 04 */ PY_FUNC_POST_PROCESS,
|
||||
/* 05 */ PY_FUNC_INIT_TRIM,
|
||||
/* 06 */ PY_FUNC_POST_TRIM,
|
||||
/* 07 */ PY_FUNC_TRIM,
|
||||
/* 08 */ PY_FUNC_HAVOC_MUTATION,
|
||||
/* 09 */ PY_FUNC_HAVOC_MUTATION_PROBABILITY,
|
||||
/* 10 */ PY_FUNC_QUEUE_GET,
|
||||
/* 11 */ PY_FUNC_QUEUE_NEW_ENTRY,
|
||||
PY_FUNC_COUNT
|
||||
|
||||
};
|
||||
@ -333,10 +353,11 @@ typedef struct afl_env_vars {
|
||||
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_cal_fast;
|
||||
afl_cal_fast, afl_cycle_schedules, afl_expand_havoc;
|
||||
|
||||
u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path,
|
||||
*afl_hang_tmout, *afl_skip_crashes, *afl_preload;
|
||||
*afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload,
|
||||
*afl_max_det_extras;
|
||||
|
||||
} afl_env_vars_t;
|
||||
|
||||
@ -347,6 +368,13 @@ struct afl_pass_stat {
|
||||
|
||||
};
|
||||
|
||||
struct foreign_sync {
|
||||
|
||||
u8 * dir;
|
||||
time_t ctime;
|
||||
|
||||
};
|
||||
|
||||
typedef struct afl_state {
|
||||
|
||||
/* Position of this state in the global states list */
|
||||
@ -454,7 +482,9 @@ typedef struct afl_state {
|
||||
fixed_seed, /* do not reseed */
|
||||
fast_cal, /* Try to calibrate faster? */
|
||||
disable_trim, /* Never trim in fuzz_one */
|
||||
shmem_testcase_mode; /* If sharedmem testcases are used */
|
||||
shmem_testcase_mode, /* If sharedmem testcases are used */
|
||||
expand_havoc, /* perform expensive havoc after no find */
|
||||
cycle_schedules; /* cycle power schedules ? */
|
||||
|
||||
u8 *virgin_bits, /* Regions yet untouched by fuzzing */
|
||||
*virgin_tmout, /* Bits we haven't seen in tmouts */
|
||||
@ -480,7 +510,8 @@ typedef struct afl_state {
|
||||
useless_at_start, /* Number of useless starting paths */
|
||||
var_byte_count, /* Bitmap bytes with var behavior */
|
||||
current_entry, /* Current queue entry ID */
|
||||
havoc_div; /* Cycle count divisor for havoc */
|
||||
havoc_div, /* Cycle count divisor for havoc */
|
||||
max_det_extras; /* deterministic extra count (dicts)*/
|
||||
|
||||
u64 total_crashes, /* Total number of crashes */
|
||||
unique_crashes, /* Crashes with unique signatures */
|
||||
@ -535,7 +566,8 @@ typedef struct afl_state {
|
||||
u64 total_bitmap_size, /* Total bit count for all bitmaps */
|
||||
total_bitmap_entries; /* Number of bitmaps counted */
|
||||
|
||||
s32 cpu_core_count; /* CPU core count */
|
||||
s32 cpu_core_count, /* CPU core count */
|
||||
cpu_to_bind; /* bind to specific CPU */
|
||||
|
||||
#ifdef HAVE_AFFINITY
|
||||
s32 cpu_aff; /* Selected CPU core */
|
||||
@ -546,13 +578,17 @@ typedef struct afl_state {
|
||||
*queue_top, /* Top of the list */
|
||||
*q_prev100; /* Previous 100 marker */
|
||||
|
||||
// growing buf
|
||||
struct queue_entry **queue_buf;
|
||||
|
||||
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 */
|
||||
|
||||
struct extra_data *a_extras; /* Automatically selected extras */
|
||||
u32 a_extras_cnt; /* Total number of tokens available */
|
||||
struct auto_extra_data
|
||||
a_extras[MAX_AUTO_EXTRAS]; /* Automatically selected extras */
|
||||
u32 a_extras_cnt; /* Total number of tokens available */
|
||||
|
||||
/* afl_postprocess API - Now supported via custom mutators */
|
||||
|
||||
@ -574,19 +610,26 @@ typedef struct afl_state {
|
||||
u8 describe_op_buf_256[256]; /* describe_op will use this to return a string
|
||||
up to 256 */
|
||||
|
||||
unsigned long long int last_avg_exec_update;
|
||||
u32 last_avg_execs;
|
||||
float last_avg_execs_saved;
|
||||
|
||||
/* foreign sync */
|
||||
#define FOREIGN_SYNCS_MAX 32
|
||||
u8 foreign_sync_cnt;
|
||||
struct foreign_sync foreign_syncs[FOREIGN_SYNCS_MAX];
|
||||
|
||||
#ifdef _AFL_DOCUMENT_MUTATIONS
|
||||
u8 do_document;
|
||||
u32 document_counter;
|
||||
#endif
|
||||
|
||||
void *maybe_add_auto;
|
||||
|
||||
/* statistics file */
|
||||
double last_bitmap_cvg, last_stability, last_eps;
|
||||
|
||||
/* plot file saves from last run */
|
||||
u32 plot_prev_qp, plot_prev_pf, plot_prev_pnf, plot_prev_ce, plot_prev_md;
|
||||
u64 plot_prev_qc, plot_prev_uc, plot_prev_uh;
|
||||
u64 plot_prev_qc, plot_prev_uc, plot_prev_uh, plot_prev_ed;
|
||||
|
||||
u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs;
|
||||
double stats_avg_exec;
|
||||
@ -597,24 +640,18 @@ typedef struct afl_state {
|
||||
|
||||
/*needed for afl_fuzz_one */
|
||||
// TODO: see which we can reuse
|
||||
u8 * out_buf;
|
||||
size_t out_size;
|
||||
u8 *out_buf;
|
||||
|
||||
u8 * out_scratch_buf;
|
||||
size_t out_scratch_size;
|
||||
u8 *out_scratch_buf;
|
||||
|
||||
u8 * eff_buf;
|
||||
size_t eff_size;
|
||||
u8 *eff_buf;
|
||||
|
||||
u8 * in_buf;
|
||||
size_t in_size;
|
||||
u8 *in_buf;
|
||||
|
||||
u8 * in_scratch_buf;
|
||||
size_t in_scratch_size;
|
||||
u8 *in_scratch_buf;
|
||||
|
||||
u8 * ex_buf;
|
||||
size_t ex_size;
|
||||
u32 custom_mutators_count;
|
||||
u8 *ex_buf;
|
||||
u32 custom_mutators_count;
|
||||
|
||||
list_t custom_mutator_list;
|
||||
|
||||
@ -627,9 +664,9 @@ typedef struct afl_state {
|
||||
struct custom_mutator {
|
||||
|
||||
const char *name;
|
||||
char * name_short;
|
||||
void * dh;
|
||||
u8 * post_process_buf;
|
||||
size_t post_process_size;
|
||||
u8 stacked_custom_prob, stacked_custom;
|
||||
|
||||
void *data; /* custom mutator data ptr */
|
||||
@ -645,6 +682,24 @@ struct custom_mutator {
|
||||
*/
|
||||
void *(*afl_custom_init)(afl_state_t *afl, unsigned int seed);
|
||||
|
||||
/**
|
||||
* This method is called just before fuzzing a queue entry with the custom
|
||||
* mutator, and receives the initial buffer. It should return the number of
|
||||
* fuzzes to perform.
|
||||
*
|
||||
* A value of 0 means no fuzzing of this queue entry.
|
||||
*
|
||||
* The function is now allowed to change the data.
|
||||
*
|
||||
* (Optional)
|
||||
*
|
||||
* @param data pointer returned in afl_custom_init for this fuzz case
|
||||
* @param buf Buffer containing the test case
|
||||
* @param buf_size Size of the test case
|
||||
* @return The amount of fuzzes to perform on this queue entry, 0 = skip
|
||||
*/
|
||||
u32 (*afl_custom_fuzz_count)(void *data, const u8 *buf, size_t buf_size);
|
||||
|
||||
/**
|
||||
* Perform custom mutations on a given input
|
||||
*
|
||||
@ -833,6 +888,7 @@ u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf,
|
||||
struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *);
|
||||
void finalize_py_module(void *);
|
||||
|
||||
u32 fuzz_count_py(void *, const u8 *, size_t);
|
||||
size_t post_process_py(void *, u8 *, size_t, u8 **);
|
||||
s32 init_trim_py(void *, u8 *, size_t);
|
||||
s32 post_trim_py(void *, u8);
|
||||
@ -881,13 +937,15 @@ 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(void *, u8 *, u32);
|
||||
void add_extra(afl_state_t *afl, u8 *mem, u32 len);
|
||||
void maybe_add_auto(afl_state_t *, u8 *, u32);
|
||||
void save_auto(afl_state_t *);
|
||||
void load_auto(afl_state_t *);
|
||||
void destroy_extras(afl_state_t *);
|
||||
|
||||
/* Stats */
|
||||
|
||||
void write_setup_file(afl_state_t *, u32, char **);
|
||||
void write_stats_file(afl_state_t *, double, double, double);
|
||||
void maybe_update_plot_file(afl_state_t *, double, double);
|
||||
void show_stats(afl_state_t *);
|
||||
@ -937,6 +995,7 @@ void fix_up_banner(afl_state_t *, u8 *);
|
||||
void check_if_tty(afl_state_t *);
|
||||
void setup_signal_handlers(void);
|
||||
void save_cmdline(afl_state_t *, u32, char **);
|
||||
void read_foreign_testcases(afl_state_t *, int);
|
||||
|
||||
/* CmpLog */
|
||||
|
||||
@ -956,6 +1015,8 @@ uint64_t rand_next(afl_state_t *afl);
|
||||
|
||||
static inline u32 rand_below(afl_state_t *afl, u32 limit) {
|
||||
|
||||
if (limit <= 1) return 0;
|
||||
|
||||
/* The boundary not being necessarily a power of 2,
|
||||
we need to ensure the result uniformity. */
|
||||
if (unlikely(!afl->rand_cnt--) && likely(!afl->fixed_seed)) {
|
||||
@ -967,7 +1028,44 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) {
|
||||
|
||||
}
|
||||
|
||||
return rand_next(afl) % limit;
|
||||
/* Modulo is biased - we don't want our fuzzing to be biased so let's do it
|
||||
right. See:
|
||||
https://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator
|
||||
*/
|
||||
u64 unbiased_rnd;
|
||||
do {
|
||||
|
||||
unbiased_rnd = rand_next(afl);
|
||||
|
||||
} while (unlikely(unbiased_rnd >= (UINT64_MAX - (UINT64_MAX % limit))));
|
||||
|
||||
return unbiased_rnd % limit;
|
||||
|
||||
}
|
||||
|
||||
/* we prefer lower range values here */
|
||||
/* this is only called with normal havoc, not MOpt, to have an equalizer for
|
||||
expand havoc mode */
|
||||
static inline u32 rand_below_datalen(afl_state_t *afl, u32 limit) {
|
||||
|
||||
if (limit <= 1) return 0;
|
||||
|
||||
switch (rand_below(afl, 3)) {
|
||||
|
||||
case 2:
|
||||
return (rand_below(afl, limit) % (1 + rand_below(afl, limit - 1))) %
|
||||
(1 + rand_below(afl, limit - 1));
|
||||
break;
|
||||
case 1:
|
||||
return rand_below(afl, limit) % (1 + rand_below(afl, limit - 1));
|
||||
break;
|
||||
case 0:
|
||||
return rand_below(afl, limit);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return 1; // cannot be reached
|
||||
|
||||
}
|
||||
|
||||
|
@ -30,12 +30,13 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* Initial size used for ck_maybe_grow */
|
||||
/* Initial size used for afl_realloc */
|
||||
#define INITIAL_GROWTH_SIZE (64)
|
||||
|
||||
// Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators
|
||||
@ -76,10 +77,6 @@
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Allocator increments for ck_realloc_block(). */
|
||||
|
||||
#define ALLOC_BLK_INC 256
|
||||
|
||||
/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized
|
||||
requests. */
|
||||
|
||||
@ -149,15 +146,6 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) {
|
||||
|
||||
}
|
||||
|
||||
/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up
|
||||
repeated small reallocs without complicating the user code). */
|
||||
|
||||
static inline void *DFL_ck_realloc_block(void *orig, u32 size) {
|
||||
|
||||
return DFL_ck_realloc(orig, size);
|
||||
|
||||
}
|
||||
|
||||
/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */
|
||||
|
||||
static inline u8 *DFL_ck_strdup(u8 *str) {
|
||||
@ -175,43 +163,6 @@ static inline u8 *DFL_ck_strdup(u8 *str) {
|
||||
|
||||
return (u8 *)memcpy(ret, str, size);
|
||||
|
||||
}
|
||||
|
||||
/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized
|
||||
or NULL inputs. */
|
||||
|
||||
static inline void *DFL_ck_memdup(void *mem, u32 size) {
|
||||
|
||||
void *ret;
|
||||
|
||||
if (!mem || !size) { return NULL; }
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = malloc(size);
|
||||
ALLOC_CHECK_RESULT(ret, size);
|
||||
|
||||
return memcpy(ret, mem, size);
|
||||
|
||||
}
|
||||
|
||||
/* Create a buffer with a block of text, appending a NUL terminator at the end.
|
||||
Returns NULL for zero-sized or NULL inputs. */
|
||||
|
||||
static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) {
|
||||
|
||||
u8 *ret;
|
||||
|
||||
if (!mem || !size) { return NULL; }
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = (u8 *)malloc(size + 1);
|
||||
ALLOC_CHECK_RESULT(ret, size);
|
||||
|
||||
memcpy(ret, mem, size);
|
||||
ret[size] = 0;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* In non-debug mode, we just do straightforward aliasing of the above
|
||||
@ -220,10 +171,7 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) {
|
||||
#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()
|
||||
@ -278,10 +226,6 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) {
|
||||
#define ALLOC_OFF_HEAD 8
|
||||
#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1)
|
||||
|
||||
/* Allocator increments for ck_realloc_block(). */
|
||||
|
||||
#define ALLOC_BLK_INC 256
|
||||
|
||||
/* Sanity-checking macros for pointers. */
|
||||
|
||||
#define CHECK_PTR(_p) \
|
||||
@ -441,29 +385,6 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) {
|
||||
|
||||
}
|
||||
|
||||
/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up
|
||||
repeated small reallocs without complicating the user code). */
|
||||
|
||||
static inline void *DFL_ck_realloc_block(void *orig, u32 size) {
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
|
||||
if (orig) {
|
||||
|
||||
CHECK_PTR(orig);
|
||||
|
||||
if (ALLOC_S(orig) >= size) return orig;
|
||||
|
||||
size += ALLOC_BLK_INC;
|
||||
|
||||
}
|
||||
|
||||
#endif /* !DEBUG_BUILD */
|
||||
|
||||
return DFL_ck_realloc(orig, size);
|
||||
|
||||
}
|
||||
|
||||
/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */
|
||||
|
||||
static inline u8 *DFL_ck_strdup(u8 *str) {
|
||||
@ -487,55 +408,6 @@ static inline u8 *DFL_ck_strdup(u8 *str) {
|
||||
|
||||
return memcpy(ret, str, size);
|
||||
|
||||
}
|
||||
|
||||
/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized
|
||||
or NULL inputs. */
|
||||
|
||||
static inline void *DFL_ck_memdup(void *mem, u32 size) {
|
||||
|
||||
void *ret;
|
||||
|
||||
if (!mem || !size) return NULL;
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = malloc(size + ALLOC_OFF_TOTAL);
|
||||
ALLOC_CHECK_RESULT(ret, size);
|
||||
|
||||
ret += ALLOC_OFF_HEAD;
|
||||
|
||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||
|
||||
return memcpy(ret, mem, size);
|
||||
|
||||
}
|
||||
|
||||
/* Create a buffer with a block of text, appending a NUL terminator at the end.
|
||||
Returns NULL for zero-sized or NULL inputs. */
|
||||
|
||||
static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) {
|
||||
|
||||
u8 *ret;
|
||||
|
||||
if (!mem || !size) return NULL;
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = malloc(size + ALLOC_OFF_TOTAL + 1);
|
||||
ALLOC_CHECK_RESULT(ret, size);
|
||||
|
||||
ret += ALLOC_OFF_HEAD;
|
||||
|
||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||
|
||||
memcpy(ret, mem, size);
|
||||
ret[size] = 0;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
@ -546,10 +418,7 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) {
|
||||
#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()
|
||||
@ -618,8 +487,8 @@ static inline void TRK_alloc_buf(void *ptr, const char *file, const char *func,
|
||||
|
||||
/* No space available - allocate more. */
|
||||
|
||||
TRK[bucket] = DFL_ck_realloc_block(
|
||||
TRK[bucket], (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
|
||||
TRK[bucket] = DFL_ck_realloc(TRK[bucket],
|
||||
(TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
|
||||
|
||||
TRK[bucket][i].ptr = ptr;
|
||||
TRK[bucket][i].file = (char *)file;
|
||||
@ -694,16 +563,6 @@ static inline void *TRK_ck_realloc(void *orig, u32 size, const char *file,
|
||||
|
||||
}
|
||||
|
||||
static inline void *TRK_ck_realloc_block(void *orig, u32 size, const char *file,
|
||||
const char *func, u32 line) {
|
||||
|
||||
void *ret = DFL_ck_realloc_block(orig, size);
|
||||
TRK_free_buf(orig, file, func, line);
|
||||
TRK_alloc_buf(ret, file, func, line);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static inline void *TRK_ck_strdup(u8 *str, const char *file, const char *func,
|
||||
u32 line) {
|
||||
|
||||
@ -713,24 +572,6 @@ static inline void *TRK_ck_strdup(u8 *str, const char *file, const char *func,
|
||||
|
||||
}
|
||||
|
||||
static inline void *TRK_ck_memdup(void *mem, u32 size, const char *file,
|
||||
const char *func, u32 line) {
|
||||
|
||||
void *ret = DFL_ck_memdup(mem, size);
|
||||
TRK_alloc_buf(ret, file, func, line);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static inline void *TRK_ck_memdup_str(void *mem, u32 size, const char *file,
|
||||
const char *func, u32 line) {
|
||||
|
||||
void *ret = DFL_ck_memdup_str(mem, size);
|
||||
TRK_alloc_buf(ret, file, func, line);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static inline void TRK_ck_free(void *ptr, const char *file, const char *func,
|
||||
u32 line) {
|
||||
|
||||
@ -749,17 +590,8 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func,
|
||||
#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_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_str(_p1, _p2) \
|
||||
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
@ -771,11 +603,14 @@ 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) {
|
||||
// Commented this out as this behavior doesn't change, according to unittests
|
||||
// if (in == 0 || in > (size_t)-1) {
|
||||
|
||||
return 0; /* avoid undefined behaviour under-/overflow */
|
||||
|
||||
}
|
||||
//
|
||||
// return 0; /* avoid undefined behaviour under-/overflow
|
||||
// */
|
||||
//
|
||||
// }
|
||||
|
||||
size_t out = in - 1;
|
||||
out |= out >> 1;
|
||||
@ -787,6 +622,35 @@ static inline size_t next_pow2(size_t in) {
|
||||
|
||||
}
|
||||
|
||||
/* AFL alloc buffer, the struct is here so we don't need to do fancy ptr
|
||||
* arithmetics */
|
||||
struct afl_alloc_buf {
|
||||
|
||||
/* The complete allocated size, including the header of len
|
||||
* AFL_ALLOC_SIZE_OFFSET */
|
||||
size_t complete_size;
|
||||
/* ptr to the first element of the actual buffer */
|
||||
u8 buf[0];
|
||||
|
||||
};
|
||||
|
||||
#define AFL_ALLOC_SIZE_OFFSET (offsetof(struct afl_alloc_buf, buf))
|
||||
|
||||
/* Returs the container element to this ptr */
|
||||
static inline struct afl_alloc_buf *afl_alloc_bufptr(void *buf) {
|
||||
|
||||
return (struct afl_alloc_buf *)((u8 *)buf - AFL_ALLOC_SIZE_OFFSET);
|
||||
|
||||
}
|
||||
|
||||
/* Gets the maximum size of the buf contents (ptr->complete_size -
|
||||
* AFL_ALLOC_SIZE_OFFSET) */
|
||||
static inline size_t afl_alloc_bufsize(void *buf) {
|
||||
|
||||
return afl_alloc_bufptr(buf)->complete_size - AFL_ALLOC_SIZE_OFFSET;
|
||||
|
||||
}
|
||||
|
||||
/* This function makes sure *size is > size_needed after call.
|
||||
It will realloc *buf otherwise.
|
||||
*size will grow exponentially as per:
|
||||
@ -794,71 +658,68 @@ static inline size_t next_pow2(size_t in) {
|
||||
Will return NULL and free *buf if size_needed is <1 or realloc failed.
|
||||
@return For convenience, this function returns *buf.
|
||||
*/
|
||||
static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
|
||||
static inline void *afl_realloc(void **buf, size_t size_needed) {
|
||||
|
||||
struct afl_alloc_buf *new_buf = NULL;
|
||||
|
||||
size_t current_size = 0;
|
||||
size_t next_size = 0;
|
||||
|
||||
if (likely(*buf)) {
|
||||
|
||||
/* the size is always stored at buf - 1*size_t */
|
||||
new_buf = afl_alloc_bufptr(*buf);
|
||||
current_size = new_buf->complete_size;
|
||||
|
||||
}
|
||||
|
||||
size_needed += AFL_ALLOC_SIZE_OFFSET;
|
||||
|
||||
/* No need to realloc */
|
||||
if (likely(size_needed && *size >= size_needed)) { return *buf; }
|
||||
if (likely(current_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) {
|
||||
|
||||
/* grow exponentially */
|
||||
size_t next_size = next_pow2(size_needed);
|
||||
next_size = INITIAL_GROWTH_SIZE;
|
||||
|
||||
/* handle overflow and zero size_needed */
|
||||
if (!next_size) { next_size = size_needed; }
|
||||
} else {
|
||||
|
||||
/* grow exponentially */
|
||||
next_size = next_pow2(size_needed);
|
||||
|
||||
/* handle overflow: fall back to the original size_needed */
|
||||
if (unlikely(!next_size)) { next_size = size_needed; }
|
||||
|
||||
}
|
||||
|
||||
/* alloc */
|
||||
*buf = realloc(*buf, next_size);
|
||||
*size = *buf ? next_size : 0;
|
||||
new_buf = realloc(new_buf, next_size);
|
||||
if (unlikely(!new_buf)) {
|
||||
|
||||
*buf = NULL;
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
new_buf->complete_size = next_size;
|
||||
*buf = (void *)(new_buf->buf);
|
||||
return *buf;
|
||||
|
||||
}
|
||||
|
||||
/* This function makes sure *size is > size_needed after call.
|
||||
It will realloc *buf otherwise.
|
||||
*size will grow exponentially as per:
|
||||
https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/
|
||||
Will FATAL if size_needed is <1.
|
||||
@return For convenience, this function returns *buf.
|
||||
*/
|
||||
static inline void *ck_maybe_grow(void **buf, size_t *size,
|
||||
size_t size_needed) {
|
||||
static inline void afl_free(void *buf) {
|
||||
|
||||
/* Oops. found a bug? */
|
||||
if (unlikely(size_needed < 1)) { FATAL("cannot grow to non-positive size"); }
|
||||
|
||||
/* No need to realloc */
|
||||
if (likely(*size >= size_needed)) { return *buf; }
|
||||
|
||||
/* No initial size was set */
|
||||
if (size_needed < INITIAL_GROWTH_SIZE) { size_needed = INITIAL_GROWTH_SIZE; }
|
||||
|
||||
/* grow exponentially */
|
||||
size_t next_size = next_pow2(size_needed);
|
||||
|
||||
/* handle overflow */
|
||||
if (!next_size) { next_size = size_needed; }
|
||||
|
||||
/* alloc */
|
||||
*buf = ck_realloc(*buf, next_size);
|
||||
*size = next_size;
|
||||
|
||||
return *buf;
|
||||
if (buf) { free(afl_alloc_bufptr(buf)); }
|
||||
|
||||
}
|
||||
|
||||
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
|
||||
static inline void swap_bufs(void **buf1, size_t *size1, void **buf2,
|
||||
size_t *size2) {
|
||||
static inline void afl_swap_bufs(void **buf1, void **buf2) {
|
||||
|
||||
void * scratch_buf = *buf1;
|
||||
size_t scratch_size = *size1;
|
||||
void *scratch_buf = *buf1;
|
||||
*buf1 = *buf2;
|
||||
*size1 = *size2;
|
||||
*buf2 = scratch_buf;
|
||||
*size2 = scratch_size;
|
||||
|
||||
}
|
||||
|
||||
|
@ -110,5 +110,11 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms);
|
||||
/* Reads the map size from ENV */
|
||||
u32 get_map_size(void);
|
||||
|
||||
/* create a stream file */
|
||||
FILE *create_ffile(u8 *fn);
|
||||
|
||||
/* create a file */
|
||||
s32 create_file(u8 *fn);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
/* Version string: */
|
||||
|
||||
// c = release, d = volatile github dev, e = experimental branch
|
||||
#define VERSION "++2.66c"
|
||||
#define VERSION "++2.68c"
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
@ -70,21 +70,21 @@
|
||||
|
||||
#ifndef __NetBSD__
|
||||
#ifndef WORD_SIZE_64
|
||||
#define MEM_LIMIT 25
|
||||
#else
|
||||
#define MEM_LIMIT 50
|
||||
#else
|
||||
#define MEM_LIMIT 75
|
||||
#endif /* ^!WORD_SIZE_64 */
|
||||
#else /* NetBSD's kernel needs more space for stack, see discussion for issue \
|
||||
#165 */
|
||||
#define MEM_LIMIT 200
|
||||
#define MEM_LIMIT 250
|
||||
#endif
|
||||
/* Default memory limit when running in QEMU mode (MB): */
|
||||
|
||||
#define MEM_LIMIT_QEMU 200
|
||||
#define MEM_LIMIT_QEMU 250
|
||||
|
||||
/* Default memory limit when running in Unicorn mode (MB): */
|
||||
|
||||
#define MEM_LIMIT_UNICORN 200
|
||||
#define MEM_LIMIT_UNICORN 250
|
||||
|
||||
/* Number of calibration cycles per every new test case (and for test
|
||||
cases that show variable behavior): */
|
||||
@ -380,6 +380,10 @@
|
||||
|
||||
#define CMPLOG_SHM_ENV_VAR "__AFL_CMPLOG_SHM_ID"
|
||||
|
||||
/* CPU Affinity lockfile env var */
|
||||
|
||||
#define CPU_AFFINITY_ENV_VAR "__AFL_LOCKFILE"
|
||||
|
||||
/* Uncomment this to use inferior block-coverage-based instrumentation. Note
|
||||
that you need to recompile the target binary for this to have any effect: */
|
||||
|
||||
@ -397,5 +401,28 @@
|
||||
|
||||
// #define IGNORE_FINDS
|
||||
|
||||
/* Text mutations */
|
||||
|
||||
/* Minimum length of a queue input to be evaluated for "is_ascii"? */
|
||||
|
||||
#define AFL_TXT_MIN_LEN 12
|
||||
|
||||
/* What is the minimum percentage of ascii characters present to be classifed
|
||||
as "is_ascii"? */
|
||||
|
||||
#define AFL_TXT_MIN_PERCENT 94
|
||||
|
||||
/* How often to perform ASCII mutations 0 = disable, 1-8 are good values */
|
||||
|
||||
#define AFL_TXT_BIAS 6
|
||||
|
||||
/* Maximum length of a string to tamper with */
|
||||
|
||||
#define AFL_TXT_STRING_MAX_LEN 1024
|
||||
|
||||
/* Maximum mutations on a string */
|
||||
|
||||
#define AFL_TXT_STRING_MAX_MUTATIONS 6
|
||||
|
||||
#endif /* ! _HAVE_CONFIG_H */
|
||||
|
||||
|
@ -28,11 +28,6 @@
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
/* __FUNCTION__ is non-iso */
|
||||
#ifdef __func__
|
||||
#define __FUNCTION__ __func__
|
||||
#endif
|
||||
|
||||
/*******************
|
||||
* Terminal colors *
|
||||
*******************/
|
||||
@ -223,43 +218,43 @@
|
||||
|
||||
/* Die with a verbose non-OS fatal error message. */
|
||||
|
||||
#define FATAL(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] PROGRAM ABORT : " cRST x); \
|
||||
SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __FUNCTION__, \
|
||||
__FILE__, __LINE__); \
|
||||
exit(1); \
|
||||
\
|
||||
#define FATAL(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] PROGRAM ABORT : " cRST x); \
|
||||
SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \
|
||||
__FILE__, __LINE__); \
|
||||
exit(1); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die by calling abort() to provide a core dump. */
|
||||
|
||||
#define ABORT(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] PROGRAM ABORT : " cRST x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __FUNCTION__, \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
\
|
||||
#define ABORT(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] PROGRAM ABORT : " cRST x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die while also including the output of perror(). */
|
||||
|
||||
#define PFATAL(x...) \
|
||||
do { \
|
||||
\
|
||||
fflush(stdout); \
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] SYSTEM ERROR : " cRST x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __FUNCTION__, \
|
||||
__FILE__, __LINE__); \
|
||||
SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \
|
||||
exit(1); \
|
||||
\
|
||||
#define PFATAL(x...) \
|
||||
do { \
|
||||
\
|
||||
fflush(stdout); \
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] SYSTEM ERROR : " cRST x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \
|
||||
__FILE__, __LINE__); \
|
||||
SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \
|
||||
exit(1); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die with FATAL() or PFATAL() depending on the value of res (used to
|
||||
@ -281,7 +276,7 @@
|
||||
#define ck_write(fd, buf, len, fn) \
|
||||
do { \
|
||||
\
|
||||
u32 _len = (len); \
|
||||
s32 _len = (s32)(len); \
|
||||
s32 _res = write(fd, buf, _len); \
|
||||
if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \
|
||||
\
|
||||
@ -290,7 +285,7 @@
|
||||
#define ck_read(fd, buf, len, fn) \
|
||||
do { \
|
||||
\
|
||||
u32 _len = (len); \
|
||||
s32 _len = (s32)(len); \
|
||||
s32 _res = read(fd, buf, _len); \
|
||||
if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \
|
||||
\
|
||||
|
@ -34,6 +34,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_CUSTOM_MUTATOR_LIBRARY",
|
||||
"AFL_CUSTOM_MUTATOR_ONLY",
|
||||
"AFL_CXX",
|
||||
"AFL_CYCLE_SCHEDULES",
|
||||
"AFL_DEBUG",
|
||||
"AFL_DEBUG_CHILD_OUTPUT",
|
||||
"AFL_DEBUG_GDB",
|
||||
@ -47,6 +48,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_GCC_INSTRUMENT_FILE",
|
||||
"AFL_GCJ",
|
||||
"AFL_HANG_TMOUT",
|
||||
"AFL_FORKSRV_INIT_TMOUT",
|
||||
"AFL_HARDEN",
|
||||
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES",
|
||||
"AFL_IMPORT_FIRST",
|
||||
@ -61,9 +63,13 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_REAL_LD",
|
||||
"AFL_LD_PRELOAD",
|
||||
"AFL_LD_VERBOSE",
|
||||
"AFL_LLVM_ALLOWLIST",
|
||||
"AFL_LLVM_DENYLIST",
|
||||
"AFL_LLVM_BLOCKLIST",
|
||||
"AFL_LLVM_CMPLOG",
|
||||
"AFL_LLVM_INSTRIM",
|
||||
"AFL_LLVM_CTX",
|
||||
"AFL_LLVM_DOCUMENT_IDS",
|
||||
"AFL_LLVM_INSTRUMENT",
|
||||
"AFL_LLVM_INSTRIM_LOOPHEAD",
|
||||
"AFL_LLVM_LTO_AUTODICTIONARY",
|
||||
@ -97,6 +103,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_NO_X86", // not really an env but we dont want to warn on it
|
||||
"AFL_MAP_SIZE",
|
||||
"AFL_MAPSIZE",
|
||||
"AFL_MAX_DET_EXTRAS",
|
||||
"AFL_PATH",
|
||||
"AFL_PERFORMANCE_FILE",
|
||||
"AFL_PRELOAD",
|
||||
@ -105,12 +112,16 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_QEMU_COMPCOV_DEBUG",
|
||||
"AFL_QEMU_DEBUG_MAPS",
|
||||
"AFL_QEMU_DISABLE_CACHE",
|
||||
"AFL_QEMU_DRIVER_NO_HOOK",
|
||||
"AFL_QEMU_PERSISTENT_ADDR",
|
||||
"AFL_QEMU_PERSISTENT_CNT",
|
||||
"AFL_QEMU_PERSISTENT_GPR",
|
||||
"AFL_QEMU_PERSISTENT_HOOK",
|
||||
"AFL_QEMU_PERSISTENT_RET",
|
||||
"AFL_QEMU_PERSISTENT_RETADDR_OFFSET",
|
||||
"AFL_QEMU_PERSISTENT_EXITS",
|
||||
"AFL_QEMU_INST_RANGES",
|
||||
"AFL_QEMU_SNAPSHOT",
|
||||
"AFL_QUIET",
|
||||
"AFL_RANDOM_ALLOC_CANARY",
|
||||
"AFL_REAL_PATH",
|
||||
@ -129,6 +140,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_USE_CFISAN",
|
||||
"AFL_WINE_PATH",
|
||||
"AFL_NO_SNAPSHOT",
|
||||
"AFL_EXPAND_HAVOC_NOW",
|
||||
NULL
|
||||
|
||||
};
|
||||
|
@ -56,6 +56,7 @@ typedef struct afl_forkserver {
|
||||
u8 no_unlink; /* do not unlink cur_input */
|
||||
|
||||
u32 exec_tmout; /* Configurable exec timeout (ms) */
|
||||
u32 init_tmout; /* Configurable init 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) */
|
||||
@ -89,9 +90,9 @@ typedef struct afl_forkserver {
|
||||
/* Function to kick off the forkserver child */
|
||||
void (*init_child_func)(struct afl_forkserver *fsrv, char **argv);
|
||||
|
||||
u8 *function_opt; /* for autodictionary: afl ptr */
|
||||
u8 *afl_ptr; /* for autodictionary: afl ptr */
|
||||
|
||||
void (*function_ptr)(void *afl_tmp, u8 *mem, u32 len);
|
||||
void (*add_extra_func)(void *afl_ptr, u8 *mem, u32 len);
|
||||
|
||||
} afl_forkserver_t;
|
||||
|
||||
|
@ -25,8 +25,7 @@
|
||||
// From AFL-Snapshot-LKM/include/afl_snapshot.h (must be kept synced)
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define AFL_SNAPSHOT_FILE_NAME "/dev/afl_snapshot"
|
||||
@ -35,6 +34,35 @@
|
||||
|
||||
#define AFL_SNAPSHOT_IOCTL_DO _IO(AFL_SNAPSHOT_IOCTL_MAGIC, 1)
|
||||
#define AFL_SNAPSHOT_IOCTL_CLEAN _IO(AFL_SNAPSHOT_IOCTL_MAGIC, 2)
|
||||
#define AFL_SNAPSHOT_EXCLUDE_VMRANGE \
|
||||
_IOR(AFL_SNAPSHOT_IOCTL_MAGIC, 3, struct afl_snapshot_vmrange_args *)
|
||||
#define AFL_SNAPSHOT_INCLUDE_VMRANGE \
|
||||
_IOR(AFL_SNAPSHOT_IOCTL_MAGIC, 4, struct afl_snapshot_vmrange_args *)
|
||||
#define AFL_SNAPSHOT_IOCTL_TAKE _IOR(AFL_SNAPSHOT_IOCTL_MAGIC, 5, int)
|
||||
#define AFL_SNAPSHOT_IOCTL_RESTORE _IO(AFL_SNAPSHOT_IOCTL_MAGIC, 6)
|
||||
|
||||
// Trace new mmaped ares and unmap them on restore.
|
||||
#define AFL_SNAPSHOT_MMAP 1
|
||||
// Do not snapshot any page (by default all writeable not-shared pages
|
||||
// are shanpshotted.
|
||||
#define AFL_SNAPSHOT_BLOCK 2
|
||||
// Snapshot file descriptor state, close newly opened descriptors
|
||||
#define AFL_SNAPSHOT_FDS 4
|
||||
// Snapshot registers state
|
||||
#define AFL_SNAPSHOT_REGS 8
|
||||
// Perform a restore when exit_group is invoked
|
||||
#define AFL_SNAPSHOT_EXIT 16
|
||||
// TODO(andrea) allow not COW snapshots (high perf on small processes)
|
||||
// Disable COW, restore all the snapshotted pages
|
||||
#define AFL_SNAPSHOT_NOCOW 32
|
||||
// Do not snapshot Stack pages
|
||||
#define AFL_SNAPSHOT_NOSTACK 64
|
||||
|
||||
struct afl_snapshot_vmrange_args {
|
||||
|
||||
unsigned long start, end;
|
||||
|
||||
};
|
||||
|
||||
static int afl_snapshot_dev_fd;
|
||||
|
||||
@ -45,15 +73,43 @@ static int afl_snapshot_init(void) {
|
||||
|
||||
}
|
||||
|
||||
static int afl_snapshot_do() {
|
||||
static void afl_snapshot_exclude_vmrange(void *start, void *end) {
|
||||
|
||||
struct afl_snapshot_vmrange_args args = {(unsigned long)start,
|
||||
(unsigned long)end};
|
||||
ioctl(afl_snapshot_dev_fd, AFL_SNAPSHOT_EXCLUDE_VMRANGE, &args);
|
||||
|
||||
}
|
||||
|
||||
static void afl_snapshot_include_vmrange(void *start, void *end) {
|
||||
|
||||
struct afl_snapshot_vmrange_args args = {(unsigned long)start,
|
||||
(unsigned long)end};
|
||||
ioctl(afl_snapshot_dev_fd, AFL_SNAPSHOT_INCLUDE_VMRANGE, &args);
|
||||
|
||||
}
|
||||
|
||||
static int afl_snapshot_take(int config) {
|
||||
|
||||
return ioctl(afl_snapshot_dev_fd, AFL_SNAPSHOT_IOCTL_TAKE, config);
|
||||
|
||||
}
|
||||
|
||||
static int afl_snapshot_do(void) {
|
||||
|
||||
return ioctl(afl_snapshot_dev_fd, AFL_SNAPSHOT_IOCTL_DO);
|
||||
|
||||
}
|
||||
|
||||
static int afl_snapshot_clean(void) {
|
||||
static void afl_snapshot_restore(void) {
|
||||
|
||||
return ioctl(afl_snapshot_dev_fd, AFL_SNAPSHOT_IOCTL_CLEAN);
|
||||
ioctl(afl_snapshot_dev_fd, AFL_SNAPSHOT_IOCTL_RESTORE);
|
||||
|
||||
}
|
||||
|
||||
static void afl_snapshot_clean(void) {
|
||||
|
||||
ioctl(afl_snapshot_dev_fd, AFL_SNAPSHOT_IOCTL_CLEAN);
|
||||
|
||||
}
|
||||
|
||||
|
3187
include/xxh3.h
3187
include/xxh3.h
File diff suppressed because it is too large
Load Diff
3763
include/xxhash.h
3763
include/xxhash.h
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,7 @@ all: libdislocator.so
|
||||
|
||||
VPATH = ..
|
||||
libdislocator.so: libdislocator.so.c ../config.h
|
||||
$(CC) $(CFLAGS) -shared -fPIC libdislocator.so.c -o ../$@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC libdislocator.so.c -o ../$@ $(LDFLAGS)
|
||||
|
||||
.NOTPARALLEL: clean
|
||||
|
||||
|
@ -166,7 +166,7 @@ static u32 alloc_canary;
|
||||
|
||||
static void *__dislocator_alloc(size_t len) {
|
||||
|
||||
u8 * ret;
|
||||
u8 * ret, *base;
|
||||
size_t tlen;
|
||||
int flags, fd, sp;
|
||||
|
||||
@ -189,6 +189,7 @@ static void *__dislocator_alloc(size_t len) {
|
||||
/* We will also store buffer length and a canary below the actual buffer, so
|
||||
let's add 8 bytes for that. */
|
||||
|
||||
base = NULL;
|
||||
tlen = (1 + PG_COUNT(rlen + 8)) * PAGE_SIZE;
|
||||
flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
fd = -1;
|
||||
@ -201,12 +202,20 @@ static void *__dislocator_alloc(size_t len) {
|
||||
if (sp) flags |= MAP_HUGETLB;
|
||||
#elif defined(__FreeBSD__)
|
||||
if (sp) flags |= MAP_ALIGNED_SUPER;
|
||||
#elif defined(__sun)
|
||||
if (sp) {
|
||||
|
||||
base = (void *)(caddr_t)(1 << 21);
|
||||
flags |= MAP_ALIGN;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#else
|
||||
(void)sp;
|
||||
#endif
|
||||
|
||||
ret = (u8 *)mmap(NULL, tlen, PROT_READ | PROT_WRITE, flags, fd, 0);
|
||||
ret = (u8 *)mmap(base, tlen, PROT_READ | PROT_WRITE, flags, fd, 0);
|
||||
#if defined(USEHUGEPAGE)
|
||||
/* We try one more time with regular call */
|
||||
if (ret == MAP_FAILED) {
|
||||
@ -217,6 +226,8 @@ static void *__dislocator_alloc(size_t len) {
|
||||
flags &= -MAP_HUGETLB;
|
||||
#elif defined(__FreeBSD__)
|
||||
flags &= -MAP_ALIGNED_SUPER;
|
||||
#elif defined(__sun)
|
||||
flags &= -MAP_ALIGN;
|
||||
#endif
|
||||
ret = (u8 *)mmap(NULL, tlen, PROT_READ | PROT_WRITE, flags, fd, 0);
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
PREFIX ?= /usr/local
|
||||
HELPER_PATH = $(PREFIX)/lib/afl
|
||||
DOC_PATH ?= $(PREFIX)/share/doc/afl
|
||||
MAN_PATH ?= $(PREFIX)/man/man8
|
||||
MAN_PATH ?= $(PREFIX)/share/man/man8
|
||||
|
||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
@ -28,22 +28,22 @@ UNAME_S =$(shell uname -s)# GNU make
|
||||
UNAME_S:sh=uname -s # BSD make
|
||||
_UNIQ=_QINU_
|
||||
|
||||
_OS_DL = $(_UNIQ)$(UNAME_S)
|
||||
__OS_DL = $(_OS_DL:$(_UNIQ)Linux=$(_UNIQ))
|
||||
___OS_DL = $(__OS_DL:$(_UNIQ)Darwin=$(_UNIQ))
|
||||
____OS_DL = $(___OS_DL:$(_UNIQ)DragonFly=$(_UNIQ))
|
||||
_____OS_DL = $(____OS_DL:$(_UNIQ)$(UNAME_S)=)
|
||||
______OS_DL = $(_____OS_DL:$(_UNIQ)="-ldl")
|
||||
_OS_DL = $(_UNIQ)$(UNAME_S)
|
||||
__OS_DL = $(_OS_DL:$(_UNIQ)Linux=$(_UNIQ))
|
||||
___OS_DL = $(__OS_DL:$(_UNIQ)Darwin=$(_UNIQ))
|
||||
____OS_DL = $(___OS_DL:$(_UNIQ)$(UNAME_S)=)
|
||||
_____OS_DL = $(____OS_DL:$(_UNIQ)="-ldl")
|
||||
|
||||
_OS_TARGET = $(____OS_DL:$(_UNIQ)FreeBSD=$(_UNIQ))
|
||||
__OS_TARGET = $(_OS_TARGET:$(_UNIQ)OpenBSD=$(_UNIQ))
|
||||
___OS_TARGET = $(__OS_TARGET:$(_UNIQ)NetBSD=$(_UNIQ))
|
||||
____OS_TARGET = $(___OS_TARGET:$(_UNIQ)Haiku=$(_UNIQ))
|
||||
_____OS_TARGET = $(___OS_TARGET:$(_UNIQ)$(UNAME_S)=)
|
||||
_OS_TARGET = $(___OS_DL:$(_UNIQ)FreeBSD=$(_UNIQ))
|
||||
__OS_TARGET = $(_OS_TARGET:$(_UNIQ)OpenBSD=$(_UNIQ))
|
||||
___OS_TARGET = $(__OS_TARGET:$(_UNIQ)NetBSD=$(_UNIQ))
|
||||
____OS_TARGET = $(___OS_TARGET:$(_UNIQ)Haiku=$(_UNIQ))
|
||||
_____OS_TARGET = $(____OS_TARGET:$(_UNIQ)SunOS=$(_UNIQ))
|
||||
______OS_TARGET = $(_____OS_TARGET:$(_UNIQ)$(UNAME_S)=)
|
||||
|
||||
TARGETS = $(____OS_TARGET:$(_UNIQ)=libtokencap.so)
|
||||
TARGETS = $(______OS_TARGET:$(_UNIQ)=libtokencap.so)
|
||||
|
||||
LDFLAGS += $(______OS_DL)
|
||||
LDFLAGS += $(_____OS_DL)
|
||||
|
||||
#ifeq "$(shell uname)" "Linux"
|
||||
# TARGETS = libtokencap.so
|
||||
@ -70,7 +70,7 @@ all: $(TARGETS)
|
||||
|
||||
VPATH = ..
|
||||
libtokencap.so: libtokencap.so.c ../config.h
|
||||
$(CC) $(CFLAGS) -shared -fPIC $< -o ../$@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC $< -o ../$@ $(LDFLAGS)
|
||||
|
||||
.NOTPARALLEL: clean
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
#if !defined __linux__ && !defined __APPLE__ && !defined __FreeBSD__ && \
|
||||
!defined __OpenBSD__ && !defined __NetBSD__ && !defined __DragonFly__ && \
|
||||
!defined(__HAIKU__)
|
||||
!defined(__HAIKU__) && !defined(__sun)
|
||||
#error "Sorry, this library is unsupported in this platform for now!"
|
||||
#endif /* !__linux__ && !__APPLE__ && ! __FreeBSD__ && ! __OpenBSD__ && \
|
||||
!__NetBSD__*/
|
||||
@ -52,6 +52,10 @@
|
||||
#include <sys/mman.h>
|
||||
#elif defined __HAIKU__
|
||||
#include <kernel/image.h>
|
||||
#elif defined __sun
|
||||
/* For map addresses the old struct is enough */
|
||||
#include <sys/procfs.h>
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
@ -237,6 +241,8 @@ static void __tokencap_load_mappings(void) {
|
||||
image_info ii;
|
||||
int32_t group = 0;
|
||||
|
||||
__tokencap_ro_loaded = 1;
|
||||
|
||||
while (get_next_image_info(0, &group, &ii) == B_OK) {
|
||||
|
||||
__tokencap_ro[__tokencap_ro_cnt].st = ii.text;
|
||||
@ -246,6 +252,38 @@ static void __tokencap_load_mappings(void) {
|
||||
|
||||
}
|
||||
|
||||
#elif defined __sun
|
||||
prmap_t *c, *map;
|
||||
char path[PATH_MAX];
|
||||
ssize_t r;
|
||||
size_t hint;
|
||||
int fd;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%ld/map", getpid());
|
||||
fd = open(path, O_RDONLY);
|
||||
hint = (1 << 20);
|
||||
map = malloc(hint);
|
||||
|
||||
__tokencap_ro_loaded = 1;
|
||||
|
||||
for (; (r = pread(fd, map, hint, 0)) == hint;) {
|
||||
|
||||
hint <<= 1;
|
||||
map = realloc(map, hint);
|
||||
|
||||
}
|
||||
|
||||
for (c = map; r > 0; c++, r -= sizeof(prmap_t)) {
|
||||
|
||||
__tokencap_ro[__tokencap_ro_cnt].st = (void *)c->pr_vaddr;
|
||||
__tokencap_ro[__tokencap_ro_cnt].en = (void *)(c->pr_vaddr + c->pr_size);
|
||||
|
||||
if (++__tokencap_ro_cnt == MAX_MAPPINGS) break;
|
||||
|
||||
}
|
||||
|
||||
free(map);
|
||||
close(fd);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -24,23 +24,26 @@ HELPER_PATH ?= $(PREFIX)/lib/afl
|
||||
BIN_PATH ?= $(PREFIX)/bin
|
||||
DOC_PATH ?= $(PREFIX)/share/doc/afl
|
||||
MISC_PATH ?= $(PREFIX)/share/afl
|
||||
MAN_PATH ?= $(PREFIX)/man/man8
|
||||
MAN_PATH ?= $(PREFIX)/share/man/man8
|
||||
|
||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u "+%Y-%m-%d")
|
||||
|
||||
ifeq "$(shell uname)" "OpenBSD"
|
||||
LLVM_CONFIG ?= $(BIN_PATH)/llvm-config
|
||||
HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1)
|
||||
ifeq "$(HAS_OPT)" "1"
|
||||
$(error llvm_mode needs a complete llvm installation (versions 3.4 up to 11) -> e.g. "pkg_add llvm-7.0.1p9")
|
||||
$(error llvm_mode needs a complete llvm installation (versions 3.4 up to 12) -> e.g. "pkg_add llvm-7.0.1p9")
|
||||
endif
|
||||
else
|
||||
LLVM_CONFIG ?= llvm-config
|
||||
endif
|
||||
|
||||
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_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^19' && echo 1 || echo 0 )
|
||||
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 )
|
||||
LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-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)
|
||||
@ -53,20 +56,29 @@ ifeq "$(LLVMVER)" ""
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_UNSUPPORTED)" "1"
|
||||
$(warning llvm_mode only supports llvm versions 3.4 up to 11)
|
||||
$(warning llvm_mode only supports llvm versions 3.4 up to 12)
|
||||
endif
|
||||
|
||||
LLVM_TOO_OLD=1
|
||||
|
||||
ifeq "$(LLVM_MAJOR)" "9"
|
||||
$(info [+] llvm_mode detected llvm 9, enabling neverZero implementation)
|
||||
LLVM_TOO_OLD=0
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_NEW_API)" "1"
|
||||
$(info [+] llvm_mode detected llvm 10+, enabling neverZero implementation and c++14)
|
||||
LLVM_STDCXX = c++14
|
||||
LLVM_TOO_OLD=0
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_MAJOR)" "11"
|
||||
$(info [+] llvm_mode detected llvm 11, enabling afl-clang-lto LTO implementation)
|
||||
ifeq "$(LLVM_TOO_OLD)" "1"
|
||||
$(info [!] llvm_mode detected an old version of llvm, upgrade to at least 9 or preferable 11!)
|
||||
$(shell sleep 1)
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_HAVE_LTO)" "1"
|
||||
$(info [+] llvm_mode detected llvm 11+, enabling afl-clang-lto LTO implementation)
|
||||
LLVM_LTO = 1
|
||||
#TEST_MMAP = 1
|
||||
endif
|
||||
@ -182,18 +194,33 @@ ifeq "$(LLVM_LTO)" "1"
|
||||
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)
|
||||
$(warn ld.lld not found, cannot enable LTO mode)
|
||||
LLVM_LTO = 0
|
||||
endif
|
||||
endif
|
||||
else
|
||||
$(warn clang option -flto is not working - maybe LLVMgold.so not found - cannot enable LTO mode)
|
||||
LLVM_LTO = 0
|
||||
endif
|
||||
endif
|
||||
|
||||
AFL_CLANG_FUSELD=
|
||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||
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
|
||||
ifeq "$(LLVM_LTO)" "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
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=ld.lld --ld-path=$(LLVM_BINDIR)/ld.lld -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
AFL_CLANG_LDPATH=1
|
||||
endif
|
||||
else
|
||||
$(warn -fuse-ld is not working, cannot enable LTO mode)
|
||||
LLVM_LTO = 0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fdebug-prefix-map=$(CURDIR)=llvm_mode -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
AFL_CLANG_DEBUG_PREFIX = -fdebug-prefix-map="$(CURDIR)=llvm_mode"
|
||||
else
|
||||
AFL_CLANG_DEBUG_PREFIX = ""
|
||||
endif
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2
|
||||
@ -202,8 +229,11 @@ CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ../include/ \
|
||||
-DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \
|
||||
-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
|
||||
-DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \
|
||||
-DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \
|
||||
-DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \
|
||||
-DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function \
|
||||
$(AFL_CLANG_DEBUG_PREFIX)
|
||||
override CFLAGS += $(CFLAGS_SAFE)
|
||||
|
||||
ifdef AFL_TRACE_PC
|
||||
@ -220,7 +250,7 @@ endif
|
||||
ifneq "$(LLVM_CONFIG)" ""
|
||||
CLANG_CFL += -I$(shell dirname $(LLVM_CONFIG))/../include
|
||||
endif
|
||||
CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS)
|
||||
CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations
|
||||
CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS)
|
||||
|
||||
|
||||
@ -253,7 +283,7 @@ ifeq "$(TEST_MMAP)" "1"
|
||||
LDFLAGS += -Wno-deprecated-declarations
|
||||
endif
|
||||
|
||||
PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-ld-lto ../afl-llvm-lto-instrumentlist.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
|
||||
PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-ld-lto ../afl-llvm-lto-instrumentlist.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 ../SanitizerCoverageLTO.so
|
||||
|
||||
# If prerequisites are not given, warn, do not build anything, and exit with code 0
|
||||
ifeq "$(LLVMVER)" ""
|
||||
@ -308,10 +338,10 @@ endif
|
||||
@echo "[+] All set and ready to build."
|
||||
|
||||
afl-common.o: ../src/afl-common.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS)
|
||||
|
||||
../afl-clang-fast: afl-clang-fast.c afl-common.o | test_deps
|
||||
$(CC) $(CLANG_CFL) $(CFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\"
|
||||
$(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\"
|
||||
ln -sf afl-clang-fast ../afl-clang-fast++
|
||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
@ -321,7 +351,7 @@ endif
|
||||
endif
|
||||
|
||||
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 $@
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC -std=$(LLVM_STDCXX) -c $< -o $@
|
||||
|
||||
../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc afl-llvm-common.o | test_deps
|
||||
-$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
@ -339,20 +369,20 @@ endif
|
||||
|
||||
../afl-ld-lto: afl-ld-lto.c
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@
|
||||
endif
|
||||
|
||||
../SanitizerCoverageLTO.so: SanitizerCoverageLTO.so.cc
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
endif
|
||||
|
||||
../afl-llvm-lto-instrumentation.so: afl-llvm-lto-instrumentation.so.cc afl-llvm-common.o
|
||||
ifeq "$(LLVM_LTO)" "1"
|
||||
$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
$(CLANG_BIN) $(CFLAGS_SAFE) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto.o
|
||||
@$(CLANG_BIN) $(CFLAGS_SAFE) -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_SAFE) -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_CPPFL) -DLLVMInsTrim_EXPORTS -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto.o
|
||||
@$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -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_SAFE) $(CPPFLAGS) -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
|
||||
|
||||
# laf
|
||||
@ -371,20 +401,20 @@ endif
|
||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
||||
|
||||
document:
|
||||
$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o
|
||||
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -m32 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -m64 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o
|
||||
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m32 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m64 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
|
||||
../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps
|
||||
$(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -fPIC -c $< -o $@
|
||||
$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -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)... "
|
||||
@$(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
@$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -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)... "
|
||||
@$(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||
@$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -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..."
|
||||
@ -403,7 +433,7 @@ all_done: test_build
|
||||
install: all
|
||||
install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
|
||||
if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-instrumentlist.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-rt-lto*.o ../afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../afl-ld-lto ]; then set -e; install -m 755 ../afl-ld-lto $${DESTDIR}$(BIN_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
|
||||
@ -411,15 +441,18 @@ install: all
|
||||
if [ -f ../split-compares-pass.so ]; then set -e; install -m 755 ../split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../split-switches-pass.so ]; then set -e; install -m 755 ../split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f ../cmplog-instructions-pass.so ]; then set -e; install -m 755 ../cmplog-*-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
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
|
||||
if [ -f ../SanitizerCoverageLTO.so ]; then set -e; install -m 755 ../SanitizerCoverageLTO.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
set -e; install -m 644 ../dynamic_list.txt $${DESTDIR}$(HELPER_PATH)
|
||||
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
|
||||
install -m 644 README.*.md $${DESTDIR}$(DOC_PATH)/
|
||||
install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.llvm_mode.md
|
||||
install -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.llvm_mode.md
|
||||
|
||||
vpath % ..
|
||||
%.8: %
|
||||
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ../$@
|
||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > ../$@
|
||||
@echo .SH NAME >> ../$@
|
||||
@echo .B $* >> ../$@
|
||||
@echo -n ".B $* \- " >> ../$@
|
||||
@../$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ../$@
|
||||
@echo >> ../$@
|
||||
@echo .SH SYNOPSIS >> ../$@
|
||||
@../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@
|
||||
|
@ -56,7 +56,6 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
protected:
|
||||
uint32_t function_minimum_size = 1;
|
||||
uint32_t debug = 0;
|
||||
char * skip_nozero = NULL;
|
||||
|
||||
private:
|
||||
@ -95,14 +94,13 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 4 || \
|
||||
#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 {
|
||||
|
||||
char be_quiet = 0;
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
|
||||
@ -134,10 +132,6 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
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");
|
||||
@ -146,7 +140,7 @@ struct InsTrim : public ModulePass {
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
unsigned int ngram_size = 0;
|
||||
/* Decide previous location vector size (must be a power of two) */
|
||||
VectorType *PrevLocTy;
|
||||
VectorType *PrevLocTy = NULL;
|
||||
|
||||
if (ngram_size_str)
|
||||
if (sscanf(ngram_size_str, "%u", &ngram_size) != 1 || ngram_size < 2 ||
|
||||
@ -183,10 +177,16 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
// IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
|
||||
uint64_t PrevLocVecSize = PowerOf2Ceil(PrevLocSize);
|
||||
int PrevLocVecSize = PowerOf2Ceil(PrevLocSize);
|
||||
IntegerType *IntLocTy =
|
||||
IntegerType::getIntNTy(C, sizeof(PREV_LOC_T) * CHAR_BIT);
|
||||
if (ngram_size) PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize);
|
||||
if (ngram_size)
|
||||
PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize
|
||||
#if LLVM_VERSION_MAJOR >= 12
|
||||
,
|
||||
false
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
|
||||
/* Get globals for the SHM region and the previous location. Note that
|
||||
@ -196,7 +196,7 @@ struct InsTrim : public ModulePass {
|
||||
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
GlobalVariable *AFLPrevLoc;
|
||||
GlobalVariable *AFLContext;
|
||||
GlobalVariable *AFLContext = NULL;
|
||||
LoadInst * PrevCtx = NULL; // for CTX sensitive coverage
|
||||
|
||||
if (ctx_str)
|
||||
@ -243,7 +243,7 @@ struct InsTrim : public ModulePass {
|
||||
for (unsigned I = 0; I < PrevLocSize - 1; ++I)
|
||||
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I));
|
||||
|
||||
for (unsigned I = PrevLocSize; I < PrevLocVecSize; ++I)
|
||||
for (int I = PrevLocSize; I < PrevLocVecSize; ++I)
|
||||
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize));
|
||||
|
||||
Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle);
|
||||
@ -258,6 +258,8 @@ struct InsTrim : public ModulePass {
|
||||
u64 total_rs = 0;
|
||||
u64 total_hs = 0;
|
||||
|
||||
scanForDangerousFunctions(&M);
|
||||
|
||||
for (Function &F : M) {
|
||||
|
||||
if (debug) {
|
||||
|
@ -19,15 +19,6 @@ see how often the loop has been rerun.
|
||||
This again is a tradeoff for speed for less path information.
|
||||
To enable this mode set `AFL_LLVM_INSTRIM_LOOPHEAD=1`.
|
||||
|
||||
There is an additional optimization option that skips single block
|
||||
functions. In 95% of the C targets and (guess) 50% of the C++ targets
|
||||
it is good to enable this, as otherwise pointless instrumentation occurs.
|
||||
The corner case where we want this instrumentation is when vtable/call table
|
||||
is used and the index to that vtable/call table is not set in specific
|
||||
basic blocks.
|
||||
To enable skipping these (most of the time) unnecessary instrumentations set
|
||||
`AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK=1`
|
||||
|
||||
## Background
|
||||
|
||||
The paper: [InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing]
|
||||
|
@ -1,79 +0,0 @@
|
||||
# Using afl++ with partial instrumentation
|
||||
|
||||
This file describes how you can selectively instrument only the source files
|
||||
that are interesting to you using the LLVM instrumentation provided by
|
||||
afl++
|
||||
|
||||
Originally developed by Christian Holler (:decoder) <choller@mozilla.com>.
|
||||
|
||||
## 1) Description and purpose
|
||||
|
||||
When building and testing complex programs where only a part of the program is
|
||||
the fuzzing target, it often helps to only instrument the necessary parts of
|
||||
the program, leaving the rest uninstrumented. This helps to focus the fuzzer
|
||||
on the important parts of the program, avoiding undesired noise and
|
||||
disturbance by uninteresting code being exercised.
|
||||
|
||||
For this purpose, I have added a "partial instrumentation" support to the LLVM
|
||||
mode of AFLFuzz that allows you to specify on a source file level which files
|
||||
should be compiled with or without instrumentation.
|
||||
|
||||
|
||||
## 2) Building the LLVM module
|
||||
|
||||
The new code is part of the existing afl++ LLVM module in the llvm_mode/
|
||||
subdirectory. There is nothing specifically to do :)
|
||||
|
||||
|
||||
## 3) How to use the partial instrumentation mode
|
||||
|
||||
In order to build with partial instrumentation, you need to build with
|
||||
afl-clang-fast and afl-clang-fast++ respectively. The only required change is
|
||||
that you need to set the environment variable AFL_LLVM_INSTRUMENT_FILE when calling
|
||||
the compiler.
|
||||
|
||||
The environment variable must point to a file containing all the filenames
|
||||
that should be instrumented. For matching, the filename that is being compiled
|
||||
must end in the filename entry contained in this the instrument file list (to avoid breaking
|
||||
the matching when absolute paths are used during compilation).
|
||||
|
||||
For example if your source tree looks like this:
|
||||
|
||||
```
|
||||
project/
|
||||
project/feature_a/a1.cpp
|
||||
project/feature_a/a2.cpp
|
||||
project/feature_b/b1.cpp
|
||||
project/feature_b/b2.cpp
|
||||
```
|
||||
|
||||
and you only want to test feature_a, then create a the instrument file list file containing:
|
||||
|
||||
```
|
||||
feature_a/a1.cpp
|
||||
feature_a/a2.cpp
|
||||
```
|
||||
|
||||
However if the instrument file list file contains only this, it works as well:
|
||||
|
||||
```
|
||||
a1.cpp
|
||||
a2.cpp
|
||||
```
|
||||
|
||||
but it might lead to files being unwantedly instrumented if the same filename
|
||||
exists somewhere else in the project directories.
|
||||
|
||||
The created the instrument file list file is then set to AFL_LLVM_INSTRUMENT_FILE when you compile
|
||||
your program. For each file that didn't match the the instrument file list, the compiler will
|
||||
issue a warning at the end stating that no blocks were instrumented. If you
|
||||
didn't intend to instrument that file, then you can safely ignore that warning.
|
||||
|
||||
For old LLVM versions this feature might require to be compiled with debug
|
||||
information (-g), however at least from llvm version 6.0 onwards this is not
|
||||
required anymore (and might hurt performance and crash detection, so better not
|
||||
use -g).
|
||||
|
||||
## 4) UNIX-style filename pattern matching
|
||||
You can add UNIX-style pattern matching in the the instrument file list entries. See `man
|
||||
fnmatch` for the syntax. We do not set any of the `fnmatch` flags.
|
86
llvm_mode/README.instrument_list.md
Normal file
86
llvm_mode/README.instrument_list.md
Normal file
@ -0,0 +1,86 @@
|
||||
# Using afl++ with partial instrumentation
|
||||
|
||||
This file describes how you can selectively instrument only the source files
|
||||
or functions that are interesting to you using the LLVM instrumentation
|
||||
provided by afl++
|
||||
|
||||
## 1) Description and purpose
|
||||
|
||||
When building and testing complex programs where only a part of the program is
|
||||
the fuzzing target, it often helps to only instrument the necessary parts of
|
||||
the program, leaving the rest uninstrumented. This helps to focus the fuzzer
|
||||
on the important parts of the program, avoiding undesired noise and
|
||||
disturbance by uninteresting code being exercised.
|
||||
|
||||
For this purpose, a "partial instrumentation" support en par with llvm sancov
|
||||
is provided by afl++ that allows you to specify on a source file and function
|
||||
level which function should be compiled with or without instrumentation.
|
||||
|
||||
Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead:
|
||||
https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation
|
||||
|
||||
The llvm sancov list format is fully supported by afl++, however afl++ has
|
||||
more flexibility.
|
||||
|
||||
## 2) Building the LLVM module
|
||||
|
||||
The new code is part of the existing afl++ LLVM module in the llvm_mode/
|
||||
subdirectory. There is nothing specifically to do :)
|
||||
|
||||
## 3) How to use the partial instrumentation mode
|
||||
|
||||
In order to build with partial instrumentation, you need to build with
|
||||
afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++.
|
||||
The only required change is that you need to set either the environment variable
|
||||
AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename.
|
||||
|
||||
That file then contains the filenames or functions that should be instrumented
|
||||
(AFL_LLVM_ALLOWLIST) or should specifically NOT be instrumented (AFL_LLVM_DENYLIST).
|
||||
|
||||
For matching, the function/filename that is being compiled must end in the
|
||||
function/filename entry contained in this instrument file list (to avoid
|
||||
breaking the matching when absolute paths are used during compilation).
|
||||
|
||||
**NOTE:** In builds with optimization enabled functions might be inlined and would not match!
|
||||
|
||||
For example if your source tree looks like this:
|
||||
```
|
||||
project/
|
||||
project/feature_a/a1.cpp
|
||||
project/feature_a/a2.cpp
|
||||
project/feature_b/b1.cpp
|
||||
project/feature_b/b2.cpp
|
||||
```
|
||||
|
||||
and you only want to test feature_a, then create a instrument file list file containing:
|
||||
```
|
||||
feature_a/a1.cpp
|
||||
feature_a/a2.cpp
|
||||
```
|
||||
|
||||
However if the instrument file list file contains only this, it works as well:
|
||||
```
|
||||
a1.cpp
|
||||
a2.cpp
|
||||
```
|
||||
but it might lead to files being unwantedly instrumented if the same filename
|
||||
exists somewhere else in the project directories.
|
||||
|
||||
You can also specify function names. Note that for C++ the function names
|
||||
must be mangled to match!
|
||||
|
||||
afl++ is able to identify if an entry is a filename or a function.
|
||||
However if you want to be sure (and compliant to the sancov allow/blocklist
|
||||
format), you can specify source file entries like this:
|
||||
```
|
||||
src: *malloc.c
|
||||
```
|
||||
and function entries like this:
|
||||
```
|
||||
fun: MallocFoo
|
||||
```
|
||||
Note that whitespace is ignored and comments (`# foo`) are supported.
|
||||
|
||||
## 4) UNIX-style pattern matching
|
||||
You can add UNIX-style pattern matching in the the instrument file list entries.
|
||||
See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags.
|
@ -35,8 +35,8 @@ bit_width may be 64, 32 or 16.
|
||||
A new experimental feature is splitting floating point comparisons into a
|
||||
series of sign, exponent and mantissa comparisons followed by splitting each
|
||||
of them into 8 bit comparisons when necessary.
|
||||
It is activated with the `AFL_LLVM_LAF_SPLIT_FLOATS` setting, available only
|
||||
when `AFL_LLVM_LAF_SPLIT_COMPARES` is set.
|
||||
It is activated with the `AFL_LLVM_LAF_SPLIT_FLOATS` setting.
|
||||
Note that setting this automatically activates `AFL_LLVM_LAF_SPLIT_COMPARES`
|
||||
|
||||
You can also set `AFL_LLVM_LAF_ALL` and have all of the above enabled :-)
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## TLDR;
|
||||
|
||||
This version requires a current llvm 11 compiled from the github master.
|
||||
This version requires a current llvm 11+ compiled from the github master.
|
||||
|
||||
1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better
|
||||
coverage than anything else that is out there in the AFL world
|
||||
@ -10,16 +10,13 @@ This version requires a current llvm 11 compiled from the github master.
|
||||
2. You can use it together with llvm_mode: laf-intel and the instrument file listing
|
||||
features and can be combined with cmplog/Redqueen
|
||||
|
||||
3. It only works with llvm 11 (current github master state)
|
||||
3. It only works with llvm 11+
|
||||
|
||||
4. AUTODICTIONARY feature! see below
|
||||
|
||||
5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`.
|
||||
Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
|
||||
|
||||
6. If a target uses _init functions or early constructors then additionally
|
||||
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
|
||||
@ -61,9 +58,9 @@ 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).
|
||||
```
|
||||
|
||||
## Getting llvm 11
|
||||
## Getting llvm 11+
|
||||
|
||||
### Installing llvm 11 from the llvm repository
|
||||
### Installing llvm from the llvm repository (version 11)
|
||||
|
||||
Installing the llvm snapshot builds is easy and mostly painless:
|
||||
|
||||
@ -83,7 +80,7 @@ apt-get install -y clang-11 clang-tools-11 libc++1-11 libc++-11-dev \
|
||||
libomp5-11 lld-11 lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools
|
||||
```
|
||||
|
||||
### Building llvm 11 yourself
|
||||
### Building llvm yourself (version 12)
|
||||
|
||||
Building llvm from github takes quite some long time and is not painless:
|
||||
```
|
||||
@ -108,15 +105,12 @@ make install
|
||||
|
||||
Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc.
|
||||
|
||||
Also the instrument file listing (AFL_LLVM_INSTRUMENT_FILE -> [README.instrument_file.md](README.instrument_file.md)) and
|
||||
Also the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> [README.instrument_list.md](README.instrument_list.md)) and
|
||||
laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work.
|
||||
InsTrim (control flow graph instrumentation) is supported and recommended!
|
||||
(set `AFL_LLVM_INSTRUMENT=CFG`)
|
||||
|
||||
Example:
|
||||
```
|
||||
CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure
|
||||
export AFL_LLVM_INSTRUMENT=CFG
|
||||
make
|
||||
```
|
||||
|
||||
@ -125,21 +119,26 @@ NOTE: some targets also need to set the linker, try both `afl-clang-lto` and
|
||||
|
||||
## 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.
|
||||
While compiling, automatically a dictionary based on string comparisons is
|
||||
generated put into the target binary. This dictionary is transfered to afl-fuzz
|
||||
on start. This improves coverage statistically by 5-10% :)
|
||||
|
||||
## 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.
|
||||
To speed up fuzzing, it is possible to set a fixed shared memory map.
|
||||
Recommened is the value 0x10000.
|
||||
In most cases this will work without any problems. However if a target uses
|
||||
early constructors, ifuncs or a deferred forkserver this can crash the target.
|
||||
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).
|
||||
|
||||
## Document edge IDs
|
||||
|
||||
Setting `export AFL_LLVM_DOCUMENT_IDS=file` will document to a file which edge
|
||||
ID was given to which function. This helps to identify functions with variable
|
||||
bytes or which functions were touched by an input.
|
||||
|
||||
## Solving difficult targets
|
||||
|
||||
@ -147,6 +146,8 @@ Some targets are difficult because the configure script does unusual stuff that
|
||||
is unexpected for afl. See the next chapter `Potential issues` how to solve
|
||||
these.
|
||||
|
||||
### Example: ffmpeg
|
||||
|
||||
An example of a hard to solve target is ffmpeg. Here is how to successfully
|
||||
instrument it:
|
||||
|
||||
@ -156,7 +157,7 @@ instrument it:
|
||||
when compiling, so we have to trick configure:
|
||||
|
||||
```
|
||||
./configure --enable-lto --disable-shared
|
||||
./configure --enable-lto --disable-shared --disable-inline-asm
|
||||
```
|
||||
|
||||
3. Now the configuration is done - and we edit the settings in `./ffbuild/config.mak`
|
||||
@ -186,6 +187,31 @@ instrument it:
|
||||
|
||||
4. Then type make, wait for a long time and you are done :)
|
||||
|
||||
### Example: WebKit jsc
|
||||
|
||||
Building jsc is difficult as the build script has bugs.
|
||||
|
||||
1. checkout Webkit:
|
||||
```
|
||||
svn checkout https://svn.webkit.org/repository/webkit/trunk WebKit
|
||||
cd WebKit
|
||||
```
|
||||
|
||||
2. Fix the build environment:
|
||||
```
|
||||
mkdir -p WebKitBuild/Release
|
||||
cd WebKitBuild/Release
|
||||
ln -s ../../../../../usr/bin/llvm-ar-12 llvm-ar-12
|
||||
ln -s ../../../../../usr/bin/llvm-ranlib-12 llvm-ranlib-12
|
||||
cd ../..
|
||||
```
|
||||
|
||||
3. Build :)
|
||||
|
||||
```
|
||||
Tools/Scripts/build-jsc --jsc-only --cli --cmakeargs="-DCMAKE_AR='llvm-ar-12' -DCMAKE_RANLIB='llvm-ranlib-12' -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_CC_FLAGS='-O3 -lrt' -DCMAKE_CXX_FLAGS='-O3 -lrt' -DIMPORTED_LOCATION='/lib/x86_64-linux-gnu/' -DCMAKE_CC=afl-clang-lto -DCMAKE_CXX=afl-clang-lto++ -DENABLE_STATIC_JSC=ON"
|
||||
```
|
||||
|
||||
## Potential issues
|
||||
|
||||
### compiling libraries fails
|
||||
@ -220,28 +246,19 @@ AS=llvm-as ...
|
||||
afl-clang-lto is still work in progress.
|
||||
|
||||
Known issues:
|
||||
* Anything that llvm 11 cannot compile, afl-clang-lto can not compile either - obviously
|
||||
* 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
|
||||
Hence if building a target with afl-clang-lto fails try to build it with llvm12
|
||||
and LTO enabled (`CC=clang-12` `CXX=clang++-12` `CFLAGS=-flto=full` and
|
||||
`CXXFLAGS=-flto=full`).
|
||||
|
||||
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)
|
||||
|
||||
Even some targets where clang-11 fails can be build if the fail is just in
|
||||
Even some targets where clang-12 fails can be build if the fail is just in
|
||||
`./configure`, see `Solving difficult targets` above.
|
||||
|
||||
### 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.
|
||||
|
||||
## History
|
||||
|
||||
This was originally envisioned by hexcoder- in Summer 2019, however we saw no
|
||||
@ -270,7 +287,7 @@ 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.
|
||||
|
||||
This is all now fixed with llvm 11. The llvm's own linker is now able to
|
||||
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 :)
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
## 1) Introduction
|
||||
|
||||
! llvm_mode works with llvm versions 3.4 up to 11 !
|
||||
! llvm_mode works with llvm versions 3.4 up to 12 !
|
||||
|
||||
The code in this directory allows you to instrument programs for AFL using
|
||||
true compiler-level instrumentation, instead of the more crude
|
||||
@ -109,7 +109,7 @@ Several options are present to make llvm_mode faster or help it rearrange
|
||||
the code to make afl-fuzz path discovery easier.
|
||||
|
||||
If you need just to instrument specific parts of the code, you can the instrument file list
|
||||
which C/C++ files to actually instrument. See [README.instrument_file](README.instrument_file.md)
|
||||
which C/C++ files to actually instrument. See [README.instrument_list](README.instrument_list.md)
|
||||
|
||||
For splitting memcmp, strncmp, etc. please see [README.laf-intel](README.laf-intel.md)
|
||||
|
||||
@ -183,4 +183,4 @@ AFL_LLVM_INSTRUMENT=PCGUARD make
|
||||
```
|
||||
|
||||
Note that this us currently the default, as it is the best mode.
|
||||
If you have llvm 11 and compiled afl-clang-lto - this is the only better mode.
|
||||
If you have llvm 11+ and compiled afl-clang-lto - this is the only better mode.
|
||||
|
@ -52,6 +52,21 @@ afl-clang-fast -o fuzz_target fuzz_target.c -lwhat_you_need_for_your_target
|
||||
And that is it!
|
||||
The speed increase is usually x10 to x20.
|
||||
|
||||
If you want to be able to compile the target without afl-clang-fast/lto then
|
||||
add this just after the includes:
|
||||
|
||||
```
|
||||
#ifndef __AFL_FUZZ_TESTCASE_LEN
|
||||
ssize_t fuzz_len;
|
||||
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
|
||||
unsigned char fuzz_buf[1024000];
|
||||
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
|
||||
#define __AFL_FUZZ_INIT() void sync(void);
|
||||
#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ?
|
||||
#define __AFL_INIT() sync()
|
||||
#endif
|
||||
```
|
||||
|
||||
## 3) deferred initialization
|
||||
|
||||
AFL tries to optimize performance by executing the targeted binary just once,
|
||||
@ -100,6 +115,33 @@ will keep working normally when compiled with a tool other than afl-clang-fast.
|
||||
Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will
|
||||
*not* generate a deferred-initialization binary) - and you should be all set!
|
||||
|
||||
*NOTE:* In the code between `main` and `__AFL_INIT()` should not be any code
|
||||
run that is instrumented - otherwise a crash might occure.
|
||||
In case this is useful (e.g. for expensive one time initialization) you can
|
||||
try to do the following:
|
||||
|
||||
Add after the includes:
|
||||
```
|
||||
extern unsigned char *__afl_area_ptr;
|
||||
#define MAX_DUMMY_SIZE 256000
|
||||
|
||||
__attribute__((constructor(1))) void __afl_protect(void) {
|
||||
#ifdef MAP_FIXED_NOREPLACE
|
||||
__afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
if ((uint64_t)__afl_area_ptr == -1)
|
||||
#endif
|
||||
__afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
if ((uint64_t)__afl_area_ptr == -1)
|
||||
__afl_area_ptr = (unsigned char*) mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
}
|
||||
|
||||
```
|
||||
and just before `__AFL_INIT()`:
|
||||
```
|
||||
munmap(__afl_area_ptr, MAX_DUMMY_SIZE);
|
||||
__afl_area_ptr = NULL;
|
||||
```
|
||||
|
||||
## 4) persistent mode
|
||||
|
||||
Some libraries provide APIs that are stateless, or whose state can be reset in
|
||||
|
1503
llvm_mode/SanitizerCoverageLTO.so.cc
Normal file
1503
llvm_mode/SanitizerCoverageLTO.so.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -161,7 +161,9 @@ 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 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0,
|
||||
preprocessor_only = 0;
|
||||
u8 have_pic = 0;
|
||||
u8 *name;
|
||||
|
||||
cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
|
||||
@ -228,7 +230,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
if (lto_mode) {
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL ||
|
||||
getenv("AFL_LLVM_WHITELIST")) {
|
||||
getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") ||
|
||||
getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
@ -243,38 +246,60 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
// laf
|
||||
if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/split-switches-pass.so", obj_path);
|
||||
if (lto_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path);
|
||||
|
||||
} else {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/split-switches-pass.so", obj_path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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.");
|
||||
if (lto_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/compare-transform-pass.so", obj_path);
|
||||
cc_params[cc_par_cnt++] = alloc_printf(
|
||||
"-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path);
|
||||
|
||||
} else {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/compare-transform-pass.so", obj_path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES")) {
|
||||
if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") ||
|
||||
getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/split-compares-pass.so", obj_path);
|
||||
if (lto_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path);
|
||||
|
||||
} else {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/split-compares-pass.so", obj_path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -284,24 +309,37 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
unsetenv("AFL_LD_CALLER");
|
||||
if (cmplog_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/cmplog-routines-pass.so", obj_path);
|
||||
if (lto_mode) {
|
||||
|
||||
// reuse split switches from laf
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/split-switches-pass.so", obj_path);
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path);
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path);
|
||||
cc_params[cc_par_cnt++] = alloc_printf(
|
||||
"-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path);
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/cmplog-instructions-pass.so", obj_path);
|
||||
} else {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/cmplog-routines-pass.so", obj_path);
|
||||
|
||||
// reuse split switches from laf
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/split-switches-pass.so", obj_path);
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/cmplog-instructions-pass.so", obj_path);
|
||||
|
||||
}
|
||||
|
||||
cc_params[cc_par_cnt++] = "-fno-inline";
|
||||
|
||||
@ -309,12 +347,22 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
if (lto_mode) {
|
||||
|
||||
#if defined(AFL_CLANG_LDPATH) && LLVM_VERSION_MAJOR >= 12
|
||||
u8 *ld_ptr = strrchr(AFL_REAL_LD, '/');
|
||||
if (!ld_ptr) ld_ptr = "ld.lld";
|
||||
cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_ptr);
|
||||
cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", AFL_REAL_LD);
|
||||
#else
|
||||
cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD);
|
||||
#endif
|
||||
|
||||
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);
|
||||
alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.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;
|
||||
@ -323,8 +371,14 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
if (instrument_mode == INSTRUMENT_PCGUARD) {
|
||||
|
||||
#if LLVM_VERSION_MAJOR > 4 || \
|
||||
(LLVM_VERSION_MAJOR == 4 && \
|
||||
(LLVM_VERSION_MINOR > 0 || LLVM_VERSION_PATCH >= 1))
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
|
||||
#else
|
||||
FATAL("pcguard instrumentation requires llvm 4.0.1+");
|
||||
#endif
|
||||
|
||||
} else {
|
||||
|
||||
@ -359,6 +413,19 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
u32 idx;
|
||||
if (lto_mode && argc > 1) {
|
||||
|
||||
for (idx = 1; idx < argc; idx++) {
|
||||
|
||||
if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1;
|
||||
|
||||
}
|
||||
|
||||
if (!have_pic) cc_params[cc_par_cnt++] = "-fPIC";
|
||||
|
||||
}
|
||||
|
||||
/* Detect stray -v calls from ./configure scripts. */
|
||||
|
||||
while (--argc) {
|
||||
@ -379,6 +446,12 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined"))
|
||||
continue;
|
||||
|
||||
if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue;
|
||||
if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue;
|
||||
|
||||
if (!strcmp(cur, "-E")) preprocessor_only = 1;
|
||||
if (!strcmp(cur, "-shared")) shared_linking = 1;
|
||||
|
||||
cc_params[cc_par_cnt++] = cur;
|
||||
|
||||
}
|
||||
@ -452,9 +525,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
|
||||
getenv("LAF_TRANSFORM_COMPARES") ||
|
||||
(lto_mode && (getenv("AFL_LLVM_LTO_AUTODICTIONARY") ||
|
||||
getenv("AFL_LLVM_AUTODICTIONARY")))) {
|
||||
getenv("LAF_TRANSFORM_COMPARES") || lto_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
|
||||
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
|
||||
@ -500,13 +571,15 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
"int __afl_sharedmem_fuzzing = 1;"
|
||||
"extern unsigned int *__afl_fuzz_len;"
|
||||
"extern unsigned char *__afl_fuzz_ptr;"
|
||||
"unsigned char *__afl_fuzz_alt_ptr;";
|
||||
"unsigned char __afl_fuzz_alt[1024000];"
|
||||
"unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;";
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : "
|
||||
"(__afl_fuzz_alt_ptr = (unsigned char *) malloc(1 * 1024 * 1024)))";
|
||||
"__afl_fuzz_alt_ptr)";
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : read(0, "
|
||||
"__afl_fuzz_alt_ptr, 1 * 1024 * 1024))";
|
||||
"-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : "
|
||||
"(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1024000)) == 0xffffffff "
|
||||
"? 0 : *__afl_fuzz_len)";
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-D__AFL_LOOP(_A)="
|
||||
@ -543,6 +616,18 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (preprocessor_only) {
|
||||
|
||||
/* In the preprocessor_only case (-E), we are not actually compiling at
|
||||
all but requesting the compiler to output preprocessed sources only.
|
||||
We must not add the runtime in this case because the compiler will
|
||||
simply output its binary content back on stdout, breaking any build
|
||||
systems that rely on a separate source preprocessing step. */
|
||||
cc_params[cc_par_cnt] = NULL;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
switch (bit_mode) {
|
||||
|
||||
@ -585,6 +670,12 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
if (!shared_linking)
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
cc_params[cc_par_cnt] = NULL;
|
||||
@ -617,6 +708,14 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL ||
|
||||
getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") ||
|
||||
getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) &&
|
||||
getenv("AFL_DONT_OPTIMIZE"))
|
||||
WARNF(
|
||||
"AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined "
|
||||
"for file matching, only function matching!");
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") ||
|
||||
getenv("INSTRIM_LIB")) {
|
||||
|
||||
@ -651,7 +750,13 @@ int main(int argc, char **argv, char **envp) {
|
||||
if (strncasecmp(ptr, "afl", strlen("afl")) == 0 ||
|
||||
strncasecmp(ptr, "classic", strlen("classic")) == 0) {
|
||||
|
||||
if (!instrument_mode || instrument_mode == INSTRUMENT_AFL)
|
||||
if (instrument_mode == INSTRUMENT_LTO) {
|
||||
|
||||
instrument_mode = INSTRUMENT_CLASSIC;
|
||||
lto_mode = 1;
|
||||
|
||||
} else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL)
|
||||
|
||||
instrument_mode = INSTRUMENT_AFL;
|
||||
else
|
||||
FATAL("main instrumentation mode already set with %s",
|
||||
@ -660,7 +765,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr, "pc-guard", strlen("pc-guard")) == 0 ||
|
||||
strncasecmp(ptr, "pcguard", strlen("pcgard")) == 0) {
|
||||
strncasecmp(ptr, "pcguard", strlen("pcguard")) == 0) {
|
||||
|
||||
if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD)
|
||||
instrument_mode = INSTRUMENT_PCGUARD;
|
||||
@ -708,13 +813,25 @@ int main(int argc, char **argv, char **envp) {
|
||||
if (strncasecmp(ptr, "ngram", strlen("ngram")) == 0) {
|
||||
|
||||
ptr += strlen("ngram");
|
||||
while (*ptr && (*ptr < '0' || *ptr > '9'))
|
||||
while (*ptr && (*ptr < '0' || *ptr > '9')) {
|
||||
|
||||
ptr++;
|
||||
if (!*ptr)
|
||||
if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) != NULL)
|
||||
|
||||
}
|
||||
|
||||
if (!*ptr) {
|
||||
|
||||
ptr = getenv("AFL_LLVM_NGRAM_SIZE");
|
||||
if (!ptr || !*ptr) {
|
||||
|
||||
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(
|
||||
@ -743,11 +860,18 @@ int main(int argc, char **argv, char **envp) {
|
||||
callname = "afl-clang-lto";
|
||||
if (!instrument_mode) {
|
||||
|
||||
instrument_mode = INSTRUMENT_LTO;
|
||||
instrument_mode = INSTRUMENT_CFG;
|
||||
ptr = instrument_mode_string[instrument_mode];
|
||||
|
||||
}
|
||||
|
||||
} else if (instrument_mode == INSTRUMENT_LTO ||
|
||||
|
||||
instrument_mode == INSTRUMENT_CLASSIC) {
|
||||
|
||||
lto_mode = 1;
|
||||
callname = "afl-clang-lto";
|
||||
|
||||
} else {
|
||||
|
||||
if (!be_quiet)
|
||||
@ -763,9 +887,21 @@ int main(int argc, char **argv, char **envp) {
|
||||
#if LLVM_VERSION_MAJOR <= 6
|
||||
instrument_mode = INSTRUMENT_AFL;
|
||||
#else
|
||||
if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST"))
|
||||
if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL ||
|
||||
getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") ||
|
||||
getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) {
|
||||
|
||||
instrument_mode = INSTRUMENT_AFL;
|
||||
else
|
||||
WARNF(
|
||||
"switching to classic instrumentation because "
|
||||
"AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use "
|
||||
"-fsanitize-coverage-allowlist=allowlist.txt or "
|
||||
"-fsanitize-coverage-blocklist=denylist.txt if you want to use "
|
||||
"PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ "
|
||||
"SanitizerCoverage.html#partially-disabling-instrumentation");
|
||||
|
||||
} else
|
||||
|
||||
instrument_mode = INSTRUMENT_PCGUARD;
|
||||
#endif
|
||||
|
||||
@ -812,10 +948,17 @@ int main(int argc, char **argv, char **envp) {
|
||||
"together");
|
||||
|
||||
if (instrument_mode == INSTRUMENT_PCGUARD &&
|
||||
(getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")))
|
||||
WARNF(
|
||||
(getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL ||
|
||||
getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") ||
|
||||
getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")))
|
||||
FATAL(
|
||||
"Instrumentation type PCGUARD does not support "
|
||||
"AFL_LLVM_INSTRUMENT_FILE!");
|
||||
"AFL_LLVM_ALLOWLIST/DENYLIST! Use "
|
||||
"-fsanitize-coverage-allowlist=allowlist.txt or "
|
||||
"-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm "
|
||||
"12+), see "
|
||||
"https://clang.llvm.org/docs/"
|
||||
"SanitizerCoverage.html#partially-disabling-instrumentation");
|
||||
|
||||
if (argc < 2 || strcmp(argv[1], "-h") == 0) {
|
||||
|
||||
@ -854,8 +997,11 @@ int main(int argc, char **argv, char **envp) {
|
||||
"AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n"
|
||||
"AFL_HARDEN: adds code hardening to catch memory bugs\n"
|
||||
"AFL_INST_RATIO: percentage of branches to instrument\n"
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
"AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n"
|
||||
#else
|
||||
"AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n"
|
||||
#endif
|
||||
"AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n"
|
||||
"AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n"
|
||||
"AFL_LLVM_LAF_SPLIT_SWITCHES: casc. comp. in 'switch'\n"
|
||||
@ -865,12 +1011,13 @@ int main(int argc, char **argv, char **envp) {
|
||||
"AFL_LLVM_LAF_TRANSFORM_COMPARES: transform library comparison "
|
||||
"function calls\n"
|
||||
"AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n"
|
||||
"AFL_LLVM_INSTRUMENT_FILE: enable the instrument file listing "
|
||||
"(selective "
|
||||
"instrumentation)\n"
|
||||
"AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable instrument"
|
||||
"allow/deny listing (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_LLVM_DOCUMENT_IDS: document edge IDs given to which function (LTO "
|
||||
"only)\n"
|
||||
"AFL_QUIET: suppress verbose output\n"
|
||||
"AFL_USE_ASAN: activate address sanitizer\n"
|
||||
"AFL_USE_CFISAN: activate control flow sanitizer\n"
|
||||
@ -895,6 +1042,10 @@ int main(int argc, char **argv, char **envp) {
|
||||
#ifdef AFL_CLANG_FLTO
|
||||
SAYF(
|
||||
"\nafl-clang-lto specific environment variables:\n"
|
||||
"AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. "
|
||||
"0x10000\n"
|
||||
"AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding "
|
||||
"functions they are in into this file\n"
|
||||
"AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a "
|
||||
"global var\n"
|
||||
"AFL_LLVM_LTO_STARTID: from which ID to start counting from for a "
|
||||
@ -939,7 +1090,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
u32 map_size = atoi(ptr2);
|
||||
if (map_size != MAP_SIZE)
|
||||
FATAL("AFL_MAP_SIZE is not supported by afl-clang-fast");
|
||||
WARNF("AFL_MAP_SIZE is not supported by afl-clang-fast");
|
||||
|
||||
}
|
||||
|
||||
|
@ -278,7 +278,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (debug) {
|
||||
|
||||
(void)getcwd(thecwd, sizeof(thecwd));
|
||||
if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, ".");
|
||||
|
||||
SAYF(cMGN "[D] " cRST "cd \"%s\";", thecwd);
|
||||
for (i = 0; i < argc; i++)
|
||||
|
@ -14,11 +14,16 @@
|
||||
#include <fstream>
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
#define IS_EXTERN extern
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static std::list<std::string> myInstrumentList;
|
||||
static std::list<std::string> allowListFiles;
|
||||
static std::list<std::string> allowListFunctions;
|
||||
static std::list<std::string> denyListFiles;
|
||||
static std::list<std::string> denyListFunctions;
|
||||
|
||||
char *getBBName(const llvm::BasicBlock *BB) {
|
||||
|
||||
@ -55,15 +60,18 @@ bool isIgnoreFunction(const llvm::Function *F) {
|
||||
"asan.",
|
||||
"llvm.",
|
||||
"sancov.",
|
||||
"__ubsan_handle_",
|
||||
"__ubsan_",
|
||||
"ign.",
|
||||
"__afl_",
|
||||
"_fini",
|
||||
"__libc_csu",
|
||||
"__asan",
|
||||
"__msan",
|
||||
"__cmplog",
|
||||
"__sancov",
|
||||
"msan.",
|
||||
"LLVMFuzzer",
|
||||
"__decide_deferred",
|
||||
"maybe_duplicate_stderr",
|
||||
"discard_output",
|
||||
"close_stdout",
|
||||
@ -85,124 +93,230 @@ bool isIgnoreFunction(const llvm::Function *F) {
|
||||
|
||||
void initInstrumentList() {
|
||||
|
||||
char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE");
|
||||
if (!instrumentListFilename)
|
||||
instrumentListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instrumentListFilename) {
|
||||
char *allowlist = getenv("AFL_LLVM_ALLOWLIST");
|
||||
if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE");
|
||||
if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST");
|
||||
char *denylist = getenv("AFL_LLVM_DENYLIST");
|
||||
if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST");
|
||||
|
||||
if (allowlist && denylist)
|
||||
FATAL(
|
||||
"You can only specify either AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST "
|
||||
"but not both!");
|
||||
|
||||
if (allowlist) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instrumentListFilename);
|
||||
if (!fileStream)
|
||||
report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE");
|
||||
fileStream.open(allowlist);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_ALLOWLIST");
|
||||
getline(fileStream, line);
|
||||
|
||||
while (fileStream) {
|
||||
|
||||
myInstrumentList.push_back(line);
|
||||
getline(fileStream, line);
|
||||
int is_file = -1;
|
||||
std::size_t npos;
|
||||
std::string original_line = line;
|
||||
|
||||
line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
|
||||
line.end());
|
||||
|
||||
// remove # and following
|
||||
if ((npos = line.find("#")) != std::string::npos)
|
||||
line = line.substr(0, npos);
|
||||
|
||||
if (line.compare(0, 4, "fun:") == 0) {
|
||||
|
||||
is_file = 0;
|
||||
line = line.substr(4);
|
||||
|
||||
} else if (line.compare(0, 9, "function:") == 0) {
|
||||
|
||||
is_file = 0;
|
||||
line = line.substr(9);
|
||||
|
||||
} else if (line.compare(0, 4, "src:") == 0) {
|
||||
|
||||
is_file = 1;
|
||||
line = line.substr(4);
|
||||
|
||||
} else if (line.compare(0, 7, "source:") == 0) {
|
||||
|
||||
is_file = 1;
|
||||
line = line.substr(7);
|
||||
|
||||
}
|
||||
|
||||
if (line.find(":") != std::string::npos) {
|
||||
|
||||
FATAL("invalid line in AFL_LLVM_ALLOWLIST: %s", original_line.c_str());
|
||||
|
||||
}
|
||||
|
||||
if (line.length() > 0) {
|
||||
|
||||
// if the entry contains / or . it must be a file
|
||||
if (is_file == -1)
|
||||
if (line.find("/") != std::string::npos ||
|
||||
line.find(".") != std::string::npos)
|
||||
is_file = 1;
|
||||
// otherwise it is a function
|
||||
|
||||
if (is_file == 1)
|
||||
allowListFiles.push_back(line);
|
||||
else
|
||||
allowListFunctions.push_back(line);
|
||||
getline(fileStream, line);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"loaded allowlist with %zu file and %zu function entries\n",
|
||||
allowListFiles.size(), allowListFunctions.size());
|
||||
|
||||
}
|
||||
|
||||
if (denylist) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(denylist);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_DENYLIST");
|
||||
getline(fileStream, line);
|
||||
|
||||
while (fileStream) {
|
||||
|
||||
int is_file = -1;
|
||||
std::size_t npos;
|
||||
std::string original_line = line;
|
||||
|
||||
line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
|
||||
line.end());
|
||||
|
||||
// remove # and following
|
||||
if ((npos = line.find("#")) != std::string::npos)
|
||||
line = line.substr(0, npos);
|
||||
|
||||
if (line.compare(0, 4, "fun:") == 0) {
|
||||
|
||||
is_file = 0;
|
||||
line = line.substr(4);
|
||||
|
||||
} else if (line.compare(0, 9, "function:") == 0) {
|
||||
|
||||
is_file = 0;
|
||||
line = line.substr(9);
|
||||
|
||||
} else if (line.compare(0, 4, "src:") == 0) {
|
||||
|
||||
is_file = 1;
|
||||
line = line.substr(4);
|
||||
|
||||
} else if (line.compare(0, 7, "source:") == 0) {
|
||||
|
||||
is_file = 1;
|
||||
line = line.substr(7);
|
||||
|
||||
}
|
||||
|
||||
if (line.find(":") != std::string::npos) {
|
||||
|
||||
FATAL("invalid line in AFL_LLVM_DENYLIST: %s", original_line.c_str());
|
||||
|
||||
}
|
||||
|
||||
if (line.length() > 0) {
|
||||
|
||||
// if the entry contains / or . it must be a file
|
||||
if (is_file == -1)
|
||||
if (line.find("/") != std::string::npos ||
|
||||
line.find(".") != std::string::npos)
|
||||
is_file = 1;
|
||||
// otherwise it is a function
|
||||
|
||||
if (is_file == 1)
|
||||
denyListFiles.push_back(line);
|
||||
else
|
||||
denyListFunctions.push_back(line);
|
||||
getline(fileStream, line);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"loaded denylist with %zu file and %zu function entries\n",
|
||||
denyListFiles.size(), denyListFunctions.size());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool isInInstrumentList(llvm::Function *F) {
|
||||
void scanForDangerousFunctions(llvm::Module *M) {
|
||||
|
||||
// is this a function with code? If it is external we dont instrument it
|
||||
// anyway and cant be in the the instrument file list. Or if it is ignored.
|
||||
if (!F->size() || isIgnoreFunction(F)) return false;
|
||||
if (!M) return;
|
||||
|
||||
// if we do not have a the instrument file list return true
|
||||
if (myInstrumentList.empty()) return true;
|
||||
#if LLVM_VERSION_MAJOR > 3 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 9)
|
||||
|
||||
// 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();
|
||||
for (GlobalIFunc &IF : M->ifuncs()) {
|
||||
|
||||
#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 = myInstrumentList.begin();
|
||||
it != myInstrumentList.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. We also allow UNIX-style pattern
|
||||
* matching */
|
||||
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) ==
|
||||
0) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
StringRef ifunc_name = IF.getName();
|
||||
Constant *r = IF.getResolver();
|
||||
StringRef r_name = cast<Function>(r->getOperand(0))->getName();
|
||||
if (!be_quiet)
|
||||
fprintf(stderr,
|
||||
"Info: Found an ifunc with name %s that points to resolver "
|
||||
"function %s, we will not instrument this, putting it into the "
|
||||
"block list.\n",
|
||||
ifunc_name.str().c_str(), r_name.str().c_str());
|
||||
denyListFunctions.push_back(r_name.str());
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
GlobalVariable *GV = M->getNamedGlobal("llvm.global_ctors");
|
||||
if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(F->getContext()));
|
||||
ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());
|
||||
|
||||
unsigned int instLine = cDILoc.getLineNumber();
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
if (InitList) {
|
||||
|
||||
(void)instLine;
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myInstrumentList.begin();
|
||||
it != myInstrumentList.end(); ++it) {
|
||||
if (ConstantStruct *CS =
|
||||
dyn_cast<ConstantStruct>(InitList->getOperand(i))) {
|
||||
|
||||
/* 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. We also allow UNIX-style pattern
|
||||
* matching */
|
||||
if (CS->getNumOperands() >= 2) {
|
||||
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
if (CS->getOperand(1)->isNullValue())
|
||||
break; // Found a null terminator, stop here.
|
||||
|
||||
if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) ==
|
||||
0) {
|
||||
ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0));
|
||||
int Priority = CI ? CI->getSExtValue() : 0;
|
||||
|
||||
return true;
|
||||
Constant *FP = CS->getOperand(1);
|
||||
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(FP))
|
||||
if (CE->isCast()) FP = CE->getOperand(0);
|
||||
if (Function *F = dyn_cast<Function>(FP)) {
|
||||
|
||||
if (!F->isDeclaration() &&
|
||||
strncmp(F->getName().str().c_str(), "__afl", 5) != 0) {
|
||||
|
||||
if (!be_quiet)
|
||||
fprintf(stderr,
|
||||
"Info: Found constructor function %s with prio "
|
||||
"%u, we will not instrument this, putting it into a "
|
||||
"block list.\n",
|
||||
F->getName().str().c_str(), Priority);
|
||||
denyListFunctions.push_back(F->getName().str());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -215,17 +329,230 @@ bool isInInstrumentList(llvm::Function *F) {
|
||||
}
|
||||
|
||||
#endif
|
||||
else {
|
||||
|
||||
// we could not find out the location. in this case we say it is not
|
||||
// in the the instrument file list
|
||||
}
|
||||
|
||||
return false;
|
||||
static std::string getSourceName(llvm::Function *F) {
|
||||
|
||||
// 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) {
|
||||
|
||||
StringRef instFilename;
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
if (cDILoc) { 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(); }
|
||||
|
||||
}
|
||||
|
||||
return instFilename.str();
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
return false;
|
||||
#else
|
||||
if (!Loc.isUnknown()) {
|
||||
|
||||
DILocation cDILoc(Loc.getAsMDNode(F->getContext()));
|
||||
|
||||
StringRef instFilename = cDILoc.getFilename();
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
return instFilename.str();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return std::string("");
|
||||
|
||||
}
|
||||
|
||||
bool isInInstrumentList(llvm::Function *F) {
|
||||
|
||||
bool return_default = true;
|
||||
|
||||
// is this a function with code? If it is external we don't instrument it
|
||||
// anyway and it can't be in the instrument file list. Or if it is it is
|
||||
// ignored.
|
||||
if (!F->size() || isIgnoreFunction(F)) return false;
|
||||
|
||||
if (!denyListFiles.empty() || !denyListFunctions.empty()) {
|
||||
|
||||
if (!denyListFunctions.empty()) {
|
||||
|
||||
std::string instFunction = F->getName().str();
|
||||
|
||||
for (std::list<std::string>::iterator it = denyListFunctions.begin();
|
||||
it != denyListFunctions.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. We also allow UNIX-style pattern
|
||||
* matching */
|
||||
|
||||
if (instFunction.length() >= it->length()) {
|
||||
|
||||
if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) {
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"Function %s is in the deny function list, "
|
||||
"not instrumenting ... \n",
|
||||
instFunction.c_str());
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!denyListFiles.empty()) {
|
||||
|
||||
std::string source_file = getSourceName(F);
|
||||
|
||||
if (!source_file.empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = denyListFiles.begin();
|
||||
it != denyListFiles.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. We also allow UNIX-style pattern
|
||||
* matching */
|
||||
|
||||
if (source_file.length() >= it->length()) {
|
||||
|
||||
if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// we could not find out the location. in this case we say it is not
|
||||
// in the instrument file list
|
||||
if (!be_quiet)
|
||||
WARNF(
|
||||
"No debug information found for function %s, will be "
|
||||
"instrumented (recompile with -g -O[1-3]).",
|
||||
F->getName().str().c_str());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if we do not have a instrument file list return true
|
||||
if (!allowListFiles.empty() || !allowListFunctions.empty()) {
|
||||
|
||||
return_default = false;
|
||||
|
||||
if (!allowListFunctions.empty()) {
|
||||
|
||||
std::string instFunction = F->getName().str();
|
||||
|
||||
for (std::list<std::string>::iterator it = allowListFunctions.begin();
|
||||
it != allowListFunctions.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. We also allow UNIX-style pattern
|
||||
* matching */
|
||||
|
||||
if (instFunction.length() >= it->length()) {
|
||||
|
||||
if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) {
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"Function %s is in the allow function list, "
|
||||
"instrumenting ... \n",
|
||||
instFunction.c_str());
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!allowListFiles.empty()) {
|
||||
|
||||
std::string source_file = getSourceName(F);
|
||||
|
||||
if (!source_file.empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = allowListFiles.begin();
|
||||
it != allowListFiles.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. We also allow UNIX-style pattern
|
||||
* matching */
|
||||
|
||||
if (source_file.length() >= it->length()) {
|
||||
|
||||
if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) {
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"Function %s is in the allowlist (%s), "
|
||||
"instrumenting ... \n",
|
||||
F->getName().str().c_str(), source_file.c_str());
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// we could not find out the location. In this case we say it is not
|
||||
// in the instrument file list
|
||||
if (!be_quiet)
|
||||
WARNF(
|
||||
"No debug information found for function %s, will not be "
|
||||
"instrumented (recompile with -g -O[1-3]).",
|
||||
F->getName().str().c_str());
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return return_default;
|
||||
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,16 @@ bool isIgnoreFunction(const llvm::Function *F);
|
||||
void initInstrumentList();
|
||||
bool isInInstrumentList(llvm::Function *F);
|
||||
unsigned long long int calculateCollisions(uint32_t edges);
|
||||
void scanForDangerousFunctions(llvm::Module *M);
|
||||
|
||||
#ifndef IS_EXTERN
|
||||
#define IS_EXTERN
|
||||
#endif
|
||||
|
||||
IS_EXTERN int debug;
|
||||
IS_EXTERN int be_quiet;
|
||||
|
||||
#undef IS_EXTERN
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,955 +0,0 @@
|
||||
/*
|
||||
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;
|
||||
uint32_t locations = 0, functions = 0;
|
||||
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
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 (isIgnoreFunction(&F)) continue;
|
||||
|
||||
functions++;
|
||||
|
||||
// the instrument file list check
|
||||
AttributeList Attrs = F.getAttributes();
|
||||
if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) {
|
||||
|
||||
if (debug)
|
||||
fprintf(stderr,
|
||||
"DEBUG: Function %s is not the instrument file listed\n",
|
||||
F.getName().str().c_str());
|
||||
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++);
|
||||
locations++;
|
||||
|
||||
} 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);
|
||||
locations++;
|
||||
|
||||
}
|
||||
|
||||
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) would_instrument = 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 for %u edges in %u functions (%llu, "
|
||||
"%llu) with no collisions (on "
|
||||
"average %llu collisions would be in afl-gcc/afl-clang-fast for %u "
|
||||
"edges) (%s mode).",
|
||||
inst_blocks, locations, functions, 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);
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "llvm/Analysis/MemorySSAUpdater.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
|
||||
#include "afl-llvm-common.h"
|
||||
|
||||
@ -86,9 +87,9 @@ class AFLLTOPass : public ModulePass {
|
||||
bool runOnModule(Module &M) override;
|
||||
|
||||
protected:
|
||||
int afl_global_id = 1, debug = 0, autodictionary = 0;
|
||||
int afl_global_id = 1, autodictionary = 1;
|
||||
uint32_t function_minimum_size = 1;
|
||||
uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0;
|
||||
uint32_t inst_blocks = 0, inst_funcs = 0, total_instr = 0;
|
||||
uint64_t map_addr = 0x10000;
|
||||
char * skip_nozero = NULL;
|
||||
|
||||
@ -102,7 +103,14 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
std::vector<std::string> dictionary;
|
||||
std::vector<CallInst *> calls;
|
||||
DenseMap<Value *, std::string *> valueMap;
|
||||
std::vector<BasicBlock *> BlockList;
|
||||
char * ptr;
|
||||
FILE * documentFile = NULL;
|
||||
|
||||
srand((unsigned int)time(NULL));
|
||||
|
||||
unsigned long long int moduleID =
|
||||
(((unsigned long long int)(rand() & 0xffffffff)) << 32) | getpid();
|
||||
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
@ -120,15 +128,17 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
if (getenv("AFL_LLVM_AUTODICTIONARY") ||
|
||||
getenv("AFL_LLVM_LTO_AUTODICTIONARY"))
|
||||
autodictionary = 1;
|
||||
if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) {
|
||||
|
||||
if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0;
|
||||
if ((documentFile = fopen(ptr, "a")) == NULL)
|
||||
WARNF("Cannot access document file %s", ptr);
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") ||
|
||||
getenv("AFL_LLVM_SKIPSINGLEBLOCK"))
|
||||
function_minimum_size = 2;
|
||||
}
|
||||
|
||||
// we make this the default as the fixed map has problems with
|
||||
// defered forkserver, early constructors, ifuncs and maybe more
|
||||
/*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/
|
||||
map_addr = 0;
|
||||
|
||||
if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) {
|
||||
|
||||
@ -137,7 +147,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
map_addr = 0;
|
||||
|
||||
} else if (map_addr == 0) {
|
||||
} else if (getenv("AFL_LLVM_MAP_DYNAMIC")) {
|
||||
|
||||
FATAL(
|
||||
"AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used together");
|
||||
@ -187,13 +197,39 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
|
||||
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
|
||||
|
||||
// This dumps all inialized global strings - might be useful in the future
|
||||
/*
|
||||
for (auto G=M.getGlobalList().begin(); G!=M.getGlobalList().end(); G++) {
|
||||
|
||||
GlobalVariable &GV=*G;
|
||||
if (!GV.getName().str().empty()) {
|
||||
|
||||
fprintf(stderr, "Global Variable: %s", GV.getName().str().c_str());
|
||||
if (GV.hasInitializer())
|
||||
if (auto *Val = dyn_cast<ConstantDataArray>(GV.getInitializer()))
|
||||
fprintf(stderr, " Value: \"%s\"", Val->getAsString().str().c_str());
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
scanForDangerousFunctions(&M);
|
||||
|
||||
/* Instrument all the things! */
|
||||
|
||||
int inst_blocks = 0;
|
||||
|
||||
for (auto &F : M) {
|
||||
|
||||
// fprintf(stderr, "DEBUG: Function %s\n", F.getName().str().c_str());
|
||||
/*For debugging
|
||||
AttributeSet X = F.getAttributes().getFnAttributes();
|
||||
fprintf(stderr, "DEBUG: Module %s Function %s attributes %u\n",
|
||||
M.getName().str().c_str(), F.getName().str().c_str(),
|
||||
X.getNumAttributes());
|
||||
*/
|
||||
|
||||
if (F.size() < function_minimum_size) continue;
|
||||
if (isIgnoreFunction(&F)) continue;
|
||||
@ -204,7 +240,8 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
if (debug)
|
||||
fprintf(stderr,
|
||||
"DEBUG: Function %s is not the instrument file listed\n",
|
||||
"DEBUG: Function %s is not in a source file that was specified "
|
||||
"in the instrument file list\n",
|
||||
F.getName().str().c_str());
|
||||
continue;
|
||||
|
||||
@ -250,14 +287,14 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
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;
|
||||
bool isStrcmp = true;
|
||||
bool isMemcmp = true;
|
||||
bool isStrncmp = true;
|
||||
bool isStrcasecmp = true;
|
||||
bool isStrncasecmp = true;
|
||||
bool isIntMemcpy = true;
|
||||
bool addedNull = false;
|
||||
size_t optLen = 0;
|
||||
|
||||
Function *Callee = callInst->getCalledFunction();
|
||||
if (!Callee) continue;
|
||||
@ -270,6 +307,24 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
isStrncasecmp &= !FuncName.compare("strncasecmp");
|
||||
isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64");
|
||||
|
||||
/* we do something different here, putting this BB and the
|
||||
successors in a block map */
|
||||
if (!FuncName.compare("__afl_persistent_loop")) {
|
||||
|
||||
BlockList.push_back(&BB);
|
||||
/*
|
||||
for (succ_iterator SI = succ_begin(&BB), SE =
|
||||
succ_end(&BB); SI != SE; ++SI) {
|
||||
|
||||
BasicBlock *succ = *SI;
|
||||
BlockList.push_back(succ);
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||
!isStrncasecmp && !isIntMemcpy)
|
||||
continue;
|
||||
@ -319,16 +374,29 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
std::string Str1, Str2;
|
||||
StringRef TmpStr;
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, TmpStr);
|
||||
if (TmpStr.empty())
|
||||
if (TmpStr.empty()) {
|
||||
|
||||
HasStr1 = false;
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = TmpStr.str();
|
||||
|
||||
}
|
||||
|
||||
bool HasStr2 = getConstantStringInfo(Str2P, TmpStr);
|
||||
if (TmpStr.empty())
|
||||
if (TmpStr.empty()) {
|
||||
|
||||
HasStr2 = false;
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
HasStr2 = true;
|
||||
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(),
|
||||
@ -487,18 +555,27 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
// add null byte if this is a string compare function and a null
|
||||
// was not already added
|
||||
if (addedNull == false && !isMemcmp) {
|
||||
if (!isMemcmp) {
|
||||
|
||||
thestring.append("\0", 1); // add null byte
|
||||
optLen++;
|
||||
if (addedNull == false) {
|
||||
|
||||
thestring.append("\0", 1); // add null byte
|
||||
optLen++;
|
||||
|
||||
}
|
||||
|
||||
// ensure we do not have garbage
|
||||
size_t offset = thestring.find('\0', 0);
|
||||
if (offset + 1 < optLen) optLen = offset + 1;
|
||||
thestring = thestring.substr(0, optLen);
|
||||
|
||||
}
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
std::string outstring;
|
||||
fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen,
|
||||
(unsigned int)thestring.length());
|
||||
fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen,
|
||||
thestring.length());
|
||||
for (uint8_t i = 0; i < thestring.length(); i++) {
|
||||
|
||||
uint8_t c = thestring[i];
|
||||
@ -534,15 +611,41 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
uint32_t succ = 0;
|
||||
if (F.size() == 1) {
|
||||
|
||||
InsBlocks.push_back(&BB);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
uint32_t succ = 0;
|
||||
for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE;
|
||||
++SI)
|
||||
if ((*SI)->size() > 0) succ++;
|
||||
|
||||
if (succ < 2) // no need to instrument
|
||||
continue;
|
||||
|
||||
if (BlockList.size()) {
|
||||
|
||||
int skip = 0;
|
||||
for (uint32_t k = 0; k < BlockList.size(); k++) {
|
||||
|
||||
if (&BB == BlockList[k]) {
|
||||
|
||||
if (debug)
|
||||
fprintf(stderr,
|
||||
"DEBUG: Function %s skipping BB with/after __afl_loop\n",
|
||||
F.getName().str().c_str());
|
||||
skip = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (skip) continue;
|
||||
|
||||
}
|
||||
|
||||
InsBlocks.push_back(&BB);
|
||||
|
||||
}
|
||||
@ -554,9 +657,12 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
do {
|
||||
|
||||
--i;
|
||||
BasicBlock * newBB = NULL;
|
||||
BasicBlock * origBB = &(*InsBlocks[i]);
|
||||
std::vector<BasicBlock *> Successors;
|
||||
Instruction * TI = origBB->getTerminator();
|
||||
uint32_t fs = origBB->getParent()->size();
|
||||
uint32_t countto;
|
||||
|
||||
for (succ_iterator SI = succ_begin(origBB), SE = succ_end(origBB);
|
||||
SI != SE; ++SI) {
|
||||
@ -566,15 +672,25 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
if (TI == NULL || TI->getNumSuccessors() < 2) continue;
|
||||
if (fs == 1) {
|
||||
|
||||
newBB = origBB;
|
||||
countto = 1;
|
||||
|
||||
} else {
|
||||
|
||||
if (TI == NULL || TI->getNumSuccessors() < 2) continue;
|
||||
countto = Successors.size();
|
||||
|
||||
}
|
||||
|
||||
// if (Successors.size() != TI->getNumSuccessors())
|
||||
// FATAL("Different successor numbers %lu <-> %u\n", Successors.size(),
|
||||
// TI->getNumSuccessors());
|
||||
|
||||
for (uint32_t j = 0; j < Successors.size(); j++) {
|
||||
for (uint32_t j = 0; j < countto; j++) {
|
||||
|
||||
BasicBlock *newBB = llvm::SplitEdge(origBB, Successors[j]);
|
||||
if (fs != 1) newBB = llvm::SplitEdge(origBB, Successors[j]);
|
||||
|
||||
if (!newBB) {
|
||||
|
||||
@ -583,6 +699,13 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
if (documentFile) {
|
||||
|
||||
fprintf(documentFile, "ModuleID=%llu Function=%s edgeID=%u\n",
|
||||
moduleID, F.getName().str().c_str(), afl_global_id);
|
||||
|
||||
}
|
||||
|
||||
BasicBlock::iterator IP = newBB->getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
|
||||
@ -615,7 +738,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
Value *Incr = IRB.CreateAdd(Counter, One);
|
||||
|
||||
if (skip_nozero) {
|
||||
if (skip_nozero == NULL) {
|
||||
|
||||
auto cf = IRB.CreateICmpEQ(Incr, Zero);
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
@ -638,6 +761,9 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
if (documentFile) fclose(documentFile);
|
||||
documentFile = NULL;
|
||||
|
||||
// save highest location ID to global variable
|
||||
// do this after each function to fail faster
|
||||
if (!be_quiet && afl_global_id > MAP_SIZE &&
|
||||
|
@ -59,39 +59,9 @@ class AFLcheckIfInstrument : public ModulePass {
|
||||
static char ID;
|
||||
AFLcheckIfInstrument() : ModulePass(ID) {
|
||||
|
||||
int entries = 0;
|
||||
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
|
||||
char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE");
|
||||
if (!instrumentListFilename)
|
||||
instrumentListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instrumentListFilename) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instrumentListFilename);
|
||||
if (!fileStream)
|
||||
report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
|
||||
myInstrumentList.push_back(line);
|
||||
getline(fileStream, line);
|
||||
entries++;
|
||||
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
PFATAL(
|
||||
"afl-llvm-lto-instrumentlist.so loaded without "
|
||||
"AFL_LLVM_INSTRUMENT_FILE?!");
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"loaded the instrument file list %s with %d entries\n",
|
||||
instrumentListFilename, entries);
|
||||
initInstrumentList();
|
||||
|
||||
}
|
||||
|
||||
@ -104,7 +74,6 @@ class AFLcheckIfInstrument : public ModulePass {
|
||||
|
||||
protected:
|
||||
std::list<std::string> myInstrumentList;
|
||||
int debug = 0;
|
||||
|
||||
};
|
||||
|
||||
@ -116,7 +85,6 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) {
|
||||
|
||||
/* Show a banner */
|
||||
|
||||
char be_quiet = 0;
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
|
||||
@ -131,102 +99,28 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) {
|
||||
for (auto &F : M) {
|
||||
|
||||
if (F.size() < 1) continue;
|
||||
|
||||
// fprintf(stderr, "F:%s\n", F.getName().str().c_str());
|
||||
if (isIgnoreFunction(&F)) continue;
|
||||
|
||||
BasicBlock::iterator IP = F.getEntryBlock().getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
if (isInInstrumentList(&F)) {
|
||||
|
||||
if (!myInstrumentList.empty()) {
|
||||
|
||||
bool instrumentFunction = false;
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void)instLine;
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST "function %s is in file %s\n",
|
||||
F.getName().str().c_str(), instFilename.str().c_str());
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myInstrumentList.begin();
|
||||
it != myInstrumentList.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 (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) ==
|
||||
0) {
|
||||
|
||||
instrumentFunction = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not the instrument file listed, so we skip instrumentation.
|
||||
* We do this by renaming the function. */
|
||||
if (instrumentFunction == true) {
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n",
|
||||
F.getName().str().c_str());
|
||||
|
||||
} else {
|
||||
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"function %s is NOT in the instrument file list\n",
|
||||
F.getName().str().c_str());
|
||||
|
||||
auto & Ctx = F.getContext();
|
||||
AttributeList Attrs = F.getAttributes();
|
||||
AttrBuilder NewAttrs;
|
||||
NewAttrs.addAttribute("skipinstrument");
|
||||
F.setAttributes(
|
||||
Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs));
|
||||
|
||||
}
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n",
|
||||
F.getName().str().c_str());
|
||||
|
||||
} else {
|
||||
|
||||
PFATAL("InstrumentList is empty");
|
||||
if (debug)
|
||||
SAYF(cMGN "[D] " cRST
|
||||
"function %s is NOT in the instrument file list\n",
|
||||
F.getName().str().c_str());
|
||||
|
||||
auto & Ctx = F.getContext();
|
||||
AttributeList Attrs = F.getAttributes();
|
||||
AttrBuilder NewAttrs;
|
||||
NewAttrs.addAttribute("skipinstrument");
|
||||
F.setAttributes(
|
||||
Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs));
|
||||
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user