Compare commits

...

886 Commits
2.66c ... 3.0c

Author SHA1 Message Date
8e712d1a74 Merge pull request #628 from AFLplusplus/dev
Final push to stable
2020-12-15 09:39:10 +01:00
149ec41e90 v3.00c 2020-12-15 09:38:26 +01:00
e0ab846f7f v3.00c 2020-12-15 09:37:52 +01:00
df7c72fd66 code format 2020-12-13 11:24:49 +01:00
76117b0471 fix afl-clang tests 2020-12-13 11:23:56 +01:00
ce9e127529 remove debugging sh 2020-12-12 22:56:09 +01:00
c5c852dada add tests for the missing afl-compiler (afl-gcc or afl-clang) 2020-12-12 22:54:34 +01:00
7382cf5f00 afl-as.c, fix compiler warnings (overflowing is UB) 2020-12-12 19:30:56 +01:00
109560e73a tmpdir not needed with sharedmap fuzzing 2020-12-12 17:53:42 +01:00
befb1a2f39 remove stray debugging fprintf 2020-12-12 16:40:13 +01:00
fd30a4184a typo 2020-12-12 16:37:23 +01:00
e4a113b953 small fix in error handling 2020-12-12 13:26:25 +01:00
7f50aa26db let the check for afl-gcc not abort 'make tests' 2020-12-12 11:25:10 +01:00
54f59c7403 add an extra test for afl-gcc and check for -B support 2020-12-12 11:21:13 +01:00
dbbbeea36e typos 2020-12-11 18:24:14 +01:00
1825e5f313 typo 2020-12-11 17:01:46 +01:00
609f3d0265 fixed gcc analyzer warnings 2020-12-11 13:29:45 +01:00
12d62d5393 Merge pull request #617 from AFLplusplus/dev
push to stable
2020-12-11 11:38:22 +01:00
d5ded820e5 Merge branch 'stable' into dev 2020-12-11 11:36:35 +01:00
cc781e44f3 code format 2020-12-11 11:21:28 +01:00
2bf68a0bf4 fix MMAP 2020-12-11 11:19:26 +01:00
8a1acac559 schedule improvements, new default is FAST 2020-12-11 10:28:39 +01:00
1a3c9bc36d Merge pull request #627 from AFLplusplus/other_weight
Other weight
2020-12-11 10:23:42 +01:00
161c80014e change search order for afl-showmin in afl-cmin* 2020-12-10 17:25:22 +01:00
af403e5872 install more llvm 2020-12-10 15:58:00 +01:00
a686c1361c fix afl-cc library search 2020-12-10 15:31:26 +01:00
b8a590b84d grammar in error messages 2020-12-10 11:56:41 +01:00
2c24cdbfc3 fix qbdi path for code format 2020-12-09 22:25:37 +01:00
e5c50037d5 fix oversight for accessing nfuzz 2020-12-09 12:03:11 +01:00
a8e568f248 move libdislocator, libtokencap and qbdi_mode to utils/ 2020-12-09 11:30:04 +01:00
39a4fac941 better examples 2020-12-09 11:07:14 +01:00
3997d06cbd fix DUSEMMAP 2020-12-09 10:53:31 +01:00
2641082a76 Fix -DUSEMMAP 2020-12-09 10:51:20 +01:00
cebbedd238 fix cmin/tmin potential overflow on too large files 2020-12-09 08:58:07 +01:00
d1eb4eeb7f weighting for explore and exploit 2020-12-09 08:34:54 +01:00
a7125c68eb optimize a bit 2020-12-08 23:51:22 +01:00
1dbb1c7193 Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-12-08 23:27:14 +01:00
9534bb87b1 fix some copmpiler warnings for USEMMAP 2020-12-08 23:26:28 +01:00
4e96447b43 fix showmap output 2020-12-08 23:26:08 +01:00
ad29eef271 Merge branch 'dev' of github.com:AFLplusplus/AFLplusplus into dev 2020-12-08 22:43:05 +01:00
c70b7ffd80 fix memory limit issue with cmplog without -m flag 2020-12-08 22:42:50 +01:00
6fb74342b8 add TEST_MMAP 2020-12-08 22:32:51 +01:00
2b543a64af small fixes 2020-12-08 22:12:05 +01:00
5bb51688e4 small fixes 2020-12-08 21:54:18 +01:00
6e61b2345c more reporting on errors 2020-12-08 20:33:41 +01:00
cf0c49dec5 typos/wording 2020-12-08 17:46:29 +01:00
064e63962f make unicornafl checkout version work again 2020-12-08 12:37:40 +01:00
6d2ac3e314 fix grammar download script 2020-12-08 12:36:19 +01:00
8999023432 fixes for grammar scripts 2020-12-08 12:29:47 +01:00
da6cddab90 fix asserts 2020-12-08 11:30:05 +01:00
46156957bd fix aflfast changes 2020-12-08 11:07:11 +01:00
2f4166d5d6 Merge pull request #624 from mboehme/dev
Sampling next seed by weight (hit_count, bitmap_size, exec_us)
2020-12-08 10:51:45 +01:00
e707a8d738 fix warning 2020-12-08 10:15:09 +01:00
eda068751e streamlined grammar mutator submodule 2020-12-08 10:09:35 +01:00
5d6b1129f0 Fix Grammar Mutator Submodule (#623)
Fixes: 651ad18e21 ("added the grammar mutator as a git submodule ...")

* Project pointer never pushed
* Reduces dirctory complexity
* Building and dependencies for the subproject should be isolated to the
  subproject's documentation
* Fix broken link to README
* Use `--init` for `git submodule`
2020-12-08 09:30:17 +01:00
06ec5ab3d7 Sampling next seed by weight (hit_count, bitmap_size, exec_us) 2020-12-07 21:32:25 +00:00
e6de85861c fixes and code format 2020-12-07 14:36:04 +01:00
4c2e375e22 little fixes 2020-12-07 14:29:59 +01:00
8f79116a15 fix find_object proc search (#elsif -> #elif), optimize static if away 2020-12-05 09:48:55 +01:00
f8c33f29e8 Typos 2020-12-05 09:19:14 +01:00
9e8e25729a Merge pull request #620 from ThomasTNO/add_missing_qemu_env_var
Add missing env var used in https://github.com/qemu/qemu/blob/bec7edf…
2020-12-04 18:08:58 +01:00
9a7d045897 build_unicorn_support no longer fetches qemuafl 2020-12-04 17:59:11 +01:00
d59d1fcd9f Add missing env var used in bec7edf41d/accel/tcg/cpu-exec.c (L389) and ./qemu_mode/README.persistent.md 2020-12-04 17:15:25 +01:00
1aebbd7490 Merge branch 'dev' of github.com:aflplusplus/aflplusplus into dev 2020-12-04 15:41:24 +01:00
330f33a435 updated helper_scripts from battelle/afl-unicorn 2020-12-04 15:40:38 +01:00
3d233b34b8 Restore contribution list (#619) 2020-12-04 15:10:07 +01:00
eb85ded6ee Merge branch 'custom_describe' into dev 2020-12-04 14:52:11 +01:00
c18ca63519 unicorn updated 2020-12-04 14:51:31 +01:00
1dbefc14ea fixed bugs in custom_describe, reported by wizche 2020-12-04 14:26:48 +01:00
1f34b9f8e1 added python mutator, documentation 2020-12-04 14:26:48 +01:00
a19b3022d9 afl_custom_describe api added 2020-12-04 14:26:47 +01:00
aca5b55b6d test-pre.sh revert removal of afl-clang 2020-12-04 08:46:46 +01:00
e9a342f3d9 common.h change from user header include to system header include 2020-12-04 08:43:58 +01:00
b31d5a7cef afl-cmin usage fix 2020-12-04 07:32:56 +01:00
f0e81b2301 updated unicorn 2020-12-03 22:26:28 +01:00
295ddaf96b fix for afl-cc 2020-12-03 15:19:10 +01:00
a2e2fae840 AFL_CRASH_EXITCODE env var added, u8->bool 2020-12-03 14:43:06 +01:00
0f803c63df move debug print 2020-12-02 15:08:08 +01:00
1890d7b9cf very complete runtime lookup rewrite 2020-12-02 15:03:21 +01:00
16a6bbb3c9 typo 2020-12-02 13:26:38 +01:00
0942158ad1 remove docs/README symlink and update references 2020-12-01 23:17:20 +01:00
c05e4efbe9 renamed examples/ to utils/ 2020-12-01 14:40:30 +01:00
8584f9d2b5 added AFL_NO_AUTODICT 2020-12-01 13:13:11 +01:00
f7d8643dc4 update clang-format to 11 2020-11-30 22:08:26 +01:00
e769102491 more DEBUGF 2020-11-30 21:54:18 +01:00
1b75cc9f74 add DEBUGF 2020-11-30 21:38:15 +01:00
403b8a1086 update persistent doc 2020-11-30 21:13:16 +01:00
e865f274f1 fix wrong rename in test 2020-11-30 13:36:27 +01:00
856968c13b persistent_demo -> persistent_mode 2020-11-30 13:03:51 +01:00
63c317218b persistent_demo -> persistent_mode 2020-11-30 13:03:33 +01:00
aff4ccb0b2 more explanation 2020-11-30 12:56:47 +01:00
e83426a79b fix make DEBUG=1 2020-11-28 19:09:13 +01:00
fdac887660 no fancy special options for the fundamental test compile (no unnecessary dependencies) 2020-11-27 21:10:55 +01:00
b792c59080 remove wrong quoting 2020-11-27 21:02:27 +01:00
d1259d0914 add -lm, afl-fuzz-queue.c wants log2(), fix GNUmakefile syntax 2020-11-27 20:54:07 +01:00
4d9eb9bda1 Merge pull request #607 from AFLplusplus/dev
push to stable
2020-11-18 21:25:35 +01:00
cd0a25be5e Use buffer protocol to retrieve result from python post_process (#605)
Saves an extra copy, gives post processing functions more flexibility
2020-11-18 20:29:17 +01:00
b260204b72 Solaris/Illumos build fix. (#609) 2020-11-18 19:13:03 +01:00
211a6eb411 typos and wording 2020-11-18 08:40:12 +01:00
108a89b559 typo 2020-11-18 08:33:06 +01:00
68e18dbb7a more info about child_debug 2020-11-18 03:09:50 +01:00
631b6d0187 fixed simple yet stupid bug 2020-11-18 03:03:49 +01:00
f80f62f14b renamed env var to AFL_DEBUG_CHILD 2020-11-18 03:02:13 +01:00
57f8aec381 brought back missing env vars 2020-11-18 02:41:35 +01:00
23f37ff505 fixed alloc errors, code format 2020-11-18 02:33:47 +01:00
54fdec0e51 fix: avoid preprocessor logic in macro arguments (not portable) 2020-11-17 21:07:29 +01:00
add108ec23 fix two exotic mem leaks detected by cppcheck 2020-11-17 21:06:47 +01:00
d042a63ab4 micro optimization: allocate only when needed 2020-11-17 20:09:52 +01:00
c06b5a1564 fix sync issue 2020-11-17 17:02:33 +01:00
99c783821f Merge pull request #601 from Rumata888/fixing_symcc
Fixing symcc custom mutator
2020-11-17 10:13:57 +01:00
9d22c8a02c Fixed fd leak on early exit and closed pipes before early exits and PFATAL 2020-11-17 12:00:06 +03:00
1cc637a0a0 support AFL_LLVM_INSTRUMENT env for our own PCGUARD 2020-11-16 10:59:09 +01:00
7000f2a2cf Merge pull request #604 from virtuald/optional-py-fuzz
Actually make python 'fuzz' method optional
2020-11-15 12:37:47 +01:00
ea689076b3 Actually make python 'fuzz' method optional
At some point mutator->afl_custom_fuzz was allowed to be NULL, so do that instead of crashing
2020-11-14 19:58:32 -05:00
3ac953ec33 typo 2020-11-14 20:09:33 +01:00
76c5b8a3b4 fix error handling in fauxserver 2020-11-14 19:38:06 +01:00
40e10895a2 now really fix -n 2020-11-14 17:21:43 +01:00
e750a5c856 add sanity check for -M/-S arguments 2020-11-14 12:36:28 +01:00
30cd8a8397 fix non instrumented mode, fix check_binary 2020-11-14 12:28:51 +01:00
bd313d4039 no binary checking in noninstrumented mode 2020-11-14 11:31:18 +01:00
81493e4fe1 Merge pull request #603 from musicinmybrain/dev
Minor typo and phrasing fixes in README.md and CONTRIBUTING.md
2020-11-14 09:13:56 +01:00
389ee822e8 More idiomatic phrasing in CONTRIBUTING.md 2020-11-13 19:04:54 -05:00
fb38de8d73 In README.md, change “in the website” to “on the website,” which is more idiomatic. 2020-11-13 19:01:29 -05:00
155c2767a0 fix you/your typo in README.md 2020-11-13 18:58:50 -05:00
622f942555 Fixed memleaks, change exit to PFATAL 2020-11-13 14:54:36 +03:00
7db5d87a32 restore qemuafl submodule 2020-11-13 10:08:13 +01:00
79ed86da3f updated README to point out that if several laf-intel instances are used, -M main should be one of them to be able to share the intermediate results 2020-11-13 10:05:00 +01:00
119d7d0025 fix frida example debug output 2020-11-13 09:02:44 +01:00
54a312a5fe more small fixes 2020-11-13 00:44:08 +01:00
4a4c14c9a9 more fixes, leaks and compile errors 2020-11-12 23:47:49 +01:00
35fd6847fe another two mem leaks 2020-11-12 21:51:44 +01:00
ffe41e6fce fix two mem leaks detected by cppcheck 2020-11-12 20:26:53 +01:00
c05c5b787b Fixed name collision problem 2020-11-12 02:36:08 +03:00
bb218b330f Formatted changed/added files 2020-11-12 01:29:17 +03:00
20a8a93fd1 Fixed symcc custom mutator 2020-11-12 01:13:57 +03:00
14e76cf3c9 update qemuafl 2020-11-11 14:03:44 +01:00
5357ae5f91 remove duplicate include 2020-11-10 19:20:13 +01:00
a3928e5a62 small fix to actually document custom mutator introspection 2020-11-10 14:20:41 +01:00
1dfd7df7c0 small fix to actually document custom mutator introspection 2020-11-10 14:16:36 +01:00
8e1047f5ef support custom mutator introspection 2020-11-10 14:08:21 +01:00
1661303248 fix libfuzzer custom mutator and add introspection function 2020-11-10 13:43:48 +01:00
4c59c5234a Merge pull request #598 from AFLplusplus/dev
fix afl-cc bug for llvm 10.0.0
2020-11-08 17:11:57 +01:00
82d1c3e18d fix wrong llvm version comparison 2020-11-08 17:08:30 +01:00
585ba4c1dd fix for llvm 10.0.0 2020-11-08 16:33:25 +01:00
a728e8f9a5 better scriptable output from introspection 2020-11-06 16:42:02 +01:00
3b799c09cd Merge pull request #594 from AFLplusplus/dev
push to stable
2020-11-06 09:37:14 +01:00
50c98445fe do not try to compile pcguard if llvm is not current enough 2020-11-06 09:15:18 +01:00
665802673d typo fixed 2020-11-05 22:58:55 +01:00
f8bbbf31ec remove duplicate headers, make '#include VirtualFileSystems.h' version dependent 2020-11-05 22:45:07 +01:00
61fe059ba2 ignore build error (unsupported platform), so top level 'make tests' can be run 2020-11-05 21:11:31 +01:00
6abe4d124e require llvm 10.0.1+ for enhanced pcguard, correct 1MB checks 2020-11-05 08:53:16 +01:00
a8de605637 typo 2020-11-04 17:13:12 +01:00
c43a12919d add missing newline at the end 2020-11-04 16:23:18 +01:00
3cfc0174f7 fix -N for forkserver 2020-11-03 16:00:29 +01:00
7b4d1c4089 revert out_fd copy 2020-11-03 15:52:19 +01:00
245f511a1d fsrv copy: out_fd = 0 on stdin 2020-11-03 15:15:58 +01:00
8bccf56553 fsrv copy: out_fd = -1 2020-11-03 15:13:04 +01:00
7ed0bfb6f5 copy fsrv: copy out_file ptr 2020-11-03 15:09:57 +01:00
d795ec0451 added better error handling to forkserver fd 2020-11-03 13:41:06 +01:00
350c3b323a fix for #562 2020-11-02 21:17:37 +01:00
d026a9a9c5 typos 2020-11-02 20:06:55 +01:00
445d8f9917 more typos 2020-11-02 19:28:18 +01:00
eab60a084c do not fail for llvm < 10 2020-11-02 17:41:54 +01:00
050354a9de code-format 2020-11-02 17:34:58 +01:00
26d78ce2ec more llvm 10.0.0 fixes 2020-11-02 17:21:07 +01:00
80401940e3 more llvm 10.0.0 fixes 2020-11-02 17:08:05 +01:00
0863d940fe more llvm 10.0.0 fixes 2020-11-02 16:24:16 +01:00
7d944e9512 support llvm svn checkout 2020-11-02 16:17:16 +01:00
fd20432deb typos 2020-11-02 14:17:44 +01:00
a260d34b49 persistent hook example 2020-11-02 14:15:44 +01:00
54ed02ef47 Merge branch 'dev' of github.com:AFLplusplus/AFLplusplus into dev 2020-11-02 13:56:36 +01:00
fdc364046e doc for qemuafl 2020-11-02 13:56:24 +01:00
e24f8ba4b0 fix SancovPCGUARD for 10.0.0 2020-11-02 13:53:27 +01:00
52512aa0af specify testcache is enabled by default 2020-11-02 13:11:21 +01:00
cbfd1c6535 fix release version display 2020-11-02 13:08:50 +01:00
e0fe546459 disable indirect calls in pcguard 2020-11-02 11:55:14 +01:00
416e01d3c6 match mopt to havoc 2020-11-02 11:04:35 +01:00
0fd98ae8b0 added mutation introspection make target 2020-11-01 21:34:08 +01:00
a0c0cf9712 update todo 2020-11-01 10:02:21 +01:00
ded80870a9 reenable afl-clang(++) 2020-11-01 06:22:18 +01:00
5a84db7c67 Fix reference, add missing word 'directory' 2020-11-24 20:14:32 +01:00
27c3423fb6 test-pre.sh: remove old uses of afl-clang, afl-cc.c: add missing env.var. AFL_LLVM_LAF_ALL 2020-11-24 19:38:55 +01:00
ed2f82eaf4 fix compiler warning turned error on NetBSD 2020-11-24 16:13:58 +01:00
cf30f52f25 fix links 2020-11-23 10:07:15 +01:00
e32b7eeb83 fixed child not killed with -c 2020-11-19 16:14:19 +01:00
5ecc4ae0ab add helper script repo 2020-10-31 18:53:35 +01:00
f810639ab1 add our own inline trace-pc-guard 2020-10-31 14:18:58 +01:00
dfb847a51b review, mark env variables, linkify 2020-10-30 14:37:06 +01:00
efd27bd8ce recover heikos faq changes 2020-10-30 14:01:59 +01:00
e0bdfd87b6 fix ck_alloc call 2020-10-30 09:44:06 +01:00
b33306ca2c fix ck_alloc call 2020-10-30 09:43:03 +01:00
a2739ef5ff extras: afl_realloc -> ck_realloc 2020-10-30 09:40:51 +01:00
bdadba60f7 Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-10-29 23:58:47 +01:00
158efe49ac typo 2020-10-29 23:57:51 +01:00
2decf1d088 typo 2020-10-29 23:55:00 +01:00
3e17a90faa typo 2020-10-29 23:45:22 +01:00
46a828212e typo 2020-10-29 23:43:58 +01:00
d4b04f0790 fix compile warning in FreeBSD 2020-10-29 20:06:55 +01:00
9e3b818c35 fix warning on Debian 32 bit 2020-10-29 18:47:00 +01:00
9347ad49b8 Don't crash for unset out_file (fixed #562) 2020-10-29 11:05:07 +01:00
abac876b3a better warn if skipping large dict 2020-10-29 10:45:32 +01:00
7c8b0af84a updated unicorn/next 2020-10-29 08:12:24 +01:00
b5686eb63e fixes two huge bugs 2020-10-29 00:05:28 +01:00
17c0d928e8 disable afl-dyninst libs until it's container is ready 2020-10-28 17:09:30 +01:00
1f46171b39 Merge pull request #592 from rhtenhove/dev
add gnuplot and afl-dyninst shared libs to Dockerfile
2020-10-28 16:19:30 +01:00
587f66f1b3 whoops.. 2020-10-28 15:42:07 +01:00
93c9093817 merge conflict 2020-10-28 14:36:49 +01:00
fe705bb956 expand havoc if not new findings in the last 5 seconds 2020-10-28 14:32:53 +01:00
0b9b4adbd3 show supplied -t option so we know it's used 2020-10-28 14:03:01 +01:00
a09a6459ed add name, forgot that last time 2020-10-28 11:36:06 +01:00
55dd04132f add afl-dyninst shared libraries 2020-10-28 11:30:21 +01:00
d4bf0c7db7 add suggestion for using a ramdisk in docker 2020-10-28 11:27:11 +01:00
90e04d4909 cache is already removed here 2020-10-28 11:02:44 +01:00
0f14057812 this package was getting uninstalled anyway 2020-10-28 11:00:50 +01:00
0d0e220b4b word-o 2020-10-28 10:39:58 +01:00
8cdf04ec90 quote the source lines 2020-10-28 10:39:45 +01:00
987ddb72b1 use fewer layers and remove apt cache
also use full-upgrade to ensure all conflicts are resolved
2020-10-28 10:38:43 +01:00
29a9d3aa49 add gnuplot for afl-plot 2020-10-28 10:36:34 +01:00
424438edfc stop using deprecated MAINTAINER instruction, use LABEL instead 2020-10-28 10:35:07 +01:00
868cb61ea6 hopeful the final testcache improvement ... 2020-10-26 15:24:33 +01:00
44c65fa0a0 add no splicing compile option and print used compile options in afl-fuzz help 2020-10-26 14:44:05 +01:00
029d44a6ec asan_build for tmin and analyze 2020-10-24 19:20:27 +02:00
ca938e7c4e asan_build for tmin and analyze 2020-10-24 19:11:41 +02:00
18ad8a097c travis test 2020-10-24 18:08:54 +02:00
aac0ab8c67 travis test 2020-10-24 17:57:05 +02:00
e5f30c6908 fix testcache bug 2020-10-24 16:28:46 +02:00
e5c2779d56 symbolize=1 for travis 2020-10-24 12:16:30 +02:00
eefc3608e7 added UBSAN, MSAN options 2020-10-24 03:31:41 +02:00
4e99e3b36c initializing testcase_buf 2020-10-24 03:16:22 +02:00
4a5df3dcfe symobile=1 for travis 2020-10-23 23:19:17 +02:00
2e8ec1e339 allow symbolize=1 for asan/debug 2020-10-23 22:49:20 +02:00
aa0d378520 better cache entry algo 2020-10-23 15:21:21 +02:00
0e748ccda7 set max testcache entries automated if not specified by the user 2020-10-23 14:05:34 +02:00
c866aef37f maybe enhancement to cache algo 2020-10-22 21:07:37 +02:00
8d75c08938 change queue cache algo 2020-10-22 15:48:09 +02:00
982260c134 fix timeout bug in read_s32_timed on non linux OSes 2020-10-20 20:48:33 +02:00
fa191017b5 Merge pull request #586 from b1gr3db/dev
qemu argv index was still wrong
2020-10-20 18:47:24 +02:00
73c0e1357f qemu argv index was still wrong 2020-10-20 05:16:34 -07:00
d0cdbc48ae always close file descriptor when opened, use standard types 2020-10-20 00:07:40 +02:00
ac1c3b8701 mini improvements 2020-10-19 11:34:57 +02:00
d8a058bf59 fix AFL_CMIN_CRASHES_ONLY=1 afl-showmap 2020-10-17 14:34:14 +02:00
fcea01a8ea add eviction stat 2020-10-17 11:38:55 +02:00
d5c3b4bafd directly add new queue to cache 2020-10-16 09:35:35 +02:00
b82e9ad3db next afl-showmap fix attempt 2020-10-16 09:16:35 +02:00
fffe53136c Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-10-15 23:30:55 +02:00
bded51e4ea fix issue #583: afl-cmin -C incompatibility 2020-10-15 23:30:36 +02:00
190a9cf1e4 Down with Markdown 2020-10-15 19:42:23 +02:00
1818d1c6a7 Added citation instruction 2020-10-15 19:41:27 +02:00
ee66cd7b27 testcache_size = 2 ok fix 2020-10-15 17:08:45 +02:00
f41aafa4f7 retake from mem if possible 2020-10-15 15:48:39 +02:00
354bda2846 fix reget of testcase after trim 2020-10-15 15:33:47 +02:00
0f8529a3db prepare halloween fuzzbench run 2020-10-15 15:22:40 +02:00
ea0851c654 fix previous commit 2020-10-15 12:54:18 +02:00
d1e18f9edf fix afl_swap_bufs usage? 2020-10-15 12:20:33 +02:00
0139b8cdcb add missing swap bufs 2020-10-15 10:28:11 +02:00
24e0c9cf65 add missing swap bufs 2020-10-15 10:22:40 +02:00
7ca5524e55 Merge pull request #584 from devnexen/dfbsd_build_fix
DragonFlyBSD build fix proposal.
2020-10-14 17:37:21 +02:00
638bf19b65 DragonFlyBSD build fix proposal. 2020-10-14 17:21:22 +01:00
735e8c3956 check for minimum cache size 2020-10-14 17:32:51 +02:00
23872d6f2c add documentation 2020-10-14 16:38:29 +02:00
56ac3fcdc5 configurable testcache with malloc (#581)
* cache item number to cache memory size

* reload testcase if trimming changed the size

* fix splicing selection

* slim splicing

* import sync fix

* write testcache stats to fuzzer_stats

* fix new seed selection algo

* malloc+read instead of mmap

* fix

* testcache is configurable now and no reference counts

* fixes compilation, test script

* fixes

* switch TEST_CC to afl-cc in makefile

* code format

* fix

* fix crash

* fix crash

* fix env help output

* remove unnecessary pointer resets

* fix endless loop bug

* actually use the cache if set

* one more fix

* increase default cache entries, add default cache size value to config.h

Co-authored-by: hexcoder- <heiko@hexco.de>
2020-10-14 15:30:30 +02:00
5ee63a6e62 fix link 2020-10-14 12:54:59 +02:00
c20ba2c2c5 fix custom mutator doc 2020-10-13 15:51:58 +02:00
d9b63766df fix new seed selection algo 2020-10-12 11:12:16 +02:00
5427f7ca98 fix tests for sync mode 2020-10-12 05:02:11 +02:00
15099f7f5a fix afl-cc.8 generation 2020-10-12 04:48:02 +02:00
aef0cd5877 fix sync mode 2020-10-12 04:41:04 +02:00
d6da5605c8 fix splicing selection 2020-10-12 04:03:42 +02:00
b7e0490bcd Revert "Merge branch 'memcache_marc' into dev"
This reverts commit c03fbcedaa, reversing
changes made to dab017ddda.
2020-10-12 03:44:34 +02:00
c03fbcedaa Merge branch 'memcache_marc' into dev 2020-10-12 03:18:54 +02:00
dab017ddda no -M/-S: auto-set -S default 2020-10-12 02:26:14 +02:00
55e1726b23 Merge pull request #579 from Edznux/add_statsd_docs
Add documentation for StatsD
2020-10-12 02:07:14 +02:00
59815cd447 Add documentation for StatsD 2020-10-11 23:06:37 +02:00
6a397d6111 add new seed selection algo and make it the default 2020-10-11 14:31:31 +02:00
01ec0cce47 fix for afl-analyze thanks to JJY-sec! 2020-10-10 15:44:47 +02:00
5540a055c5 afl-showmap: free malloced coverage map 2020-10-10 11:19:45 +02:00
445aba9221 determinstic fuzzing is now disabled by default 2020-10-10 10:55:56 +02:00
5dc3bc175b fix typo 2020-10-10 10:41:30 +02:00
c075003bd2 Merge pull request #571 from Edznux/statsd_implem
Statsd support implementation
2020-10-10 10:36:48 +02:00
1ab83d7d89 fix afl-cc build for no llvm present 2020-10-10 00:21:55 +02:00
cbf8212fe0 fix afl-cc build for no llvm present 2020-10-10 00:18:08 +02:00
e5de66e463 fix afl-cc build for no llvm present 2020-10-10 00:16:52 +02:00
125f8b6ba7 -m none is the default now 2020-10-09 23:23:44 +02:00
4cb4772e2a Remove ==1 in the condition 2020-10-09 18:58:27 +02:00
43d8296504 increase default cache size 2020-10-09 16:45:27 +02:00
0220a8ff66 Add env var toggle for StatsD 2020-10-08 20:48:46 +02:00
fd4a2dfec2 fix persistent_demo_new.c 2020-10-08 17:51:16 +02:00
deab5a1532 Add documentation/help for AFL_STATSD_TAGS_FLAVOR 2020-10-07 19:44:02 +02:00
c12b98e0a4 efficient queue jump 2020-10-07 17:45:54 +02:00
794e8d2d67 change HAVOC_MAX_MULT* to 64 2020-10-07 08:32:58 +02:00
1fd2ffaf14 Fix read on undefined char*. 2020-10-07 00:51:31 +02:00
3e16cf5fbf Remove statsd_socket_init, just let it init when doing the first iteration 2020-10-07 00:20:13 +02:00
17abe7d36e Fixed segfault because wrong order in args 2020-10-06 23:23:45 +02:00
3d7bdc9f0b [WIP: segfault on non dogstatsd] Adding MACROS for format 2020-10-06 23:00:11 +02:00
f519fff7bc fix compiler warning on 32-bit platforms 2020-10-06 21:56:17 +02:00
231420775f fix test-performance.sh 2020-10-06 19:49:48 +02:00
2d5fadc1e6 hunting ref underflow 2020-10-06 16:45:25 +02:00
4f207b4eba fixed ref check 2020-10-06 16:20:32 +02:00
693252c737 fixed missing quote warning 2020-10-06 16:17:53 +02:00
74dc227c44 code format 2020-10-06 15:38:36 +02:00
a4b60ca5b6 testcase cache added 2020-10-06 15:37:59 +02:00
fd4efd04a1 fixed gcc 10 warning 2020-10-05 23:07:22 +02:00
9ac9aa2511 Fix code format 2020-10-05 22:21:24 +02:00
916b6fd317 Refactor global var into afl_state_t struct 2020-10-05 22:21:01 +02:00
2bf3a70e2b Correctly handle env var. 2020-10-05 22:01:50 +02:00
9b112fde1a Add help for AFL_STATSD_HOST &
AFL_STATSD_PORT env var
2020-10-05 19:29:37 +02:00
bab60b68d9 changed the default schedule to coe 2020-10-04 20:45:59 +02:00
1a12db1b59 Code format 2020-10-04 16:11:05 +02:00
1e0bc2e5c3 Merge remote-tracking branch 'origin/dev' into statsd_implem 2020-10-04 16:03:15 +02:00
b0de6fed11 Mention tags format in macro's name 2020-10-04 14:29:50 +02:00
ca6106a1dc Refactor 2020-10-04 14:24:25 +02:00
6006cce0cf Define config, change parent func to show_stats 2020-10-04 03:24:09 +02:00
ff8c6d2415 Adds other metrics 2020-10-04 03:22:28 +02:00
26dcddab0c Add config ifdef 2020-10-03 23:56:55 +02:00
2239743361 Rewrote format metric to be simpler/more static 2020-10-01 00:11:01 +02:00
383cd487a2 small improvements to Marcel's patch, fix laf-intel + redqueen crashes 2020-09-29 15:02:57 +02:00
fe08482c1b Merge pull request #568 from mboehme/dev
Patching and improving AFLFast schedules.
2020-09-29 14:06:20 +02:00
e87eca7fe8 Patching and improving AFLFast schedules. 2020-09-29 11:53:27 +00:00
e69b25e34b increase havoc_stack_pow2 on no finds 2020-09-28 10:13:00 +02:00
e85fde201e OpenBSD build fix (#566)
* OpenBSD build fix proposal.
Mainly the gcc plugin, using one of the egcc/eg++ version available.
Needs pthread flag to r/w lock api.

* README update
2020-09-26 19:02:19 +02:00
e301822c68 readme 2020-09-26 12:41:13 +02:00
a55e0d1189 WIP envs 2020-09-25 23:28:15 +02:00
fc75d2b181 remove warnings 2020-09-25 13:14:54 +02:00
6b3b1775b6 improving on splice candidate check patch 2020-09-25 12:03:24 +02:00
3334eeb4eb Merge pull request #564 from wakolzin/dev
Fix potential endless loop in custom_mutator_stage
2020-09-25 11:39:25 +02:00
e38ca0e750 setting HAVOC_STACK_POW2 to 4, as fuzzbench shows that too mach mutation is less effective 2020-09-25 10:48:08 +02:00
a75e7594f7 Add comments
Co-authored-by: Ivan Gulakov <gulakov@ispras.ru>
2020-09-24 18:50:59 +03:00
888d63748a Fix potential endless loop in custom_mutator_stage
Co-authored-by: Ivan Gulakov <gulakov@ispras.ru>
2020-09-24 18:25:32 +03:00
818c05cf29 qemuafl 2020-09-23 19:53:37 +02:00
60ef1f7305 Update Changelog.md 2020-09-23 02:28:19 +02:00
b3b5e25091 examples: fixed path for docs (#563) 2020-09-23 01:58:11 +02:00
f4a0407270 add comment for future 2020-09-22 20:06:39 +02:00
daa2285083 fix LTO autodictionary for ints > 64 bits 2020-09-22 09:32:16 +02:00
639372b644 code-format, and no code-format for custom mutators 2020-09-22 02:11:10 +02:00
52c135e1a2 fix warning 2020-09-22 02:05:31 +02:00
093754f6bf dict2file fix for integers > 64 bit 2020-09-22 02:04:23 +02:00
207cbd5cf7 fix and update libfuzzer custom mutator 2020-09-21 15:47:10 +02:00
f34fe1f81e updated unicornafl 2020-09-21 13:13:03 +02:00
5f52f72761 set explore as default, fix schedule display 2020-09-20 14:58:08 +02:00
6d99695108 fix travis 2020-09-19 17:25:39 +02:00
a18523f018 fix exploit, mmopt and rare schedule 2020-09-19 15:13:58 +02:00
7ff9800804 fix exploit, mmopt and rare schedule 2020-09-19 15:13:15 +02:00
44c0dc6d96 fix expand havoc 2020-09-18 12:19:27 +02:00
1225f3aa70 improved defaults from fuzzbench results 2020-09-18 10:29:49 +02:00
9eed8fe588 portability: type -> command -v, compiler warnings 2020-09-18 09:02:43 +02:00
4561a9590f WIP. basic state working: submitting statsd metrics (path, crashes, hangs) 2020-09-17 01:29:09 +02:00
5241d1221b fix if no clang is present 2020-09-14 15:46:36 +02:00
a95427a873 fix if no clang is present 2020-09-14 15:15:53 +02:00
c5c5570d97 fix if no clang is present 2020-09-14 15:08:07 +02:00
3abace4f54 Haiku build fix. (#556) 2020-09-13 16:58:06 +02:00
7cdbe3173e fixed ngram size unset 2020-09-13 15:00:10 +02:00
7f94fe3587 code format 2020-09-13 14:26:57 +02:00
060dbe1239 wine argv fix 2020-09-13 14:26:24 +02:00
a9ba907676 Fix qemu argv construction (#555) 2020-09-13 14:25:02 +02:00
fe39e0184f gcc_plugin switch CC to gcc when cc is something else 2020-09-13 00:02:42 +02:00
688e2c87df portability: make llvm-dict2file compilable under Ubuntu xenial (llvm 3.8.0) 2020-09-11 22:13:42 +02:00
38e6de1eff portability: make new gcc-plugin compilable under Ubuntu 16.04 (gcc 5.4.0) 2020-09-11 21:46:20 +02:00
862b6d0382 fix for afl-compiler-rt to only send dictionary data if there is some 2020-09-11 08:56:28 +02:00
543765bc0d fix unittests 2020-09-10 16:41:40 +02:00
380051868a add libfuzzer custom mutator, minor enhancements and fixes 2020-09-10 15:26:46 +02:00
fdb0452245 update documentation 2020-09-10 08:54:57 +02:00
454a860020 update gcc readme 2020-09-09 23:25:01 +02:00
a4cac3fce5 new custom mutator: symcc 2020-09-09 17:49:43 +02:00
f8c0182e16 fix travis 2020-09-09 12:49:49 +02:00
d3282ef1fe fix makefile 2020-09-09 10:37:04 +02:00
9b6564f0a3 fix for gcc_plugin 2020-09-09 09:36:27 +02:00
eb9f323d7c Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev 2020-09-08 20:58:44 +02:00
fe98e2d760 update qemuafl 2020-09-08 20:58:35 +02:00
ab744abc4b code-format 2020-09-08 17:54:01 +02:00
2802245da7 update instrumenation/README.instrument_file.md for gcc_plugin 2020-09-08 17:15:32 +02:00
c091340a85 new gcc_plugin integration 2020-09-08 16:15:31 +02:00
3890225c35 Merge pull request #551 from AdaCore/topic/plugin
rewrite gcc plugin
2020-09-08 15:21:55 +02:00
9544b3dbf2 rewrite gcc plugin
When we started using AFL, it did not have an integrated GCC plugin.
There was one proposed by Austin Seipp, but for various reasons we
ended up using some of its infrastructure (runtime and wrapper), but
writing the GCC plugin proper from scratch.

With AFL++'s renewed interest in a GCC plugin, we rebased ours, with
some features that are or were missing in the one that was integrated:

* efficient, fully-functional inline and out-of-line instrumentation

Inline instrumentation was work in progress in the original plugin.
Controlled by AFL_GCC_OUT_OF_LINE.

* reproducible instrumentation

Obey -frandom-seed for pseudorandom number generation.

* licensing clarity and strict compliance

GPLv3+ for the plugin, that uses GCC internals; add a copy of the
license, as required.

* allow/deny list support

Copied and adjusted from the LLVM plugin implementation.

* neverZero support

Not as compact as the asm-wrapper version, but likely more efficient.
Both are quite thread-unsafe, with different caveats.
Controlled with AFL_GCC_SKIP_NEVERZERO.
2020-09-08 14:55:19 +02:00
43cb626ad3 revert for unicorn_mode 2020-09-08 14:03:53 +02:00
13f2ac83a9 fix GNUmakefile for new qemuafl 2020-09-08 11:48:31 +02:00
374e068f97 set correct error code when -V 2020-09-08 11:37:09 +02:00
10f775be07 conflicts 2020-09-08 11:19:17 +02:00
3565641c41 arm64 travis 2020-09-08 11:13:16 +02:00
679b2d63a8 qemuafl + arm 2020-09-08 10:56:37 +02:00
6404abd7d6 bugfix for fixed seeds 2020-09-07 17:30:28 +02:00
7bcbfd48e5 update ideas 2020-09-06 15:12:23 +02:00
06ac2850c5 bcmp for compare-transform-pass 2020-09-06 13:53:40 +02:00
6114a48b89 add std::string and bcmp to dictionary functions 2020-09-06 13:29:32 +02:00
e065ba9497 update gitignore 2020-09-06 12:47:14 +02:00
8ec41b2585 fix various warnings 2020-09-06 11:40:41 +02:00
bd007c151d fix unicorn clean 2020-09-06 11:23:59 +02:00
163e5ffd10 -p seek is now the default 2020-09-05 17:40:39 +02:00
ded4d093ff skip crashes but keep for splices 2020-09-05 16:16:56 +02:00
2508922288 cmp dict for LTO 2020-09-05 15:49:10 +02:00
39c020ec74 Merge pull request #549 from AFLplusplus/ignore_unstable_edges
ignore unstable edges
2020-09-05 13:26:46 +02:00
81b1d85f61 Merge pull request #548 from AFLplusplus/pre-3
Pre 3.0 changes
2020-09-05 13:26:08 +02:00
2f90f2faba code-format 2020-09-05 13:19:19 +02:00
e30b2c6af6 final changes for pre-3.0 2020-09-05 13:18:28 +02:00
4b3ad5f037 add cull queue, -i subdir traversal 2020-09-05 12:32:10 +02:00
996986bed5 first batch of changes 2020-09-05 12:11:48 +02:00
ee206da389 Merge pull request #545 from AFLplusplus/dev
v2.68c
2020-09-04 22:51:43 +02:00
fac108476c v2.68c 2020-09-04 22:50:45 +02:00
4f7a8a4c70 Merge pull request #542 from AFLplusplus/dev
push to stable
2020-09-04 22:48:46 +02:00
976ee9022c fix assignment 2020-09-04 22:47:37 +02:00
0625eb0a05 avoid signed ints for amounts (which are positive) 2020-09-04 22:26:39 +02:00
77b824d101 compile fix 2020-09-04 17:56:17 +02:00
b7b38205d8 fix travis 2020-09-04 17:37:11 +02:00
6c715f1a69 more changes to fuzzer_setup 2020-09-04 17:04:42 +02:00
fc19aa96f7 Merge pull request #544 from ThomasTNO/export_env_vars
Export set afl_environment_variables to stats
2020-09-04 16:30:15 +02:00
50f61b64b1 Make open_file() inline 2020-09-04 16:22:22 +02:00
809a7cffe2 Write set environment variables in an env file style. 2020-09-04 16:02:09 +02:00
6399f84ba2 fix example 2020-09-04 16:02:02 +02:00
8459bcdf85 fix example 2020-09-04 16:01:08 +02:00
6adaacbb3a Seperate fuzzer_setup from fuzzer_stats, only write fuzzer_setup at the start 2020-09-04 15:54:57 +02:00
6c846bcf2c fix driver test 2020-09-04 15:27:02 +02:00
e45ae8e5da Export set afl_environment_variables to stats 2020-09-04 13:48:43 +02:00
cea2fadbf4 fix afl-whatsup time 2020-09-03 10:27:32 +02:00
4c48d3a3ad update xxh3 to 0.8.0, fix is_ascii 2020-09-03 09:59:23 +02:00
c39a552cc0 ignore unstable 2020-09-02 20:30:26 +02:00
020b8a4964 minor FAQ fixes 2020-09-02 20:19:49 +02:00
08f6e1d66a children terminate on sigpipe 2020-09-02 17:54:54 +02:00
28e457e8d8 add temporary travis target 2020-09-02 10:18:10 +02:00
c7255f2e26 bugfix for Ubuntu trusty: avoid unknown compiler option 2020-09-01 18:55:37 +00:00
6340674a23 Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-09-01 19:54:18 +02:00
4538f689ed split-compares-pass: bugfix add missing instrument_file filtering in some places 2020-09-01 19:51:53 +02:00
415b759ed1 update qemuafl 2020-09-01 16:20:14 +02:00
e4a86b40a5 child cleanup 2020-09-01 13:42:33 +02:00
b8d3a97a4f build with qemuafl 2020-09-01 13:08:32 +02:00
75c38d6243 Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev 2020-09-01 12:36:13 +02:00
6f75100602 qemuafl envs 2020-09-01 12:36:04 +02:00
07cee6b750 fix unicorn clean 2020-09-01 11:13:26 +02:00
651ad18e21 added the grammar mutator as a git submodule + documentation 2020-09-01 10:52:39 +02:00
664daa2f3c add qemu driver env var 2020-09-01 01:12:40 +02:00
ed6243df5a Review FAQ.md 2020-09-01 00:49:26 +02:00
bd57784664 code format 2020-08-31 23:59:10 +02:00
7f621509ee Merge pull request #540 from AFLplusplus/dev
Dev
2020-08-31 23:04:09 +02:00
4261e17b3e replace non portable echo -n with printf 2020-08-31 22:08:54 +02:00
8ca4414d70 merge conflicts 2020-08-31 20:34:28 +02:00
6090bb1bca better fix for #539 2020-08-31 20:33:56 +02:00
a552631d3b update changelog 2020-08-31 20:22:20 +02:00
c552229c4d Merge pull request #539 from hazimeh/dev
Fixed stack use-after-return bug in strntoll
2020-08-31 20:20:36 +02:00
2dffed1cff Merge pull request #534 from AFLplusplus/dev
push to stable for GSOC
2020-08-31 19:04:37 +02:00
e93f78eca5 Merge pull request #538 from ploppelop/patch-1
Update parallel_fuzzing.md
2020-08-31 18:44:28 +02:00
9bbbec3fa8 Fixed stack use-after-return bug in strntoll 2020-08-31 18:39:50 +02:00
338638b124 Update parallel_fuzzing.md
fix multisystem example
2020-08-31 18:34:27 +02:00
17e1a72b3b Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev 2020-08-31 18:32:33 +02:00
3e6471b949 added files to gitignore 2020-08-31 18:32:28 +02:00
e4de4e3500 update gitignore 2020-08-31 18:32:01 +02:00
bea76b346c fixed build error 2020-08-31 18:29:45 +02:00
53e63e9ded added forkserver init timeout 2020-08-31 18:28:36 +02:00
b1b5e21600 removed dummy 2020-08-31 18:20:39 +02:00
d765740707 hopefully finally fix ancient apple gmake 2020-08-31 18:18:40 +02:00
192cadee34 fix docs 2020-08-31 18:10:18 +02:00
d7d8afa512 fix afl-gcc help output 2020-08-31 17:54:21 +02:00
01fcee1190 fixed make install on mac os 2020-08-31 17:36:16 +02:00
0805437222 fix make man 2020-08-31 17:10:13 +02:00
4398b9b517 fix network server 2020-08-31 16:50:26 +02:00
909262f6c5 fix non portable option -T for 'install' 2020-08-31 16:44:18 +02:00
155ef8875a Fix few warnings for FreeBSD case. (#536) 2020-08-31 16:37:46 +02:00
58cf030546 fix for MacOS sudo 2020-08-31 16:34:57 +02:00
18ea9a8447 omit linker option '--dynamic-list' for MacOS 2020-08-31 16:19:09 +02:00
ebd1e6bc4b Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-08-31 15:40:47 +02:00
45d866d548 typo 2020-08-31 15:40:20 +02:00
8087cf7988 Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev 2020-08-31 15:17:51 +02:00
9e8b3f13e1 fixed warning on mac 2020-08-31 15:17:37 +02:00
ce4700ca6e Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-08-31 15:17:24 +02:00
8253f90134 typos/wording 2020-08-31 15:16:55 +02:00
86421f3469 Merge pull request #535 from rhertzog/manpages
Fix manpages generated by llvm_mode/GNUMakefile
2020-08-31 15:05:54 +02:00
811ef13b20 Fix manpages generated by llvm_mode/GNUMakefile
* Use a build date derived from SOURCE_DATE_EPOCH like in the main
  Makefile
* Fix the path to the binary
2020-08-31 14:56:02 +02:00
7fb72f1038 typos 2020-08-31 14:47:22 +02:00
d2c9e4baa7 fix warnings and weird code insert 2020-08-31 13:12:59 +02:00
81767287c3 improve documentation 2020-08-31 13:02:40 +02:00
6c980e2a02 rewrite documentation 2020-08-31 12:56:14 +02:00
e7db4d4fe0 fix sync script, update remote sync documentation 2020-08-31 12:36:30 +02:00
567042d146 typos fixed. 2020-08-31 12:32:31 +02:00
4697e4a5a5 description for AFL_MAX_DET_EXTRAS added 2020-08-31 12:26:41 +02:00
92b1f9cf36 added SO link about rand for clarification 2020-08-31 12:11:38 +02:00
bbf00204ea Merge pull request #531 from rhertzog/extend-cflags-safe
Add -fdebug-prefix-map to CFLAGS_SAFE
2020-08-31 11:38:09 +02:00
a42b74b624 Merge pull request #530 from rhertzog/add-cppflags
Pass CPPFLAGS to all calls of the C compiler
2020-08-31 11:33:34 +02:00
7ee255cbcf Merge pull request #529 from rhertzog/manpages
Manual pages related changes
2020-08-31 11:21:52 +02:00
961ddfd7f8 Merge pull request #528 from rhertzog/fix-symlinks
Fix the /usr/bin/afl-clang and afl-clang++ symlinks
2020-08-31 11:19:16 +02:00
4566bcf122 code-format 2020-08-31 10:57:01 +02:00
ca0105ddf6 fix make DEBUG=1 (error seen with gcc 10.2.0) 2020-08-29 21:45:23 +02:00
41bb359428 Fix various spelling errors (#532)
All those spelling errors have been caught by lintian's built-in
spellchecker:
https://lintian.debian.org/tags/spelling-error-in-binary.html
2020-08-28 23:04:25 +02:00
146ede0f29 Add -fdebug-prefix-map to CFLAGS_SAFE
In Debian, we override CFLAGS to include -fdebug-prefix-map to avoid
hardcoding the build path in any generated debug information. This is
to help with getting the package to build reproducibly.

However you seem to voluntarily not honor CFLAGS but only CFLAGS_SAFE
for a limited number of source files. This resulted in a lintian warning
on Debian's side (https://lintian.debian.org/tags/file-references-package-build-path.html)
pointing to /usr/lib/afl/afl-llvm-rt-64.o and /usr/lib/afl/afl-llvm-rt.o.

With this commit, I'm manually adding -fdebug-prefix-map as a safe
build flag to CFLAGS_SAFE.
2020-08-28 22:16:08 +02:00
c0fd7ba6d1 Pass CPPFLAGS to all calls of the C compiler
This variable is a standard way to inject options for the C
preprocessor. It's respected by the implicit rules of make
and autoconf/automake.

Debian sets this variable during package build to inject
`-D_FORTIFY_SOURCE=2` and we would like afl++ to respect it.

Note that this commit also adds $(CFLAGS) in the build of
afl-performance.o where it was missing. It might have been
on purpose but we want to keep CFLAGS everywhere as well
since Debian injects various options through that variable
(for hardening and reproducibility).
2020-08-28 21:55:52 +02:00
b0b2a15891 Improve the generated manual page to be compatible with whatis
And generate the manual page for afl-g++ too.
2020-08-28 21:34:36 +02:00
ff3c9cbd73 Fix installation path of manual pages
Manual pages are stored in /usr/share/man/.
2020-08-28 21:34:08 +02:00
6e839f0f6a Fix the /usr/bin/afl-clang and afl-clang++ symlinks
Right now they are created pointing to '../afl-clang-fast' instead
of 'afl-clang-fast. Given that all the binaries are in the same directory,
the symlinks are effectively broken.
2020-08-28 21:26:32 +02:00
a3cd523250 todo update 2020-08-27 15:24:38 +02:00
b44620f0b0 Merge pull request #526 from h1994st/dev
Prevent afl-fuzz from modifying stage_max during fuzzing
2020-08-27 08:56:08 +02:00
9a6a32775f Prevent afl-fuzz from modifying stage_max during fuzzing 2020-08-27 00:32:53 -04:00
3e8beaafc8 fixing wrong fatal ... 2020-08-26 21:22:24 +02:00
33e58c1d4e some warnings fixes 2020-08-26 14:45:59 +02:00
4be0ea596b rand unlikely added 2020-08-26 13:04:03 +02:00
96ef7083c8 using unbiased rand_below 2020-08-26 05:28:33 +02:00
78eaa6b203 lintokencap: fix compiler complains on Solaris 11. (#525) 2020-08-25 17:11:15 +02:00
1efc6e59b7 Added out_file value when using stdio (#524) 2020-08-24 21:18:51 +02:00
19eddbb0c7 make py functions as optional as they are in the doc 2020-08-24 18:12:08 +02:00
6a34c5aa3e fix python implementation for new function 2020-08-24 18:06:07 +02:00
c7f0d30668 added afl_custom_fuzz_count 2020-08-24 17:32:41 +02:00
a7c3f252d5 unicorn build warning for MUSL based linux distros. (#510) 2020-08-24 12:06:04 +02:00
b9b6f06429 Allow Large Extras (#523)
* allow large extras

* skipping large testcases again
2020-08-24 12:04:29 +02:00
a1442bd1ac no longer warns for prob. extras 2020-08-23 11:21:49 +02:00
4d9d52e3d9 code format 2020-08-23 11:00:46 +02:00
6184832ea9 added more env var docs, fsrv fixes for cmin, tmin 2020-08-23 10:59:56 +02:00
e2b54bfa05 code format 2020-08-23 10:40:46 +02:00
425908a00c Option for specifying forkserver initialization timeout via environment variable (#522)
* Addition of AFL_FORKSRV_INIT_TMOUT env var

This commit introduces a new environment variable which allows to
specify the timespan AFL should wait for initial contact with the
forkserver.

This is useful for fuzz-targets requiring a rather long setup time
before the actual fuzzing can be started (e.g., unicorn).

* add .swp files to .gitignore

* Inherit init_tmout in afl_fsrv_init_dup

Without this patch, the forkserver would spawn with a timeout of 0 in
cmplog mode, leading to an immediate crash.

Additionally, this commit removes a spurious whitespace.

* Initialize afl->fsrv.init_tmout in afl_fsrv_init

Not all afl-components will need the new AFL_FORKSRV_INIT_TMOUT
environment variable. Hence, it's initialized to the safe "default"
value from before in afl_fsrv_init now.
2020-08-23 10:39:34 +02:00
1301552101 added AFL_MAX_DET_EXTRAS env var 2020-08-23 01:48:36 +02:00
c4f71ab201 enable autodict for cmplog compile, it is ensure not be used in the forkserver 2020-08-22 10:01:45 +02:00
42ef1968a5 Merge pull request #521 from AFLplusplus/dev
Push to stable to fix wrong free on exit
2020-08-22 01:30:21 +02:00
5ec91fce23 fix for bad free (#520) 2020-08-21 23:03:08 +02:00
47878f6974 add execs_done to plot file 2020-08-21 23:33:35 +02:00
d5c77a9e96 update todo 2020-08-21 15:45:15 +02:00
4d2694c114 fix semicolon 2020-08-21 15:26:43 +02:00
017c8a6940 fix global id documentation for LTO pcguard 2020-08-21 14:39:47 +02:00
b0a783e86f code format 2020-08-21 11:18:18 +02:00
714e4d2b46 fixed for LTO llvm 11 2020-08-21 11:17:03 +02:00
85a4c5e724 only compile SanitizerCoverage for LTO 2020-08-21 10:50:06 +02:00
182b8b3e14 remove doc reference for SKIPSINGLEBLOCK 2020-08-20 19:00:15 +02:00
4ce5ed370a LTO: sancov made default, deprecated SKIPSINGLEBLOCK, deactivate LTO autodict for cmplog binaries 2020-08-20 18:57:05 +02:00
f7bac482e9 Merge pull request #518 from AFLplusplus/ltopcguard
sancov ported to LTO
2020-08-20 18:33:53 +02:00
bd074e9150 add missing features for sancov lto 2020-08-20 18:32:22 +02:00
d52ea44c27 fix wrong var 2020-08-20 18:27:22 +02:00
9c1b6cfb99 Merge pull request #517 from AFLplusplus/custommut-readme
Custom Mutator readme
2020-08-20 14:21:42 +02:00
631d3f274a move afl_loop check 2020-08-20 13:49:11 +02:00
3cdaf4dcf2 sancov enhancement 2020-08-20 13:37:34 +02:00
572944d726 Merge pull request #514 from AFLplusplus/dev
push to stable
2020-08-20 11:00:07 +02:00
779d8f6b7e support current llvm12 changes 2020-08-20 10:56:51 +02:00
322847755a Merge pull request #513 from fouzhe/patch-1
fix typo
2020-08-19 17:58:35 +02:00
f9f28b9c7c fix typo 2020-08-19 23:54:45 +08:00
c3bc0145e7 Merge pull request #512 from AFLplusplus/stable
activate travis for stable
2020-08-19 16:21:03 +02:00
17d403b8f8 activate travis for stable 2020-08-19 16:19:31 +02:00
9faf7b6fc8 v2.67d 2020-08-18 20:08:29 +02:00
5c759953f4 Merge pull request #511 from AFLplusplus/stable
push to dev
2020-08-18 20:05:56 +02:00
1c64048d0f 2.67c 2020-08-18 19:56:47 +02:00
b504b9313a code-format, changelog 2020-08-18 01:36:49 +02:00
1a94cfe2af moved autodict extras away from extras_a 2020-08-18 01:31:40 +02:00
7470b475a9 Reworked maybe_grow to take a single ptr, renamed to afl_realloc (#505)
* maybe_grow takes a single ptr

* fixed use_deflate

* reworked maybe_grow_bufsize

* helper to access underlying buf

* remove redundant realloc_block

* code format

* fixes

* added unit tests

* renamed maybe_grow to afl_realloc

* BUF_PARAMS -> AFL_BUF_PARAM
2020-08-18 00:50:52 +02:00
0a6084f361 Merge pull request #499 from AFLplusplus/dev
important push to stable
2020-08-17 23:42:45 +02:00
f92607cff1 pcguard for lto 2020-08-17 22:56:48 +02:00
9532499ef5 install libafldrivers 2020-08-16 14:14:24 +02:00
1d56de6c1d fix lto autodict for long strings 2020-08-16 13:29:24 +02:00
266b51a842 final afl-llvm-rt.o.c that takes care of all eventualities 2020-08-16 10:53:38 +02:00
cc1fe2f2d2 skip instrumenting blocks following __afl_loop to improve stability in LTO. not in afl-llvm-pass and instrim because they are outdated, sancov cant be fixed 2020-08-15 22:59:49 +02:00
43214d6b46 more likely 2020-08-15 22:10:28 +02:00
2f28ecd3a5 more unlikely 2020-08-15 20:51:57 +02:00
73a629d6f2 important bugfix for large covmaps 2020-08-15 18:14:44 +02:00
0a251f93e0 increase initial memory sized 2020-08-15 13:34:51 +02:00
1cf4738487 more FAQ 2020-08-15 10:27:40 +02:00
af14acf2c1 Revert "Merge branch 'debug' into dev"
This reverts commit a7537b5511, reversing
changes made to 15e799f7ae.
2020-08-14 14:35:05 +02:00
a7537b5511 Merge branch 'debug' into dev 2020-08-14 13:23:14 +02:00
15e799f7ae fix for llvm 11 2020-08-14 12:42:45 +02:00
5f0a9c90c8 fixes lots of llvm warnings 2020-08-14 12:06:00 +02:00
9ff9ff2ad2 more secure way to work with a dynamic map 2020-08-14 11:40:26 +02:00
d86b13384f remove unnecessary code, increase init map size 2020-08-14 11:25:13 +02:00
17a4e9fadf remove debug 2020-08-14 10:11:04 +02:00
ce513c4f3e fix llvm12 -fuseld warning 2020-08-14 10:10:23 +02:00
ce92adcb9b formatting 2020-08-14 08:33:36 +02:00
e94cc1fae0 new faq entries 2020-08-14 07:09:01 +02:00
32fe047894 fix AFL_LLVM_MAP_DYNAMIC 2020-08-14 06:46:22 +02:00
d1bc0207cc no longer using alloc for autodict 2020-08-14 01:33:03 +02:00
69f8c62955 code-format 2020-08-14 00:46:48 +02:00
83df65a66b cleaned up maybe_add_auto calls 2020-08-14 00:46:15 +02:00
c3a6e7e870 testcases indicate count 2020-08-14 00:20:04 +02:00
d0ab83a202 Changelog wording/whitespace 2020-08-13 23:19:05 +02:00
b5d1a021ef fix llvm and afl-showmap 2020-08-13 22:34:11 +02:00
e9fb5f4cbc fix for llvm < 11 2020-08-13 21:46:27 +02:00
212bb990b7 LTO: apply laf-intel+redqueen/cmplog at link time 2020-08-13 21:27:11 +02:00
8e984c2aa0 fix for sancov 2020-08-13 18:24:36 +02:00
7f435ec5f1 refactor get filename 2020-08-13 16:29:00 +02:00
47faf3dd33 code review: fixed some typos 2020-08-13 15:44:03 +02:00
c4e52e20c9 fix warning 2020-08-13 12:58:10 +02:00
2c5e103278 make cmplog deterministic 2020-08-13 12:39:18 +02:00
7a6867e2f8 split up __afl_manual_init, added internal AFL_DISABLE_LLVM_INSTRUMENTATION, skipping ctor+ifunc functions for all llvm, code-format 2020-08-12 16:06:30 +02:00
8044ae28be Merge pull request #496 from AFLplusplus/dev
push to stable
2020-08-12 14:29:34 +02:00
b38837f4ff setting attribute hot intelligently gives 0.5% speed 2020-08-12 14:14:44 +02:00
c25479264d fix LLVM version check 2020-08-12 13:28:31 +02:00
e9b3da5d96 llvm 9 workaround 2020-08-12 11:03:33 +02:00
132b57cf03 nozero for llvm pcguard 2020-08-12 10:41:09 +02:00
ee548df05f improve readme 2020-08-11 22:58:57 +02:00
052d74b16c added new githubsecurity blog entry 2020-08-11 18:44:57 +02:00
83281503b3 Merge pull request #498 from AFLplusplus/taint
dont merge
2020-08-11 18:26:30 +02:00
b604f5eafc finalize first beta! yay! 2020-08-11 18:06:18 +02:00
220dc4a43d review done, pray 2020-08-11 16:25:35 +02:00
457f627101 move taint_mode var 2020-08-11 15:10:18 +02:00
4f695b6f4c fixes 2020-08-11 11:16:48 +02:00
3ec1b23743 cleanup minor issues 2020-08-11 10:36:34 +02:00
0ba09ee85a enhancements 2020-08-11 10:24:45 +02:00
67dac15226 Merge branch 'debug' into taint 2020-08-11 03:40:12 +02:00
9cf8637fab break up llvm rt afl init 2020-08-11 03:37:02 +02:00
50e76fce12 adding ctor function skipping in LTO fixed map mode 2020-08-11 02:05:39 +02:00
432638404f ignoring ifuncs on fixed map LTO 2020-08-11 01:31:44 +02:00
1e38c10efb remove warning, fix text 2020-08-10 23:48:13 +02:00
701fb95d24 LTO: make dynamic map the default 2020-08-10 23:42:33 +02:00
7b5a18428e fixed __func__ 2020-08-10 23:17:39 +02:00
7d7a8c7c39 code-format 2020-08-10 19:05:22 +02:00
a422fcaa40 fixed minor inconsistencies, reenabled warnings 2020-08-10 19:04:51 +02:00
fee58a4d1b segfault info 2020-08-10 14:32:35 +02:00
3ecafde29d increase stack size 2020-08-10 13:59:30 +02:00
8428b18d2a fix another segfault 2020-08-10 13:30:25 +02:00
9c953ab51f memory grab at startup to prevent crashes 2020-08-10 13:03:59 +02:00
f181a8307b put ! in .new map 2020-08-10 12:48:15 +02:00
84b9d551fd disable expand havoc mopt for taint 2020-08-10 12:11:49 +02:00
8f8555dfdf fix segfault 2020-08-10 12:05:30 +02:00
464c27082a fuzzbench integration 2020-08-10 11:45:32 +02:00
3aa7d8081d fuzzbench integration 2020-08-10 11:23:33 +02:00
fb84103ffb please no -Werror in the dev branch 2020-08-10 09:39:43 +02:00
c270646722 fix wrong incomplete 2020-08-10 09:37:32 +02:00
87da1e7af6 fix driver 2020-08-10 09:16:55 +02:00
9b71f7e5e4 debug 2020-08-10 08:47:52 +02:00
9945c1648b fix driver 2020-08-10 01:34:45 +02:00
e5d24827de force llvm map 2020-08-10 01:28:26 +02:00
a6521e89fc different driver 2020-08-10 01:04:50 +02:00
5e36fb32a8 readme 2020-08-10 00:56:08 +02:00
fb0181f5bc readme 2020-08-10 00:53:50 +02:00
6fa2c213ef add driver 2020-08-10 00:24:27 +02:00
9ec223c844 final touches for first testing 2020-08-09 23:47:51 +02:00
558a82891a finalize havoc 2020-08-09 22:02:44 +02:00
4fc16b542e havoc copy 2020-08-09 21:32:15 +02:00
ff40359a60 fixes 2020-08-09 21:09:07 +02:00
e99d7e9730 integration in fuzz_one 2020-08-09 20:24:56 +02:00
b60663c031 taint integration done 2020-08-09 18:48:12 +02:00
32db31b555 fixes 2020-08-09 12:35:52 +02:00
a1129b67c2 changes 2020-08-09 12:15:36 +02:00
8a1cf3f0f9 Merge pull request #497 from murx-/dev
Add support for specific custom mutator name
2020-08-09 01:19:55 +02:00
0bb59ba116 code format 2020-08-09 01:09:26 +02:00
e4a0237cbc step 1 2020-08-09 00:35:12 +02:00
d8f5502d83 initial integration 2020-08-08 20:29:56 +02:00
45d0e4765e fix stderr output 2020-08-08 19:37:15 +02:00
9a1d526ed4 Add support for specific custom mutator name 2020-08-08 18:34:54 +02:00
ebc6f52868 cp inc 2020-08-08 12:55:35 +02:00
a19b31bf82 cp inc 2020-08-08 12:53:39 +02:00
28251a495a reformatted table 2020-08-08 12:41:35 +02:00
f4592a8fb4 merged readme changes 2020-08-08 12:38:10 +02:00
b29d91edf5 add stub directory 2020-08-08 11:17:05 +02:00
986af28df2 README.md: show afl-gcc as intel only 2020-08-07 22:06:36 +02:00
27abecbff5 compile fixes on 32-bit OSs 2020-08-07 21:07:05 +02:00
33141cf8a3 tests: cleanup core files, more time for llvm cmplog 2020-08-07 20:22:13 +02:00
8551d8e48e Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-08-07 19:53:32 +02:00
32558bc807 minor test fixes for Raspberry Pi Linux 64-bit 2020-08-07 19:41:39 +02:00
934cdc32f4 Merge branch 'dev' of https://github.com/vanhauser-thc/AFLplusplus into dev 2020-08-07 17:32:52 +02:00
699ebaa8e2 code format 2020-08-07 17:32:41 +02:00
44ad516edd remove debug 2020-08-07 17:23:11 +02:00
fd9a7e719d fixed wextra 2020-08-07 17:20:24 +02:00
e51f1ea5a5 Merge branch 'dev' of https://github.com/vanhauser-thc/AFLplusplus into dev 2020-08-07 16:56:12 +02:00
22d3a5e90a enabled Wextra, fixed bugs 2020-08-07 16:55:58 +02:00
673ace2a4b test-llvm.sh: clear file errors after test 2020-08-07 16:34:10 +02:00
4a6d66d8c5 fix typos 2020-08-07 14:43:17 +02:00
1978629d87 Custom Mutator readme (#495) 2020-08-07 14:21:12 +02:00
6b1ad311da Custom Mutator readme 2020-08-07 14:20:38 +02:00
5b06166144 reverse cmplog temps 2020-08-07 11:10:17 +02:00
a0fab35bbf temporary fix for cmplog 2020-08-07 10:42:33 +02:00
420b202124 temporary fix for cmplog 2020-08-07 10:37:28 +02:00
fb14e55cc9 fix -N description 2020-08-07 09:05:40 +02:00
e2434cf8c6 remove datalen in havoc 2020-08-06 23:27:50 +02:00
d94681186d Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev 2020-08-06 20:26:06 +02:00
58a710d192 check cmp map null in sancov trace switch 2020-08-06 20:25:57 +02:00
716eb226b2 enforce __afl_cmp_map points to a dummy by default 2020-08-06 20:06:57 +02:00
cb3631a322 add cmplog map to aflpp driver 2020-08-06 20:02:47 +02:00
bd1d148f83 Merge pull request #494 from AFLplusplus/test
aflpp driver fix for early callers
2020-08-06 19:43:56 +02:00
7e0c9a36ef update persistent doc 2020-08-06 19:42:33 +02:00
bbfff7d472 fix 2020-08-06 19:37:13 +02:00
e048d95660 fix 2020-08-06 19:13:04 +02:00
970d75d681 fix 2020-08-06 19:07:52 +02:00
51f3a81037 fix 2020-08-06 19:05:57 +02:00
8190436f8f fix 2020-08-06 18:51:16 +02:00
08bcaa135f dummy mem test 2020-08-06 18:44:12 +02:00
c4e5f75728 install dynamic list 2020-08-05 15:02:21 +02:00
1064c7114e code format 2020-08-05 13:30:43 +02:00
0281872ddf remove shared_linking check 2020-08-05 13:05:30 +02:00
c6bf23377d update dynamic list 2020-08-05 12:59:48 +02:00
2d650f8c22 merge conflict 2020-08-05 12:54:52 +02:00
19631851f6 dynamic symbols export for dlopen 2020-08-05 12:53:46 +02:00
f30ca1476c fix short write 2020-08-05 11:17:15 +02:00
0712d44cbc minor fixes 2020-08-05 10:42:41 +02:00
15f3210d93 Merge pull request #492 from dozernz/stable
Fix support for mips in qemu mode
2020-08-05 10:36:48 +02:00
9864d9c189 Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev 2020-08-05 10:28:00 +02:00
bd36aac60a remove shared_linking case 2020-08-05 10:27:48 +02:00
4a859aff70 travis fixes 2020-08-05 10:25:53 +02:00
8fc727e597 port patch from https://github.com/google/AFL/pull/112 2020-08-05 10:01:58 +02:00
585ec04503 silly rand_below_datalen fix 2020-08-05 09:21:39 +02:00
a1c93f24d4 Fix support for mips in qemu mode 2020-08-05 12:19:43 +12:00
f6c89ec3a9 workaround travis 2020-08-05 01:29:05 +02:00
3d8f054580 readme fix 2020-08-05 01:19:31 +02:00
6d364dd2cb add sancov-like allow/denylist instrument feature 2020-08-05 01:13:51 +02:00
8ed6207b5c update honggfuzz custom mutator. make update is all it takes to stay current :) 2020-08-04 23:33:35 +02:00
c8354d7516 new rand mode for data offsets that prefer low offset values 2020-08-04 23:22:42 +02:00
79f873a597 posix compatible sourcing 2020-08-04 21:57:56 +02:00
8850e1a5bf chmod for testcase 2020-08-04 21:47:21 +02:00
194188fe56 split up testcases 2020-08-04 21:33:29 +02:00
cc74efa35e fix test for nixos 2020-08-04 15:10:20 +02:00
e7f2770275 Merge pull request #490 from AFLplusplus/dev
makefile fix?
2020-08-04 14:35:33 +02:00
af277a0b56 makefile fix? 2020-08-04 14:20:38 +02:00
4163f47e09 fix example 2020-08-04 13:54:13 +02:00
b2aa8b03d9 Merge pull request #488 from AFLplusplus/dev
Dev
2020-08-04 13:30:08 +02:00
e1d20706ca fix cmplog with lto 2020-08-04 13:17:53 +02:00
76888fdf59 bugfix libtokencap Makefile 2020-08-03 23:11:58 +02:00
e6e38d1703 give document edge id a unique id per module 2020-08-03 21:25:32 +02:00
44060590b4 Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev 2020-08-03 20:51:00 +02:00
38bed607d1 code format 2020-08-03 20:50:47 +02:00
ed63364a77 add touch shmem in forkserver, add binary library and patches.txt for easy testing 2020-08-03 18:13:06 +02:00
55bd24b0c7 Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-08-03 15:17:47 +02:00
f18c2eb8ae no support for DragonFlyBSD. 2020-08-03 15:16:46 +02:00
898353c87a enforce no built-ins for lto 2020-08-03 14:17:51 +02:00
d5d8d664d0 Merge pull request #477 from AFLplusplus/dev
Push to stable
2020-08-03 13:39:55 +02:00
409e4ae945 fix expand havoc for ..._only modes 2020-08-03 13:13:32 +02:00
f335c48686 better PS1 in dockerfile 2020-08-03 11:50:10 +02:00
9d82c3cf5e test for llvm cmplog 2020-08-03 11:30:34 +02:00
491cee669f fix #483 2020-08-03 11:15:12 +02:00
e0d1529061 edge id documentation example for sancov 2020-08-03 10:03:45 +02:00
1cddd51662 refactoring debug/be_quiet, fatal on dont_optimize and instrument_file 2020-08-02 22:28:16 +02:00
6041b1c486 fix LTO document id feature, warnings for INSTRUMENT_FILE 2020-08-02 15:00:49 +02:00
349fed3fcd warn on old llvm 2020-08-02 11:08:47 +02:00
b708cf7d45 fix lto single block and no zero 2020-08-01 19:43:29 +02:00
a267ff1ab5 better LTO mode detection warnings 2020-08-01 18:31:11 +02:00
8e0c776137 more flexible use of shmem persistent vars 2020-08-01 17:58:37 +02:00
4512377fa1 compile persistent mode without afl 2020-08-01 17:39:27 +02:00
9439ba1dac document env var 2020-08-01 16:32:01 +02:00
9c9c4a6b2b remove forced -shared for lto, seems unneeded 2020-08-01 14:27:18 +02:00
6efe51a8a7 improve chances to compile with lto 2020-08-01 13:56:10 +02:00
593940c39a refer to llvm 12 for partial instrumentation for PCGUARD 2020-07-31 20:20:30 +02:00
8ea19d4266 easier float test 2020-07-31 19:37:05 +02:00
b7bcc50c61 reenable cpu tests in test.sh 2020-07-31 19:09:04 +02:00
e939677726 fix travis 2020-07-31 18:43:30 +02:00
ca17ec3fe9 Merge pull request #481 from devnexen/haiku_settings
Haiku set explicitly to performance.
2020-07-31 18:42:06 +02:00
54d9668580 Haiku set explicitly to performance.
No command line to set through afl-system-config (the only one is a GUI).
2020-07-31 17:27:01 +00:00
16b674c652 fix find free cpu 2020-07-31 18:35:43 +02:00
25ad992c62 fix travis 2020-07-31 18:26:18 +02:00
37f1b7cddb fix travis 2020-07-31 18:18:05 +02:00
729445b64f Bind cpu (#480)
* silence compiletime warning

* refactored cpu binding

* formatted code
2020-07-31 18:17:03 +02:00
185f443659 add LTO AFL_LLVM_DOCUMENT_IDS feature 2020-07-31 17:53:01 +02:00
c101a3f5ab readme update 2020-07-31 17:23:31 +02:00
cf9cb73afe Review as requested (watch out for XXX) 2020-07-31 16:27:50 +02:00
071fcac430 building docker from cloned repo 2020-07-31 15:55:48 +02:00
a74ec89461 fixed dockerfile 2020-07-31 15:37:50 +02:00
630d2a934b less gotos 2020-07-31 14:36:58 +02:00
d5758c138b update todo 2020-07-31 11:04:22 +02:00
149b0021b7 improve README 2020-07-31 10:49:25 +02:00
68f46f6178 remove travis debug 2020-07-31 10:44:54 +02:00
cd576fa59d fixes 2020-07-31 10:42:43 +02:00
320f26d26f add -b option to afl-fuzz 2020-07-30 19:00:41 +02:00
c661587128 cdecl void 2020-07-30 18:06:25 +02:00
486e5365d9 fix float splitting if not on a tty 2020-07-30 18:01:18 +02:00
8e809d8593 added NULL check 2020-07-30 17:51:32 +02:00
ea9ba53cdb fix oob reads, code-format 2020-07-30 17:09:22 +02:00
1ba48a5ba0 :Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-07-30 17:02:13 +02:00
7cb00b69f0 warnings reenabled 2020-07-30 17:01:57 +02:00
cbe8f0a9d0 cleanup messages in floating point test case 2020-07-30 17:00:10 +02:00
da8b464e67 fix test.sh 2020-07-30 16:07:47 +02:00
13350bf22f Merge branch 'dev' of https://github.com/AFLplusplus/AFLplusplus into dev 2020-07-30 13:48:16 +02:00
5ce55d87ec make travis green again (floating point testcase), but all laf testcases need AFL_DEBUG=1 (that is another bug) 2020-07-30 13:46:52 +02:00
fc401f1acc fix post process check 2020-07-30 11:51:13 +02:00
fe39e4dfdf remove unneeded stuff 2020-07-30 11:30:37 +02:00
49b77207dd aflpp driver cpp -> c 2020-07-30 11:11:30 +02:00
35a448ee92 enhance for custom trim buffer 2020-07-30 09:20:22 +02:00
3f9f00a798 Merge pull request #460 from rish9101/dev
Add post-process functionality in write_with_gap
2020-07-30 09:15:42 +02:00
ffe5619a9d fix snapshot include 2020-07-29 14:30:22 +02:00
3b194e1690 Merge pull request #476 from AFLplusplus/new_lkm
New snapshot lkm support
2020-07-29 14:15:39 +02:00
45b6508339 fix typo 2020-07-29 13:00:55 +02:00
22921c493f improve docs, enable laf compare if float is set 2020-07-29 12:58:02 +02:00
f32811922e minor opt 2020-07-29 11:56:38 +02:00
6cfa27d78a remove dead code, code-format 2020-07-29 11:47:32 +02:00
8e3ca8eaa9 Merge pull request #473 from AFLplusplus/text_inputs
add splice
2020-07-29 11:41:54 +02:00
4550613f58 Merge branch 'dev' into text_inputs 2020-07-29 11:39:00 +02:00
015fde3703 fix ffmpeg lto example 2020-07-28 22:25:48 +02:00
827ecd61f6 add new example/guideline 2020-07-28 22:13:37 +02:00
565da10a8f Minor change to write_with_gap 2020-07-29 01:05:05 +05:30
d64c0e8887 lkm new api 2020-07-28 16:31:07 +02:00
0b8c44cbb1 add FAQ 2020-07-28 16:29:47 +02:00
a22f4dd1ac new snapshot api 2020-07-28 16:13:32 +02:00
952e5b47eb allow custom mut with mopt if -L is -1 2020-07-28 16:02:15 +02:00
b3f5b566b0 update contribute doc 2020-07-28 13:29:50 +02:00
0b3332d579 finalize new readme 2020-07-28 13:27:18 +02:00
a76e375d5c import lost PR when switching to stable, updated todo 2020-07-28 11:05:00 +02:00
8b21c2e472 unnice 2020-07-28 09:55:32 +02:00
23718e5198 forgot getopt 2020-07-28 09:52:28 +02:00
031aa240bc unnice2 2020-07-27 23:09:26 +02:00
7944009a65 unrenice 2020-07-27 13:07:55 +02:00
4eb06bb54b Merge pull request #465 from electricworry/wine-fix
Fixes AFLplusplus/AFLplusplus#464 winepath path translation for .cur_…
2020-07-27 10:41:07 +02:00
bd5308d839 fix readme 2020-07-27 10:27:57 +02:00
b508532c78 format 2020-07-27 10:09:00 +02:00
fb9888a068 Merge pull request #468 from Kirin-say/fix_qemu_mode
Fix bug in qemu mode
2020-07-27 10:07:12 +02:00
11f25747a9 child renice 2020-07-27 09:39:15 +02:00
8ebed3471f fix readme 2020-07-27 09:30:42 +02:00
85684cd8b7 fix readme 2020-07-27 09:25:30 +02:00
2585a33005 fix readme 2020-07-27 09:21:07 +02:00
1bbeef48e1 update readme, renice -20 2020-07-27 09:10:48 +02:00
7f3317110e update new readme 2020-07-27 00:16:09 +02:00
298ff5c7d0 update new readme 2020-07-27 00:14:28 +02:00
c3f65bff5b update new readme 2020-07-27 00:13:03 +02:00
2323c30b5b fix honggfuzz dict 2020-07-26 22:05:14 +02:00
80f4b32f0b new readme --- in progress 2020-07-26 18:41:54 +02:00
16e362d2b9 add last 60s exec/s stat 2020-07-26 15:55:03 +02:00
23da490f26 Merge pull request #469 from devnexen/haiku_gcc_build_fix
gcc plugin forcing USEMMAP on Haiku
2020-07-26 15:30:47 +02:00
ff107714f1 remove test input mutation in havoc 2020-07-26 15:00:44 +02:00
7e4703c328 Fix typo for afl_custom_deinit (#470) 2020-07-26 14:10:24 +02:00
ae41cedafe gcc plugin forcing USEMMAP on Haiku 2020-07-25 16:14:27 +00:00
a879f72131 fix 2020-07-25 17:38:40 +02:00
131df8bec9 try to fix travis 2020-07-25 16:39:36 +02:00
89557d1607 fix bug in qemu mode 2020-07-25 00:36:36 +08:00
7959808384 llvm12 support 2020-07-24 16:39:50 +02:00
ecb0601bc1 massage chances 2020-07-24 13:42:39 +02:00
30c0991543 better text mutation 2020-07-24 13:26:07 +02:00
9cddbc0420 add -F option to sync to foreign fuzzer queues 2020-07-24 12:26:52 +02:00
2fa31dab60 Remove reduntant copying from write_with_gap function 2020-07-23 23:48:26 +05:30
4898db80cb Add post-process functionality in write_with_gap 2020-07-23 23:16:04 +05:30
aa3856261d fix warning 2020-07-23 17:17:44 +02:00
3e04dbd5a1 no cycle on expand 2020-07-23 16:43:25 +02:00
72b46a07d6 added honggfuzz custom mutator :) 2020-07-23 15:58:13 +02:00
2ba88dcd8a skip large files, dont bail 2020-07-23 14:17:29 +02:00
1ddb70e0d9 fix compilation 2020-07-23 05:08:20 +02:00
024a88a6bb Fixes AFLplusplus/AFLplusplus#464 winepath path translation for .cur_input. 2020-07-22 20:19:55 +01:00
af10c05ac3 ascii redqueen 2020-07-22 18:22:31 +02:00
a46a733dbe fix bugs 2020-07-22 17:04:57 +02:00
b015e4f07a epand havoc now env 2020-07-22 16:15:16 +02:00
44928a0265 write expand havoc value to fuzzer_stats 2020-07-22 02:41:19 +02:00
d90328f6be Allow the custom mutator to generate larger trimmed data (#463) 2020-07-22 00:00:21 +02:00
ce9b4698fe added andrea's splicing, added cycle_schedules 2020-07-21 20:53:51 +02:00
9a33a29b4a Merge pull request #462 from AFLplusplus/dev
Dev
2020-07-21 13:17:29 +02:00
b6e65f9882 Merge pull request #461 from AFLplusplus/new_splicing
New splicing
2020-07-21 13:15:59 +02:00
6c163910ee debug test for rng 2020-07-20 12:08:31 +02:00
9151cb9ba2 update link 2020-07-20 11:40:34 +02:00
204ae75d7b add superion custom mutator link 2020-07-20 10:31:07 +02:00
f2d9b0096e Merge pull request #458 from h1994st/dev
Allow the custom trim function to return 0 bytes
2020-07-20 10:19:14 +02:00
67d2e6319b Skip the empty test case generated by the custom trimming 2020-07-18 23:20:32 -04:00
5e10f660e8 Allow the fuzzer to write empty buffer (i.e., len == 0) to the file 2020-07-18 02:16:04 -04:00
0da0b5cba0 Merge pull request #456 from h1994st/dev
Fix typo
2020-07-17 08:41:26 +02:00
67d7c364f6 Fix typo 2020-07-16 21:59:50 -04:00
67d58e2437 doc updates 2020-07-16 20:24:01 +02:00
c2b04bdf6c queue buffer and new splice havoc mutation 2020-07-16 14:32:41 +02:00
6513bca07e Update post_library_gif.so.c (#454) 2020-07-16 11:47:36 +02:00
0b0366d9b4 removed debug print and code format 2020-07-16 02:27:07 +02:00
f465a75b65 added initial defork example 2020-07-16 02:17:05 +02:00
4314e59af9 code format 2020-07-16 02:03:52 +02:00
a84c958647 fixed mem leak in redqueen 2020-07-16 01:00:39 +02:00
1ec2615a3e tiny fixes 2020-07-16 00:53:08 +02:00
2077309c8d fix afl-frida 2020-07-16 00:24:37 +02:00
08d3169df4 fix afl-frida 2020-07-15 16:58:40 +02:00
3cc0445e27 Merge branch 'dev' of github.com:vanhauser-thc/AFLplusplus into dev 2020-07-15 10:35:51 +02:00
ee77fe4094 improve len encoding in redqueen 2020-07-15 10:35:38 +02:00
133dfc8b69 update documentation 2020-07-15 10:32:07 +02:00
a8726b8254 ensure afl-frida uses persistent mode 2020-07-15 00:08:38 +02:00
c5963f707c make fuzzing of test-floatingpoint reproducible 2020-07-14 23:42:47 +02:00
383b280531 added frida gum extension 2020-07-14 23:26:11 +02:00
95276f7da6 test float splitting increase timeout to 30 seconds 2020-07-13 23:17:21 +02:00
e1d4621796 Merge pull request #450 from devnexen/haiku_build_fix
Haiku build fix. librt is necessary for Linux primarly and SunOS
2020-07-13 18:06:38 +02:00
e137b40eb5 Haiku build fix. librt is necessary for Linux primarly and SunOS 2020-07-13 16:22:18 +00:00
4d929f80fb fix for laf intel float split not enabled if not not on a tty 2020-07-13 17:57:02 +02:00
6b79e1f76d test.sh: FP fuzzing: check for crashes 2020-07-13 11:27:08 +02:00
5a26656ea1 add floating point test cases. One for fuzzing (test-floatingpoint.c) and one for testing all cases with the instrumented program (test-fp_cases.c) 2020-07-13 10:35:43 +02:00
abb0d47985 little untracer enhancements 2020-07-12 23:53:29 +02:00
b126a5d5a8 LTO: autodict default, instrim disabled 2020-07-12 13:44:25 +02:00
571031a467 fix several cases in floating point comparison splitting 2020-07-11 00:56:35 +02:00
2981f2025f increase shm for travis 2020-07-09 23:14:33 +02:00
c3a6065a21 shm + mem info in travis 2020-07-09 23:02:04 +02:00
60bb1afc72 code format 2020-07-09 21:32:06 +02:00
84a320f834 skip -fuse-ld parameters when in LTO mode 2020-07-09 21:31:15 +02:00
88bd460100 Merge pull request #449 from devnexen/illumos_typo
illumos littlefixes: little typo for cpu binding and
2020-07-09 19:05:05 +02:00
90adc2cb85 illumos littlefixes: little typo for cpu binding and
even tough gcc plugin less good than LLVM, clang
is more buggy on this os.
2020-07-09 15:43:05 +01:00
7c8d823396 dockerfile updates 2020-07-09 12:07:29 +02:00
83790d65af eliminate race condition for cpu affinity on -M/-S 2020-07-08 11:16:39 +02:00
70bd0f799d fix afl-whatsup if fuzzer_stats is still empty 2020-07-08 09:39:26 +02:00
cbe029664e fix issue #446 2020-07-07 12:59:00 +02:00
cade0214db Merge pull request #445 from toralf/fix-y-axis-of-low_freq.png
afl-plot: scale y-axis of low_freq.png with integers
2020-07-06 22:29:23 +02:00
2f5cdb72c8 afl-plot: set xlabel to show that times are in UTC
Signed-off-by: Toralf Förster <toralf.foerster@gmx.de>
2020-07-06 19:23:13 +02:00
0aed549df1 warn rather than fail if AFL_MAP_SIZE is set and not understood by instrumenter 2020-07-06 14:11:21 +02:00
75fa1ac3b0 warn rather than fail if AFL_MAP_SIZE is set and not understood by instrumenter 2020-07-06 14:10:14 +02:00
b5a00312e0 rtf.dict: make it more complete (and unique) and fix some entries 2020-07-06 10:27:48 +02:00
37697127dc afl-plot: scale y-axis of low_freq.png with integers
Signed-off-by: Toralf Förster <toralf.foerster@gmx.de>
2020-07-05 15:35:24 +02:00
8acc8b5389 Merge pull request #441 from bmwiedemann/man
Fix generation of afl-system-config.8
2020-07-05 13:53:51 +02:00
8644c42482 check for enough plot data 2020-07-05 13:48:14 +02:00
20e63078f0 Fix generation of afl-system-config.8
and afl-whatsup.8

Without this patch, afl-system-config.8 varied between build hosts
because it contained lines such as
 ./afl-system-config: line 30: sysctl: command not found
 ./afl-system-config: line 31: /sys/kernel/mm/transparent_hugepage/enabled: Permission denied
 It is recommended to boot the kernel with lots of security off

See https://reproducible-builds.org/ for why this matters.

afl-system-config.8 is generated by the %.8 target in GNUmakefile
that calls commands with -hh to fill the OPTIONS section of man-pages.

This PR was done while working on reproducible builds for openSUSE.
2020-07-05 13:41:50 +02:00
95fd080ca1 code format 2020-07-05 11:08:22 +02:00
7d0af01d8b fix rtf.dict 2020-07-05 11:05:33 +02:00
0f0230b068 Merge pull request #437 from devnexen/fbsd_build_fix_llvm_mode
llvm mode shared segment fix for FreeBSD.
2020-07-05 11:01:58 +02:00
869c602b99 Merge pull request #438 from EliaGeretto/fix-typo
Fix typo in compiler wrapper for LLVM instrumentation
2020-07-05 11:01:24 +02:00
3144f72e1c Merge pull request #440 from devnexen/libdislocator_solaris_upd
libdislocator: hugepage enabled for illumos too.
2020-07-05 11:00:45 +02:00
147b0a151c fix laf-intel/compare-transform-pass for 32-Bit 2020-07-04 17:34:03 +02:00
29102d6bf1 libdislocator: hugepage enabled for illumos too. 2020-07-04 12:36:53 +01:00
4fd145c52e llvm_mode: Fix typo in compiler wrapper 2020-07-03 18:37:53 +02:00
e6d4d29af5 llvm mode shared segment fix for FreeBSD.
MAP_EXCL|MAP_FIXED is a (genuine) equivalent to Linux's MAP_FIXED_NOREPLACE.
2020-07-03 15:21:33 +01:00
139665c01d ubsan options 2020-07-03 10:20:10 +02:00
509b991607 Merge pull request #435 from devnexen/libtokencap_solaris
libtokencap illumos/solaris support proposal.
2020-07-03 09:45:19 +02:00
c671ecb511 Fix map list iteration. 2020-07-02 10:23:56 +01:00
1aa7c87ea8 libtokencap illumos/solaris support proposal. 2020-07-02 07:10:43 +01:00
00abb999e3 v2.66d init 2020-07-01 18:24:00 +02:00
fc5cfc6cb3 Merge pull request #428 from AFLplusplus/dev
Dev
2020-06-29 18:36:06 +02:00
25c3a29004 Merge pull request #414 from AFLplusplus/dev
work around for llvm 11 bug
2020-06-17 23:56:17 +02:00
118cc88429 Merge pull request #412 from AFLplusplus/dev
fix displayed schedule
2020-06-17 16:46:54 +02:00
0dc9967984 Merge pull request #403 from AFLplusplus/dev
push to master
2020-06-17 15:05:14 +02:00
349 changed files with 38989 additions and 16259 deletions

View File

@ -29,14 +29,15 @@ CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN")
if CLANG_FORMAT_BIN is None:
o = 0
try:
p = subprocess.Popen(["clang-format-10", "--version"], stdout=subprocess.PIPE)
p = subprocess.Popen(["clang-format-11", "--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:
print ("clang-format-10 is needed. Aborted.")
print ("clang-format-11 is needed. Aborted.")
exit(1)
#if o < 7:
# if subprocess.call(['which', 'clang-format-7'], stdout=subprocess.PIPE) == 0:
@ -45,13 +46,13 @@ if CLANG_FORMAT_BIN is None:
# CLANG_FORMAT_BIN = 'clang-format-8'
# elif subprocess.call(['which', 'clang-format-9'], stdout=subprocess.PIPE) == 0:
# CLANG_FORMAT_BIN = 'clang-format-9'
# elif subprocess.call(['which', 'clang-format-10'], stdout=subprocess.PIPE) == 0:
# CLANG_FORMAT_BIN = 'clang-format-10'
# elif subprocess.call(['which', 'clang-format-11'], stdout=subprocess.PIPE) == 0:
# CLANG_FORMAT_BIN = 'clang-format-11'
# else:
# print ("clang-format 7 or above is needed. Aborted.")
# exit(1)
else:
CLANG_FORMAT_BIN = 'clang-format-10'
CLANG_FORMAT_BIN = 'clang-format-11'
COLUMN_LIMIT = 80
for line in fmt.split("\n"):

65
.dockerignore Normal file
View 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

33
.gitignore vendored
View File

@ -1,8 +1,19 @@
.test
.test2
.sync_tmp
.vscode
*.o
*.so
*.swp
*.pyc
*.dSYM
as
a.out
ld
in
out
core*
compile_commands.json
afl-analyze
afl-as
afl-clang
@ -31,7 +42,10 @@ afl-clang-lto++.8
afl-cmin.8
afl-cmin.bash.8
afl-fuzz.8
afl-c++.8
afl-cc.8
afl-gcc.8
afl-g++.8
afl-gcc-fast.8
afl-g++-fast.8
afl-gotcpu.8
@ -40,14 +54,18 @@ afl-showmap.8
afl-system-config.8
afl-tmin.8
afl-whatsup.8
afl-c++
afl-cc
afl-lto
afl-lto++
afl-lto++.8
afl-lto.8
qemu_mode/libcompcov/compcovtest
as
ld
qemu_mode/qemu-*
qemu_mode/qemuafl
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 +73,12 @@ 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
examples/aflpp_driver/libAFLDriver.a
examples/aflpp_driver/libAFLQemuDriver.a
libAFLDriver.a
libAFLQemuDriver.a
test/.afl_performance

8
.gitmodules vendored
View File

@ -1,3 +1,9 @@
[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/grammar_mutator
url = https://github.com/AFLplusplus/Grammar-Mutator
[submodule "qemu_mode/qemuafl"]
path = qemu_mode/qemuafl
url = https://github.com/AFLplusplus/qemuafl

View File

@ -4,27 +4,27 @@ sudo: required
branches:
only:
- master
- stable
- dev
matrix:
include:
# - os: linux # focal errors every run with a timeout while installing packages
# dist: focal
# env: NAME="focal-amd64" MODERN="yes" GCC="9"
#- os: linux # again disabled because fetching packages times out very often :(
# dist: focal
# env: NAME="focal-amd64" MODERN="yes" GCC="9"
- os: linux
dist: bionic
env: NAME="bionic-amd64" MODERN="yes" GCC="7"
- os: linux
dist: xenial
env: NAME="xenial-amd64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0"
- os: linux
dist: trusty
env: NAME="trusty-amd64" MODERN="no" GCC="4.8"
# - os: linux # until travis can fix this!
# dist: xenial
# arch: arm64
# env: NAME="xenial-arm64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0" AFL_NO_X86="1" CPU_TARGET="aarch64"
# - os: linux # disabled: fatal: unable to access 'https://git.qemu.org/git/capstone/': gnutls_handshake() failed: Handshake failed
# dist: trusty
# env: NAME="trusty-amd64" MODERN="no" GCC="4.8"
- os: linux # until travis can fix this!
dist: xenial
arch: arm64
env: NAME="xenial-arm64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0" AFL_NO_X86="1" CPU_TARGET="aarch64"
# - os: osx
# osx_image: xcode11.2
# env: NAME="osx" HOMEBREW_NO_ANALYTICS="1" LINK="http://releases.llvm.org/9.0.0/" NAME="clang+llvm-9.0.0-x86_64-darwin-apple"
@ -51,8 +51,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

View File

@ -101,7 +101,7 @@ cc_binary_host {
],
srcs: [
"llvm_mode/afl-clang-fast.c",
"src/afl-cc.c",
],
}
@ -119,7 +119,7 @@ cc_binary_host {
],
srcs: [
"llvm_mode/afl-clang-fast.c",
"src/afl-cc.c",
],
}
@ -136,6 +136,6 @@ cc_library_static {
],
srcs: [
"llvm_mode/afl-llvm-rt.o.c",
"instrumentation/afl-llvm-rt.o.c",
],
}

View File

@ -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.
```
@ -14,9 +16,9 @@ project, or added a file in a directory we already format, otherwise run:
```
Regarding the coding style, please follow the AFL style.
No camel case at all and use the AFL's macros wherever possible
No camel case at all and use 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 generic as possible.

View File

@ -2,16 +2,16 @@
# This Dockerfile for AFLplusplus uses Ubuntu 20.04 focal and
# installs LLVM 11 from llvm.org for afl-clang-lto support :-)
# It also installs gcc/g++ 10 from the Ubuntu development platform
# has focal has gcc-10 but not g++-10 ...
# since focal has gcc-10 but not g++-10 ...
#
FROM ubuntu:20.04
MAINTAINER afl++ team <afl@aflplus.plus>
FROM ubuntu:20.04 AS aflplusplus
LABEL "maintainer"="afl++ team <afl@aflplus.plus>"
LABEL "about"="AFLplusplus docker image"
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get upgrade -y && \
RUN apt-get update && \
apt-get -y install --no-install-suggests --no-install-recommends \
automake \
bison flex \
@ -20,43 +20,48 @@ 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
libpixman-1-dev \
gnuplot-nox \
&& rm -rf /var/lib/apt/lists/*
RUN echo deb http://apt.llvm.org/focal/ llvm-toolchain-focal 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 && \
RUN echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main" >> /etc/apt/sources.list && \
wget -qO - 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 && \
apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 1E9377A2BA9EF27F
RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y gcc-10 g++-10 gcc-10-plugin-dev gcc-10-multilib \
libc++-10-dev gdb lcov
RUN apt-get install -y clang-11 clang-tools-11 libc++1-11 libc++-11-dev \
RUN apt-get update && apt-get full-upgrade -y && \
apt-get -y install --no-install-suggests --no-install-recommends \
gcc-10 g++-10 gcc-10-plugin-dev gcc-10-multilib gdb lcov \
clang-11 clang-tools-11 libc++1-11 libc++-11-dev \
libc++abi1-11 libc++abi-11-dev libclang1-11 libclang-11-dev \
libclang-common-11-dev libclang-cpp11 libclang-cpp11-dev liblld-11 \
liblld-11-dev liblldb-11 liblldb-11-dev libllvm11 libomp-11-dev \
libomp5-11 lld-11 lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools
libomp5-11 lld-11 lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools \
&& rm -rf /var/lib/apt/lists/*
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 0
RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 0
RUN rm -rf /var/cache/apt/archives/*
ENV LLVM_CONFIG=llvm-config-11
ENV AFL_SKIP_CPUFREQ=1
ARG CC=gcc-10
ARG CXX=g++-10
ARG LLVM_CONFIG=llvm-config-11
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/AFLplusplus/AFLplusplus
RUN cd AFLplusplus && export REAL_CXX=g++-10 && make distrib && \
make install && make clean
COPY . /AFLplusplus
WORKDIR /AFLplusplus
RUN git clone https://github.com/vanhauser-thc/afl-cov afl-cov
RUN cd afl-cov && make install
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
RUN echo 'export PS1="[afl++]$PS1"' >> ~/.bashrc
ENV IS_DOCKER="1"
ENV AFL_SKIP_CPUFREQ=1
# Disabled until we have the container ready
#COPY --from=aflplusplus/afl-dyninst /usr/local/lib/libdyninstAPI_RT.so /usr/local/lib/libdyninstAPI_RT.so
#COPY --from=aflplusplus/afl-dyninst /afl-dyninst/libAflDyninst.so /usr/local/lib/libAflDyninst.so

View File

@ -31,11 +31,34 @@ VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f
# PROGS intentionally omit afl-as, which gets installed elsewhere.
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
PROGS = 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
ifdef NO_SPLICING
override CFLAGS += -DNO_SPLICING
endif
ifdef ASAN_BUILD
$(info Compiling ASAN version of binaries)
override CFLAGS+=$(ASAN_CFLAGS)
LDFLAGS+=$(ASAN_LDFLAGS)
endif
ifdef UBSAN_BUILD
$(info Compiling UBSAN version of binaries)
override CFLAGS += -fsanitize=undefined -fno-omit-frame-pointer
override LDFLAGS += -fsanitize=undefined
endif
ifdef MSAN_BUILD
$(info Compiling MSAN version of binaries)
CC := clang
override CFLAGS += -fsanitize=memory -fno-omit-frame-pointer
override LDFLAGS += -fsanitize=memory
endif
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"
CFLAGS_FLTO ?= -flto=full
@ -56,16 +79,20 @@ 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
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
@ -85,6 +112,11 @@ ifdef PROFILING
LDFLAGS += -pg
endif
ifdef INTROSPECTION
$(info Compiling with introspection documentation)
CFLAGS_OPT += -DINTROSPECTION=1
endif
ifneq "$(shell uname -m)" "x86_64"
ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386"
ifneq "$(shell uname -m)" "amd64"
@ -95,8 +127,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 +234,10 @@ else
endif
ifneq "$(filter Linux GNU%,$(shell uname))" ""
LDFLAGS += -ldl
ifndef DEBUG
override CFLAGS += -D_FORTIFY_SOURCE=2
endif
LDFLAGS += -ldl -lrt -lm
endif
ifneq "$(findstring FreeBSD, $(shell uname))" ""
@ -209,10 +250,9 @@ ifneq "$(findstring NetBSD, $(shell uname))" ""
LDFLAGS += -lpthread
endif
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
TEST_CC = afl-gcc
else
TEST_CC = afl-clang
ifneq "$(findstring OpenBSD, $(shell uname))" ""
override CFLAGS += -pthread
LDFLAGS += -lpthread
endif
COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h
@ -239,52 +279,65 @@ ifeq "$(shell command -v svn >/dev/null && svn proplist . 2>/dev/null && echo 1
endif
ifeq "$(shell echo 'int main() { return 0;}' | $(CC) $(CFLAGS) -fsanitize=address -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1"
ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer -DASAN_BUILD
ASAN_LDFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer
endif
ifdef ASAN_BUILD
$(info Compiling ASAN version of binaries)
override CFLAGS+=$(ASAN_CFLAGS)
LDFLAGS+=$(ASAN_LDFLAGS)
endif
ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1"
SHMAT_OK=1
else
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
.PHONY: all
all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_build all_done
.PHONY: llvm
llvm:
-$(MAKE) -f GNUmakefile.llvm
@test -e afl-cc || { echo "[-] Compiling afl-cc failed. You seem not to have a working compiler." ; exit 1; }
.PHONY: gcc_plugin
gcc_plugin:
-$(MAKE) -f GNUmakefile.gcc_plugin
.PHONY: man
man: $(MANPAGES)
.PHONY: test
test: tests
.PHONY: tests
tests: source-only
@cd test ; ./test.sh
@cd test ; ./test-all.sh
@rm -f test/errors
.PHONY: performance-tests
performance-tests: performance-test
.PHONY: test-performance
test-performance: performance-test
.PHONY: performance-test
performance-test: source-only
@cd test ; ./test-performance.sh
# hint: make targets are also listed in the top level README.md
.PHONY: help
help:
@echo "HELP --- the following make targets exist:"
@echo "=========================================="
@echo "all: just the main afl++ binaries"
@echo "binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap"
@echo "source-only: everything for source code fuzzing: llvm_mode, gcc_plugin, libdislocator, libtokencap"
@echo "source-only: everything for source code fuzzing: gcc_plugin, libdislocator, libtokencap"
@echo "distrib: everything (for both binary-only and source code fuzzing)"
@echo "man: creates simple man pages from the help option of the programs"
@echo "install: installs everything you have compiled with the build option above"
@ -302,13 +355,18 @@ 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 INTROSPECTION - compile afl-fuzz with mutation introspection
@echo NO_PYTHON - disable python support
@echo NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing
@echo AFL_NO_X86 - if compiling on non-intel/amd platforms
@echo "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian)"
@echo "=========================================="
@echo e.g.: make ASAN_BUILD=1
.PHONY: test_x86
ifndef AFL_NO_X86
test_x86:
@echo "[*] Checking for the default compiler cc..."
@type $(CC) >/dev/null || ( echo; echo "Oops, looks like there is no compiler '"$(CC)"' in your path."; echo; echo "Don't panic! You can restart with '"$(_)" CC=<yourCcompiler>'."; echo; exit 1 )
@ -317,53 +375,38 @@ test_x86:
@echo "[*] Checking for the ability to compile x86 code..."
@echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) $(CFLAGS) -w -x c - -o .test1 || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 )
@rm -f .test1
else
test_x86:
@echo "[!] Note: skipping x86 compilation checks (AFL_NO_X86 set)."
endif
.PHONY: test_shm
ifeq "$(SHMAT_OK)" "1"
test_shm:
@echo "[+] shmat seems to be working."
@rm -f .test2
else
test_shm:
@echo "[-] shmat seems not to be working, switching to mmap implementation"
endif
.PHONY: test_python
ifeq "$(PYTHON_OK)" "1"
test_python:
@rm -f .test 2> /dev/null
@echo "[+] $(PYTHON_VERSION) support seems to be working."
else
test_python:
@echo "[-] You seem to need to install the package python3-dev, python2-dev or python-dev (and perhaps python[23]-apt), but it is optional so we continue"
endif
.PHONY: ready
ready:
@echo "[+] Everything seems to be working, ready to compile."
afl-gcc: src/afl-gcc.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) 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)
ln -sf afl-as as
@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
@ -378,7 +421,7 @@ 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
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) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) -lm
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)
@ -392,9 +435,11 @@ afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o src/afl-perf
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)
.PHONY: document
document: afl-fuzz-document
# 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
afl-fuzz-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)
test/unittests/unit_maybe_alloc.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_maybe_alloc.c $(AFL_FUZZ_FILES)
@ -432,126 +477,131 @@ 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
./test/unittests/unit_preallocable
.PHONY: unit_clean
unit_clean:
@rm -f ./test/unittests/unit_preallocable ./test/unittests/unit_list ./test/unittests/unit_maybe_alloc test/unittests/*.o
.PHONY: unit
ifneq "$(shell uname)" "Darwin"
unit: unit_maybe_alloc unit_preallocable unit_list unit_clean unit_rand unit_hash
unit: unit_maybe_alloc unit_preallocable unit_list unit_clean unit_rand unit_hash
else
unit:
@echo [-] unit tests are skipped on Darwin \(lacks GNU linker feature --wrap\)
endif
.PHONY: code-format
code-format:
./.custom-format.py -i src/*.c
./.custom-format.py -i include/*.h
./.custom-format.py -i libdislocator/*.c
./.custom-format.py -i libtokencap/*.c
./.custom-format.py -i llvm_mode/*.c
./.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/*.cc
./.custom-format.py -i custom_mutators/*/*.c
./.custom-format.py -i custom_mutators/*/*.h
./.custom-format.py -i examples/*/*.c
./.custom-format.py -i examples/*/*.h
./.custom-format.py -i instrumentation/*.h
./.custom-format.py -i instrumentation/*.cc
./.custom-format.py -i instrumentation/*.c
@#./.custom-format.py -i custom_mutators/*/*.c* # destroys libfuzzer :-(
@#./.custom-format.py -i custom_mutators/*/*.h # destroys honggfuzz :-(
./.custom-format.py -i utils/*/*.c*
./.custom-format.py -i utils/*/*.h
./.custom-format.py -i test/*.c
./.custom-format.py -i qemu_mode/patches/*.h
./.custom-format.py -i qemu_mode/libcompcov/*.c
./.custom-format.py -i qemu_mode/libcompcov/*.cc
./.custom-format.py -i qemu_mode/libcompcov/*.h
./.custom-format.py -i qbdi_mode/*.c
./.custom-format.py -i qbdi_mode/*.cpp
./.custom-format.py -i *.h
./.custom-format.py -i *.c
.PHONY: test_build
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 )
test_build: afl-cc afl-gcc afl-as afl-showmap
@echo "[*] Testing the CC wrapper afl-cc and its instrumentation output..."
@unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; 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
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
@echo "[+] All right, the instrumentation seems to be working!"
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation of afl-cc does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
@echo
@echo "[+] All right, the instrumentation of afl-cc seems to be working!"
@echo "[*] Testing the CC wrapper afl-gcc and its instrumentation output..."
@unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_INST_RATIO=100 AFL_PATH=. ./afl-gcc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-gcc failed"; 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
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation of afl-gcc does not seem to be behaving correctly!"; \
gcc -v 2>&1 | grep -q -- --with-as= && ( echo; echo "Gcc is configured not to use an external assembler with the -B option."; echo "See docs/INSTALL.md section 5 how to build a -B enabled gcc." ) || \
( echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue." ); echo; exit 0; fi
@echo
@echo "[+] All right, the instrumentation of afl-gcc seems to be working!"
else
test_build: afl-gcc afl-as afl-showmap
test_build: afl-cc afl-as afl-showmap
@echo "[!] Note: skipping build tests (you may need to use LLVM or QEMU mode)."
endif
.PHONY: all_done
all_done: test_build
@if [ ! "`type clang 2>/dev/null`" = "" ]; then echo "[+] LLVM users: see llvm_mode/README.md for a faster alternative to afl-gcc."; fi
@test -e afl-cc && echo "[+] Main compiler 'afl-cc' successfully built!" || { echo "[-] Main compiler 'afl-cc' failed to build, set up a working build environment first!" ; exit 1 ; }
@test -e cmplog-instructions-pass.so && echo "[+] LLVM mode for 'afl-cc' successfully built!" || echo "[-] LLVM mode for 'afl-cc' failed to build, likely you either don't have llvm installed, or you need to set LLVM_CONFIG, to point to e.g. llvm-config-11. See instrumentation/README.llvm.md how to do this. Highly recommended!"
@test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode for 'afl-cc' successfully built!" || echo "[-] LLVM LTO mode for 'afl-cc' failed to build, this would need LLVM 11+, see instrumentation/README.lto.md how to build it"
@test -e afl-gcc-pass.so && echo "[+] gcc_plugin for 'afl-cc' successfully built!" || echo "[-] gcc_plugin for 'afl-cc' failed to build, unless you really need it that is fine - or read instrumentation/README.gcc_plugin.md how to build it"
@echo "[+] All done! Be sure to review the README.md - it's pretty short and useful."
@if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi
@! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.md for advice.\033[0m\n" 2>/dev/null
.NOTPARALLEL: clean all
.PHONY: clean
clean:
rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-*
rm -rf out_dir qemu_mode/qemu-3.1.1 *.dSYM */*.dSYM
-$(MAKE) -C llvm_mode clean
-$(MAKE) -C gcc_plugin clean
$(MAKE) -C libdislocator clean
$(MAKE) -C libtokencap clean
$(MAKE) -C examples/afl_network_proxy clean
$(MAKE) -C examples/socket_fuzzing clean
$(MAKE) -C examples/argv_fuzzing clean
rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* afl-gcc afl-g++ afl-clang afl-clang++ test/unittests/unit_hash test/unittests/unit_rand
-$(MAKE) -f GNUmakefile.llvm clean
-$(MAKE) -f GNUmakefile.gcc_plugin clean
$(MAKE) -C utils/libdislocator clean
$(MAKE) -C utils/libtokencap clean
$(MAKE) -C utils/afl_network_proxy clean
$(MAKE) -C utils/socket_fuzzing clean
$(MAKE) -C utils/argv_fuzzing clean
$(MAKE) -C qemu_mode/unsigaction 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 qemu_mode/qemuafl/Makefile && $(MAKE) -C qemu_mode/qemuafl 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 qemu_mode/qemuafl
rm -rf unicorn_mode/unicornafl
endif
.PHONY: deepclean
deepclean: clean
rm -rf qemu_mode/qemu-3.1.1.tar.xz
rm -rf unicorn_mode/unicornafl
git reset --hard >/dev/null 2>&1 || true
rm -rf qemu_mode/qemuafl
# NEVER EVER ACTIVATE THAT!!!!! git reset --hard >/dev/null 2>&1 || true
.PHONY: distrib
distrib: all
-$(MAKE) -C llvm_mode
-$(MAKE) -C gcc_plugin
$(MAKE) -C libdislocator
$(MAKE) -C libtokencap
$(MAKE) -C examples/afl_network_proxy
$(MAKE) -C examples/socket_fuzzing
$(MAKE) -C examples/argv_fuzzing
-$(MAKE) -f GNUmakefile.llvm
-$(MAKE) -f GNUmakefile.gcc_plugin
$(MAKE) -C utils/libdislocator
$(MAKE) -C utils/libtokencap
$(MAKE) -C utils/aflpp_driver
$(MAKE) -C utils/afl_network_proxy
$(MAKE) -C utils/socket_fuzzing
$(MAKE) -C utils/argv_fuzzing
-cd qemu_mode && sh ./build_qemu_support.sh
cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh
-cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh
.PHONY: binary-only
binary-only: all
$(MAKE) -C libdislocator
$(MAKE) -C libtokencap
$(MAKE) -C examples/afl_network_proxy
$(MAKE) -C examples/socket_fuzzing
$(MAKE) -C examples/argv_fuzzing
$(MAKE) -C utils/libdislocator
$(MAKE) -C utils/libtokencap
$(MAKE) -C utils/afl_network_proxy
$(MAKE) -C utils/socket_fuzzing
$(MAKE) -C utils/argv_fuzzing
-cd qemu_mode && sh ./build_qemu_support.sh
cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh
-cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh
.PHONY: source-only
source-only: all
-$(MAKE) -C llvm_mode
-$(MAKE) -C gcc_plugin
$(MAKE) -C libdislocator
$(MAKE) -C libtokencap
#$(MAKE) -C examples/afl_network_proxy
#$(MAKE) -C examples/socket_fuzzing
#$(MAKE) -C examples/argv_fuzzing
-$(MAKE) -f GNUmakefile.llvm
-$(MAKE) -f GNUmakefile.gcc_plugin
$(MAKE) -C utils/libdislocator
$(MAKE) -C utils/libtokencap
$(MAKE) -C utils/aflpp_driver
%.8: %
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@
@ -572,28 +622,31 @@ source-only: all
@echo .SH LICENSE >> $@
@echo Apache License Version 2.0, January 2004 >> $@
.PHONY: install
install: all $(MANPAGES)
install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh
@install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
@rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh
@rm -f $${DESTDIR}$(BIN_PATH)/afl-as
@rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o
install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH)
rm -f $${DESTDIR}$(BIN_PATH)/afl-as
if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi
if [ -f afl-gcc-fast ]; then set e; install -m 755 afl-gcc-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-gcc-fast $${DESTDIR}$(BIN_PATH)/afl-g++-fast; install -m 755 afl-gcc-pass.so afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH); fi
if [ -f afl-clang-fast ]; then $(MAKE) -C llvm_mode install; fi
if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi
if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi
if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi
if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi
set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++
set -e; if [ -f afl-clang-fast ] ; then ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi
mkdir -m 0755 -p ${DESTDIR}$(MAN_PATH)
@if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi
@if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi
@if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi
@if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi
@if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi
@if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C utils/socket_fuzzing install; fi
@if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C utils/argv_fuzzing install; fi
@if [ -f utils/afl_network_proxy/afl-network-server ]; then $(MAKE) -C utils/afl_network_proxy install; fi
@if [ -f utils/aflpp_driver/libAFLDriver.a ]; then set -e; install -m 644 utils/aflpp_driver/libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi
@if [ -f utils/aflpp_driver/libAFLQemuDriver.a ]; then set -e; install -m 644 utils/aflpp_driver/libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi
-$(MAKE) -f GNUmakefile.llvm install
-$(MAKE) -f GNUmakefile.gcc_plugin install
ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc
ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-g++
ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang
ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang++
@mkdir -m 0755 -p ${DESTDIR}$(MAN_PATH)
install -m0644 *.8 ${DESTDIR}$(MAN_PATH)
install -m 755 afl-as $${DESTDIR}$(HELPER_PATH)
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
install -m 644 docs/*.md $${DESTDIR}$(DOC_PATH)

View File

@ -19,24 +19,24 @@
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#TEST_MMAP=1
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 = $(shell grep '^$(HASH)define VERSION ' ./config.h | cut -d '"' -f2)
CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
CFLAGS_SAFE := -Wall -I../include -Wno-pointer-sign \
CFLAGS_SAFE := -Wall -Iinclude -Wno-pointer-sign \
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
-DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \
-Wno-unused-function
override CFLAGS += $(CFLAGS_SAFE)
CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
CXXEFLAGS := $(CXXFLAGS) -Wall
CXXEFLAGS := $(CXXFLAGS) -Wall -std=c++11
CC ?= gcc
CXX ?= g++
@ -51,7 +51,13 @@ ifeq "clang++" "$(CXX)"
CXX = g++
endif
PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(shell $(CC) -print-file-name=plugin)/include"
ifeq "$(findstring Foundation,$(shell $(CC) --version))" ""
CC = gcc
CXX = g++
endif
PLUGIN_BASE = "$(shell $(CC) -print-file-name=plugin)"
PLUGIN_FLAGS = -fPIC -fno-rtti -I$(PLUGIN_BASE)/include -I$(PLUGIN_BASE)
HASH=\#
GCCVER = $(shell $(CC) --version 2>/dev/null | awk 'NR == 1 {print $$NF}')
@ -61,103 +67,119 @@ ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int ma
SHMAT_OK=1
else
SHMAT_OK=0
override CFLAGS += -DUSEMMAP=1
override CFLAGS_SAFE += -DUSEMMAP=1
endif
ifeq "$(TEST_MMAP)" "1"
SHMAT_OK=0
override CFLAGS += -DUSEMMAP=1
override CFLAGS_SAFE += -DUSEMMAP=1
endif
ifneq "$(shell uname -s)" "Haiku"
LDFLAGS += -lrt
ifneq "$(shell uname -s)" "OpenBSD"
LDFLAGS += -lrt
endif
else
CFLAGS_SAFE += -DUSEMMAP=1
endif
PROGS = ../afl-gcc-fast ../afl-gcc-pass.so ../afl-gcc-rt.o
ifeq "$(shell uname -s)" "OpenBSD"
CC = egcc
CXX = eg++
PLUGIN_FLAGS += -I/usr/local/include
endif
ifeq "$(shell uname -s)" "DragonFly"
PLUGIN_FLAGS += -I/usr/local/include
endif
ifeq "$(shell uname -s)" "SunOS"
PLUGIN_FLAGS += -I/usr/include/gmp
endif
all: test_shm test_deps $(PROGS) afl-gcc-fast.8 test_build all_done
PROGS = ./afl-gcc-pass.so
.PHONY: all
all: test_shm test_deps $(PROGS) test_build all_done
.PHONY: test_shm
ifeq "$(SHMAT_OK)" "1"
test_shm:
@echo "[+] shmat seems to be working."
@rm -f .test2
else
test_shm:
@echo "[-] shmat seems not to be working, switching to mmap implementation"
endif
.PHONY: test_deps
test_deps:
@echo "[*] Checking for working '$(CC)'..."
@type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
@command -v $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
# @echo "[*] Checking for gcc for plugin support..."
# @$(CC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 )
@echo "[*] Checking for gcc plugin development header files..."
@test -d `$(CC) -print-file-name=plugin`/include || ( echo "[-] Oops, can't find gcc header files. Be sure to install 'gcc-X-plugin-dev'."; exit 1 )
@echo "[*] Checking for '../afl-showmap'..."
@test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 )
@echo "[*] Checking for './afl-showmap'..."
@test -f ./afl-showmap || ( echo "[-] Oops, can't find './afl-showmap'. Be sure to compile AFL first."; exit 1 )
@echo "[+] All set and ready to build."
afl-common.o: ../src/afl-common.c
$(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
afl-common.o: ./src/afl-common.c
$(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)
ln -sf afl-gcc-fast ../afl-g++-fast
../afl-gcc-pass.so: afl-gcc-pass.so.cc | test_deps
./afl-gcc-pass.so: instrumentation/afl-gcc-pass.so.cc | test_deps
$(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@
ln -sf afl-cc afl-gcc-fast
ln -sf afl-cc afl-g++-fast
ln -sf afl-cc.8 afl-gcc-fast.8
ln -sf afl-cc.8 afl-g++-fast.8
../afl-gcc-rt.o: afl-gcc-rt.o.c | test_deps
$(CC) $(CFLAGS_SAFE) -fPIC -c $< -o $@
.PHONY: test_build
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_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
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)
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
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
@echo "[+] All right, the instrumentation seems to be working!"
.PHONY: all_done
all_done: test_build
@echo "[+] All done! You can now use '../afl-gcc-fast' to compile programs."
@echo "[+] All done! You can now use './afl-gcc-fast' to compile programs."
.NOTPARALLEL: clean
vpath % ..
%.8: %
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ../$@
@echo .SH NAME >> ../$@
@echo .B $* >> ../$@
@echo >> ../$@
@echo .SH SYNOPSIS >> ../$@
@../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@
@echo >> ../$@
@echo .SH OPTIONS >> ../$@
@echo .nf >> ../$@
@../$* -h 2>&1 | tail -n +4 >> ../$@
@echo >> ../$@
@echo .SH AUTHOR >> ../$@
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ../$@
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ../$@
@echo >> ../$@
@echo .SH LICENSE >> ../$@
@echo Apache License Version 2.0, January 2004 >> ../$@
ln -sf afl-gcc-fast.8 ../afl-g++-fast.8
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ./$@
@echo .SH NAME >> ./$@
@echo .B $* >> ./$@
@echo >> ./$@
@echo .SH SYNOPSIS >> ./$@
@./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ./$@
@echo >> ./$@
@echo .SH OPTIONS >> ./$@
@echo .nf >> ./$@
@./$* -h 2>&1 | tail -n +4 >> ./$@
@echo >> ./$@
@echo .SH AUTHOR >> ./$@
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ./$@
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
@echo >> ./$@
@echo .SH LICENSE >> ./$@
@echo Apache License Version 2.0, January 2004 >> ./$@
ln -sf afl-cc.8 ./afl-g++-fast.8
.PHONY: install
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
ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc-fast
ln -sf afl-c++ $${DESTDIR}$(BIN_PATH)/afl-g++-fast
ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o
install -m 755 ./afl-gcc-pass.so $${DESTDIR}$(HELPER_PATH)
install -m 644 -T instrumentation/README.gcc_plugin.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md
.PHONY: clean
clean:
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2
rm -f $(PROGS) afl-common.o ../afl-g++-fast ../afl-g*-fast.8
rm -f $(PROGS) afl-common.o ./afl-g++-fast ./afl-g*-fast.8 instrumentation/*.o

517
GNUmakefile.llvm Normal file
View File

@ -0,0 +1,517 @@
# american fuzzy lop++ - LLVM instrumentation
# -----------------------------------------
#
# Written by Laszlo Szekeres <lszekeres@google.com> and
# Michal Zalewski
#
# LLVM integration design comes from Laszlo Szekeres.
#
# Copyright 2015, 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# For Heiko:
#TEST_MMAP=1
HASH=\#
PREFIX ?= /usr/local
HELPER_PATH ?= $(PREFIX)/lib/afl
BIN_PATH ?= $(PREFIX)/bin
DOC_PATH ?= $(PREFIX)/share/doc/afl
MISC_PATH ?= $(PREFIX)/share/afl
MAN_PATH ?= $(PREFIX)/share/man/man8
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")
VERSION = $(shell grep '^$(HASH)define VERSION ' ./config.h | cut -d '"' -f2)
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"
$(warning 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//' | sed 's/svn//' )
LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' )
LLVM_MINOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/.*\.//' | sed 's/git//' | sed 's/svn//' | sed 's/ .*//' )
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_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-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_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null)
LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null)
LLVM_STDCXX = gnu++11
LLVM_APPLE_XCODE = $(shell clang -v 2>&1 | grep -q Apple && echo 1 || echo 0)
LLVM_LTO = 0
ifeq "$(LLVMVER)" ""
$(warning [!] llvm_mode needs llvm-config, which was not found)
endif
ifeq "$(LLVM_UNSUPPORTED)" "1"
$(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_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-lto LTO implementation)
LLVM_LTO = 1
#TEST_MMAP = 1
endif
ifeq "$(LLVM_LTO)" "0"
$(info [+] llvm_mode detected llvm < 11, afl-lto LTO will not be build.)
endif
ifeq "$(LLVM_APPLE_XCODE)" "1"
$(warning llvm_mode will not compile with Xcode clang...)
endif
# We were using llvm-config --bindir to get the location of clang, but
# this seems to be busted on some distros, so using the one in $PATH is
# probably better.
CC = $(LLVM_BINDIR)/clang
CXX = $(LLVM_BINDIR)/clang++
# llvm-config --bindir may not providing a valid path, so ...
ifeq "$(shell test -e $(CC) || echo 1 )" "1"
# however we must ensure that this is not a "CC=gcc make"
ifeq "$(shell command -v $(CC) 2> /dev/null)" ""
# we do not have a valid CC variable so we try alternatives
ifeq "$(shell test -e '$(BIN_DIR)/clang' && echo 1)" "1"
# we found one in the local install directory, lets use these
CC = $(BIN_DIR)/clang
else
# hope for the best
$(warning we have trouble finding clang - llvm-config is not helping us)
CC = clang
endif
endif
endif
# llvm-config --bindir may not providing a valid path, so ...
ifeq "$(shell test -e $(CXX) || echo 1 )" "1"
# however we must ensure that this is not a "CXX=g++ make"
ifeq "$(shell command -v $(CXX) 2> /dev/null)" ""
# we do not have a valid CXX variable so we try alternatives
ifeq "$(shell test -e '$(BIN_DIR)/clang++' && echo 1)" "1"
# we found one in the local install directory, lets use these
CXX = $(BIN_DIR)/clang++
else
# hope for the best
$(warning we have trouble finding clang++ - llvm-config is not helping us)
CXX = clang++
endif
endif
endif
# sanity check.
# Are versions of clang --version and llvm-config --version equal?
CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*version\ (1?[0-9]\.[0-9]\.[0-9]).*/s//\1/p')
# I disable this because it does not make sense with what we did before (marc)
# We did exactly set these 26 lines above with these values, and it would break
# "CC=gcc make" etc. usages
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
CC_SAVE := $(LLVM_BINDIR)/clang
else
CC_SAVE := $(CC)
endif
ifeq "$(findstring clang, $(shell $(CXX) --version 2>/dev/null))" ""
CXX_SAVE := $(LLVM_BINDIR)/clang++
else
CXX_SAVE := $(CXX)
endif
CLANG_BIN := $(CC_SAVE)
CLANGPP_BIN := $(CXX_SAVE)
ifeq "$(CC_SAVE)" "$(LLVM_BINDIR)/clang"
USE_BINDIR = 1
else
ifeq "$(CXX_SAVE)" "$(LLVM_BINDIR)/clang++"
USE_BINDIR = 1
else
USE_BINDIR = 0
endif
endif
# On old platform we cannot compile with clang because std++ libraries are too
# old. For these we need to use gcc/g++, so if we find REAL_CC and REAL_CXX
# variable we override the compiler variables here
ifneq "$(REAL_CC)" ""
CC = $(REAL_CC)
endif
ifneq "$(REAL_CXX)" ""
CXX = $(REAL_CXX)
endif
#
# Now it can happen that CC points to clang - but there is no clang on the
# system. Then we fall back to cc
#
ifeq "$(shell command -v $(CC) 2>/dev/null)" ""
CC = cc
endif
ifeq "$(shell command -v $(CXX) 2>/dev/null)" ""
CXX = c++
endif
# After we set CC/CXX we can start makefile magic tests
#ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
# CFLAGS_OPT = -march=native
#endif
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
AFL_CLANG_FLTO ?= -flto=full
else
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
AFL_CLANG_FLTO ?= -flto=thin
else
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
AFL_CLANG_FLTO ?= -flto
endif
endif
endif
ifeq "$(LLVM_LTO)" "1"
ifneq "$(AFL_CLANG_FLTO)" ""
ifeq "$(AFL_REAL_LD)" ""
ifneq "$(shell readlink $(LLVM_BINDIR)/ld.lld 2>&1)" ""
AFL_REAL_LD = $(LLVM_BINDIR)/ld.lld
else
$(warn ld.lld not found, 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=
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
CFLAGS_SAFE := -Wall -g -Wno-cast-qual -Wno-variadic-macros -Wno-pointer-sign -I ./include/ -I ./instrumentation/ \
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
-DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \
-DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \
-Wno-deprecated -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \
-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
$(info Compile option AFL_TRACE_PC is deprecated, just set AFL_LLVM_INSTRUMENT=PCGUARD to activate when compiling targets )
endif
CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2
override CXXFLAGS += -Wall -g -I ./include/ \
-DVERSION=\"$(VERSION)\" -Wno-variadic-macros \
-DLLVM_MINOR=$(LLVM_MINOR) -DLLVM_MAJOR=$(LLVM_MAJOR)
ifneq "$(shell $(LLVM_CONFIG) --includedir) 2> /dev/null" ""
CLANG_CFL = -I$(shell $(LLVM_CONFIG) --includedir)
endif
ifneq "$(LLVM_CONFIG)" ""
CLANG_CFL += -I$(shell dirname $(LLVM_CONFIG))/../include
endif
CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations
CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS)
# User teor2345 reports that this is required to make things work on MacOS X.
ifeq "$(shell uname)" "Darwin"
CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress
else
CLANG_CPPFL += -Wl,-znodelete
endif
ifeq "$(shell uname)" "OpenBSD"
CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so
CLANG_CPPFL += -mno-retpoline
CFLAGS += -mno-retpoline
# Needed for unwind symbols
LDFLAGS += -lc++abi -lpthread
endif
ifeq "$(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1"
SHMAT_OK=1
else
SHMAT_OK=0
CFLAGS_SAFE += -DUSEMMAP=1
LDFLAGS += -Wno-deprecated-declarations
endif
ifeq "$(TEST_MMAP)" "1"
SHMAT_OK=0
CFLAGS_SAFE += -DUSEMMAP=1
LDFLAGS += -Wno-deprecated-declarations
endif
PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o
PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so
# If prerequisites are not given, warn, do not build anything, and exit with code 0
ifeq "$(LLVMVER)" ""
NO_BUILD = 1
endif
ifneq "$(LLVM_UNSUPPORTED)$(LLVM_APPLE_XCODE)" "00"
NO_BUILD = 1
endif
ifeq "$(NO_BUILD)" "1"
TARGETS = test_shm $(PROGS_ALWAYS) afl-cc.8
else
TARGETS = test_shm test_deps $(PROGS) afl-cc.8 test_build all_done
endif
LLVM_MIN_4_0_1 = $(shell awk 'function tonum(ver, a) {split(ver,a,"."); return a[1]*1000000+a[2]*1000+a[3]} BEGIN { exit tonum(ARGV[1]) >= tonum(ARGV[2]) }' $(LLVMVER) 4.0.1; echo $$?)
.PHONY: all
all: $(TARGETS)
.PHONY: test_shm
ifeq "$(SHMAT_OK)" "1"
test_shm:
@echo "[+] shmat seems to be working."
@rm -f .test2
else
test_shm:
@echo "[-] shmat seems not to be working, switching to mmap implementation"
endif
.PHONY: no_build
no_build:
@printf "%b\\n" "\\033[0;31mPrerequisites are not met, skipping build llvm_mode\\033[0m"
.PHONY: test_deps
test_deps:
@echo "[*] Checking for working 'llvm-config'..."
ifneq "$(LLVM_APPLE_XCODE)" "1"
@type $(LLVM_CONFIG) >/dev/null 2>&1 || ( echo "[-] Oops, can't find 'llvm-config'. Install clang or set \$$LLVM_CONFIG or \$$PATH beforehand."; echo " (Sometimes, the binary will be named llvm-config-3.5 or something like that.)"; exit 1 )
endif
@echo "[*] Checking for working '$(CC)'..."
@type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
@echo "[*] Checking for matching versions of '$(CC)' and '$(LLVM_CONFIG)'"
ifneq "$(CLANGVER)" "$(LLVMVER)"
@echo "[!] WARNING: we have llvm-config version $(LLVMVER) and a clang version $(CLANGVER)"
else
@echo "[*] We have llvm-config version $(LLVMVER) with a clang version $(CLANGVER), good."
endif
@echo "[*] Checking for './afl-showmap'..."
@test -f ./afl-showmap || ( echo "[-] Oops, can't find './afl-showmap'. Be sure to compile AFL first."; exit 1 )
@echo "[+] All set and ready to build."
instrumentation/afl-common.o: ./src/afl-common.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS)
./afl-cc: src/afl-cc.c instrumentation/afl-common.o
$(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< instrumentation/afl-common.o -o $@ -DLLVM_MINOR=$(LLVM_MINOR) -DLLVM_MAJOR=$(LLVM_MAJOR) $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\"
@ln -sf afl-cc ./afl-c++
@ln -sf afl-cc ./afl-gcc
@ln -sf afl-cc ./afl-g++
@ln -sf afl-cc ./afl-clang
@ln -sf afl-cc ./afl-clang++
@ln -sf afl-cc ./afl-clang-fast
@ln -sf afl-cc ./afl-clang-fast++
ifneq "$(AFL_CLANG_FLTO)" ""
ifeq "$(LLVM_LTO)" "1"
@ln -sf afl-cc ./afl-clang-lto
@ln -sf afl-cc ./afl-clang-lto++
@ln -sf afl-cc ./afl-lto
@ln -sf afl-cc ./afl-lto++
endif
endif
instrumentation/afl-llvm-common.o: instrumentation/afl-llvm-common.cc instrumentation/afl-llvm-common.h
$(CXX) $(CFLAGS) $(CPPFLAGS) `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC -std=$(LLVM_STDCXX) -c $< -o $@
./libLLVMInsTrim.so: instrumentation/LLVMInsTrim.so.cc instrumentation/MarkNodes.cc instrumentation/afl-llvm-common.o | test_deps
-$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< instrumentation/MarkNodes.cc -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
./afl-llvm-pass.so: instrumentation/afl-llvm-pass.so.cc instrumentation/afl-llvm-common.o | test_deps
ifeq "$(LLVM_MIN_4_0_1)" "0"
$(info [!] N-gram branch coverage instrumentation is not available for llvm version $(LLVMVER))
endif
$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
./SanitizerCoveragePCGUARD.so: instrumentation/SanitizerCoveragePCGUARD.so.cc instrumentation/afl-llvm-common.o | test_deps
ifeq "$(LLVM_10_OK)" "1"
-$(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
endif
./afl-llvm-lto-instrumentlist.so: instrumentation/afl-llvm-lto-instrumentlist.so.cc instrumentation/afl-llvm-common.o
ifeq "$(LLVM_LTO)" "1"
$(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
endif
./afl-ld-lto: src/afl-ld-lto.c
ifeq "$(LLVM_LTO)" "1"
$(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@
endif
./SanitizerCoverageLTO.so: instrumentation/SanitizerCoverageLTO.so.cc
ifeq "$(LLVM_LTO)" "1"
$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
endif
./afl-llvm-lto-instrumentation.so: instrumentation/afl-llvm-lto-instrumentation.so.cc instrumentation/afl-llvm-common.o
ifeq "$(LLVM_LTO)" "1"
$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c instrumentation/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 instrumentation/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 instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
endif
# laf
./split-switches-pass.so: instrumentation/split-switches-pass.so.cc instrumentation/afl-llvm-common.o | test_deps
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
./compare-transform-pass.so: instrumentation/compare-transform-pass.so.cc instrumentation/afl-llvm-common.o | test_deps
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
./split-compares-pass.so: instrumentation/split-compares-pass.so.cc instrumentation/afl-llvm-common.o | test_deps
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
# /laf
./cmplog-routines-pass.so: instrumentation/cmplog-routines-pass.cc instrumentation/afl-llvm-common.o | test_deps
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
./cmplog-instructions-pass.so: instrumentation/cmplog-instructions-pass.cc instrumentation/afl-llvm-common.o | test_deps
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
afl-llvm-dict2file.so: instrumentation/afl-llvm-dict2file.so.cc instrumentation/afl-llvm-common.o | test_deps
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
.PHONY: document
document:
$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt.o
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m32 -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-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 instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
./afl-compiler-rt.o: instrumentation/afl-compiler-rt.o.c
$(CC) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -fPIC -c $< -o $@
./afl-compiler-rt-32.o: instrumentation/afl-compiler-rt.o.c
@printf "[*] Building 32-bit variant of the runtime (-m32)... "
@$(CC) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; ln -sf afl-compiler-rt-32.o afl-llvm-rt-32.o; else echo "failed (that's fine)"; fi
./afl-compiler-rt-64.o: instrumentation/afl-compiler-rt.o.c
@printf "[*] Building 64-bit variant of the runtime (-m64)... "
@$(CC) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; ln -sf afl-compiler-rt-64.o afl-llvm-rt-64.o; else echo "failed (that's fine)"; fi
.PHONY: test_build
test_build: $(PROGS)
@echo "[*] Testing the CC wrapper and instrumentation output..."
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_LLVM_LAF_ALL=1 ./afl-cc $(CFLAGS) $(CPPFLAGS) ./test-instr.c -o test-instr $(LDFLAGS)
ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
@rm -f test-instr
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
@echo "[+] All right, the instrumentation seems to be working!"
.PHONY: all_done
all_done: test_build
@echo "[+] All done! You can now use './afl-cc' to compile programs."
.NOTPARALLEL: clean
.PHONY: install
install: all
@install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
@if [ -f ./afl-cc ]; then set -e; install -m 755 ./afl-cc $${DESTDIR}$(BIN_PATH); ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-c++; fi
@rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt*.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt*.o
@if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt.o ;fi
@if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${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-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o ;fi
@if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o ; fi
@if [ -f ./compare-transform-pass.so ]; then set -e; install -m 755 ./*.so $${DESTDIR}$(HELPER_PATH); fi
@if [ -f ./compare-transform-pass.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-fast ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-fast++ ; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang++ ; fi
@if [ -f ./SanitizerCoverageLTO.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-lto++ ; fi
set -e; install -m 644 ./dynamic_list.txt $${DESTDIR}$(HELPER_PATH)
install -m 644 instrumentation/README.*.md $${DESTDIR}$(DOC_PATH)/
%.8: %
@echo .TH $* 8 $(BUILD_DATE) "afl++" > ./$@
@echo .SH NAME >> ./$@
@printf "%s" ".B $* \- " >> ./$@
@./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ./$@
@echo .B $* >> ./$@
@echo >> ./$@
@echo .SH SYNOPSIS >> ./$@
@./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ./$@
@echo >> ./$@
@echo .SH OPTIONS >> ./$@
@echo .nf >> ./$@
@./$* -h 2>&1 | tail -n +4 >> ./$@
@echo >> ./$@
@echo .SH AUTHOR >> ./$@
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ./$@
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
@echo >> ./$@
@echo .SH LICENSE >> ./$@
@echo Apache License Version 2.0, January 2004 >> ./$@
@ln -sf afl-cc.8 ./afl-c++.8
ifneq "$(AFL_CLANG_FLTO)" ""
ifeq "$(LLVM_LTO)" "1"
@ln -sf afl-cc.8 ./afl-clang-lto.8
@ln -sf afl-cc.8 ./afl-clang-lto++.8
@ln -sf afl-cc.8 ./afl-lto.8
@ln -sf afl-cc.8 ./afl-lto++.8
endif
endif
.PHONY: clean
clean:
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1 *.dwo
rm -f $(PROGS) afl-common.o ./afl-c++ ./afl-lto ./afl-lto++ ./afl-clang-lto* ./afl-clang-fast* ./afl-clang*.8 ./ld ./afl-ld ./afl-llvm-rt*.o instrumentation/*.o

1016
README.md

File diff suppressed because it is too large Load Diff

24
TODO.md
View File

@ -1,28 +1,23 @@
# TODO list for AFL++
## Roadmap 2.66+
## Roadmap 3.00+
- 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
gcc_plugin:
- (wait for submission then decide)
- laf-intel
- better instrumentation (seems to be better with gcc-9+)
- add __sanitizer_cov_trace_cmp* support
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 +25,10 @@ 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

View File

@ -113,13 +113,15 @@ function usage() {
" -C - keep crashing inputs, reject everything else\n" \
" -e - solve for edge coverage only, ignore hit counts\n" \
"\n" \
"For additional tips, please consult docs/README.md\n" \
"For additional tips, please consult README.md\n" \
"\n" \
"Environment variables used:\n" \
"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" \
"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \
"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" \
"AFL_KEEP_TRACES: leave the temporary <out_dir>/.traces directory\n" \
"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_PATH: path for the afl-showmap binary if not found anywhere else\n" \
"AFL_SKIP_BIN_CHECK: skip check for target binary\n"
exit 1
}
@ -132,6 +134,8 @@ BEGIN {
# defaults
extra_par = ""
AFL_CMIN_CRASHES_ONLY = ""
# process options
Opterr = 1 # default is to diagnose
Optind = 1 # skip ARGV[0]
@ -168,7 +172,7 @@ BEGIN {
continue
} else
if (_go_c == "C") {
ENVIRON["AFL_CMIN_CRASHES_ONLY"] = 1
AFL_CMIN_CRASHES_ONLY = "AFL_CMIN_CRASHES_ONLY=1 "
continue
} else
if (_go_c == "e") {
@ -309,14 +313,18 @@ BEGIN {
close( stdin_file )
}
if (!ENVIRON["AFL_PATH"]) {
if (0 == system("test -f afl-cmin")) {
# First we look in PATH
if (0 == system("command -v afl-showmap >/dev/null 2>&1")) {
"command -v afl-showmap 2>/dev/null" | getline showmap
} else {
# then we look in the current directory
if (0 == system("test -x ./afl-showmap")) {
showmap = "./afl-showmap"
} else {
"command -v afl-showmap 2>/dev/null" | getline showmap
if (ENVIRON["AFL_PATH"]) {
showmap = ENVIRON["AFL_PATH"] "/afl-showmap"
}
}
} else {
showmap = ENVIRON["AFL_PATH"] "/afl-showmap"
}
if (!showmap || 0 != system("test -x "showmap )) {
@ -364,10 +372,10 @@ BEGIN {
print "[*] Testing the target binary..."
if (!stdin_file) {
system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -- \""target_bin"\" "prog_args_string" <\""in_dir"/"first_file"\"")
system( "AFL_CMIN_ALLOW_ANY=1 "AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -- \""target_bin"\" "prog_args_string" <\""in_dir"/"first_file"\"")
} else {
system("cp "in_dir"/"first_file" "stdin_file)
system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
system( "AFL_CMIN_ALLOW_ANY=1 "AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
}
first_count = 0
@ -398,14 +406,16 @@ BEGIN {
cur = 0;
if (!stdin_file) {
print " Processing "in_count" files (forkserver mode)..."
retval = system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string
retval = system( AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
} else {
print " Processing "in_count" files (forkserver mode)..."
retval = system( "AFL_CMIN_ALLOW_ANY=1 \""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null")
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null"
retval = system( AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null")
}
if (retval) {
print "[!]Exit code != 0 received from afl-showmap, terminating..."
if (retval && !AFL_CMIN_CRASHES_ONLY) {
print "[!] Exit code "retval" != 0 received from afl-showmap, terminating..."
if (!ENVIRON["AFL_KEEP_TRACES"]) {
system("rm -rf "trace_dir" 2>/dev/null")

View File

@ -128,11 +128,11 @@ Minimization settings:
-C - keep crashing inputs, reject everything else
-e - solve for edge coverage only, ignore hit counts
For additional tips, please consult docs/README.md.
For additional tips, please consult README.md.
Environment variables used:
AFL_KEEP_TRACES: leave the temporary <out_dir>\.traces directory
AFL_PATH: path for the afl-showmap binary
AFL_PATH: last resort location to find the afl-showmap binary
AFL_SKIP_BIN_CHECK: skip check for target binary
_EOF_
exit 1
@ -244,10 +244,21 @@ if [ ! "$STDIN_FILE" = "" ]; then
touch "$STDIN_FILE" || exit 1
fi
if [ "$AFL_PATH" = "" ]; then
SHOWMAP="${0%/afl-cmin.bash}/afl-showmap"
SHOWMAP=`command -v afl-showmap 2>/dev/null`
if [ -z "$SHOWMAP" ]; then
TMP="${0%/afl-cmin.bash}/afl-showmap"
if [ -x "$TMP" ]; then
SHOWMAP=$TMP
fi
fi
if [ -z "$SHOWMAP" -a -x "./afl-showmap" ]; then
SHOWMAP="./afl-showmap"
else
SHOWMAP="$AFL_PATH/afl-showmap"
if [ -n "$AFL_PATH" ]; then
SHOWMAP="$AFL_PATH/afl-showmap"
fi
fi
if [ ! -x "$SHOWMAP" ]; then

View File

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

View File

@ -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
@ -80,4 +80,3 @@ if [ "$PLATFORM" = "Darwin" ] ; then
DONE=1
fi
test -z "$DONE" && echo Error: Unknown platform: $PLATFORM
test -z "$AFL_TMPDIR" && echo Also use AFL_TMPDIR and point it to a tmpfs for the input file caching

View File

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

View File

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

View File

@ -1,4 +1,21 @@
# 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:
```sh
git submodule update --init
```
Read the README in the [Grammar-Mutator] repository on how to use it.
[Grammar-Mutator]: https://github.com/AFLplusplus/Grammar-Mutator
## Production-Ready Custom Mutators
This directory holds ready to use custom mutators.
Just type "make" in the individual subdirectories.
@ -10,3 +27,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

View File

@ -0,0 +1 @@
b3c4fcf

View File

@ -0,0 +1,6 @@
# Grammar-Mutator
This is just a stub directory that will clone the real grammar mutator
directory.
Execute `./build_grammar_mutator.sh` to set everything up.

View File

@ -0,0 +1,140 @@
#!/bin/sh
#
# american fuzzy lop++ - unicorn mode build script
# ------------------------------------------------
#
# Originally written by Nathan Voss <njvoss99@gmail.com>
#
# Adapted from code by Andrew Griffiths <agriffiths@google.com> and
# Michal Zalewski
#
# Adapted for AFLplusplus by Dominik Maier <mail@dmnk.co>
#
# CompareCoverage and NeverZero counters by Andrea Fioraldi
# <andreafioraldi@gmail.com>
#
# Copyright 2017 Battelle Memorial Institute. 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
#
# This script downloads, patches, and builds a version of Unicorn with
# minor tweaks to allow Unicorn-emulated binaries to be run under
# afl-fuzz.
#
# The modifications reside in patches/*. The standalone Unicorn library
# will be written to /usr/lib/libunicornafl.so, and the Python bindings
# will be installed system-wide.
#
# You must make sure that Unicorn Engine is not already installed before
# running this script. If it is, please uninstall it first.
GRAMMAR_VERSION="$(cat ./GRAMMAR_VERSION)"
GRAMMAR_REPO="https://github.com/AFLplusplus/grammar-mutator"
echo "================================================="
echo "Grammar Mutator build script"
echo "================================================="
echo
echo "[*] Performing basic sanity checks..."
PLT=`uname -s`
if [ ! -f "../../config.h" ]; then
echo "[-] Error: key files not found - wrong working directory?"
exit 1
fi
PYTHONBIN=`command -v python3 || command -v python || command -v python2 || echo python3`
MAKECMD=make
TARCMD=tar
if [ "$PLT" = "Darwin" ]; then
CORES=`sysctl -n hw.ncpu`
TARCMD=tar
fi
if [ "$PLT" = "FreeBSD" ]; then
MAKECMD=gmake
CORES=`sysctl -n hw.ncpu`
TARCMD=gtar
fi
if [ "$PLT" = "NetBSD" ] || [ "$PLT" = "OpenBSD" ]; then
MAKECMD=gmake
CORES=`sysctl -n hw.ncpu`
TARCMD=gtar
fi
PREREQ_NOTFOUND=
for i in git $MAKECMD $TARCMD; do
T=`command -v "$i" 2>/dev/null`
if [ "$T" = "" ]; then
echo "[-] Error: '$i' not found. Run 'sudo apt-get install $i' or similar."
PREREQ_NOTFOUND=1
fi
done
if echo "$CC" | grep -qF /afl-; then
echo "[-] Error: do not use afl-gcc or afl-clang to compile this tool."
PREREQ_NOTFOUND=1
fi
if [ "$PREREQ_NOTFOUND" = "1" ]; then
exit 1
fi
echo "[+] All checks passed!"
echo "[*] Making sure grammar mutator is checked out"
git status 1>/dev/null 2>/dev/null
if [ $? -eq 0 ]; then
echo "[*] initializing grammar mutator submodule"
git submodule init || exit 1
git submodule update ./grammar-mutator 2>/dev/null # ignore errors
else
echo "[*] cloning grammar mutator"
test -d grammar-mutator || {
CNT=1
while [ '!' -d grammar-mutator -a "$CNT" -lt 4 ]; do
echo "Trying to clone grammar-mutator (attempt $CNT/3)"
git clone "$GRAMMAR_REPO"
CNT=`expr "$CNT" + 1`
done
}
fi
test -d grammar-mutator || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; }
echo "[+] Got grammar mutator."
cd "grammar-mutator" || exit 1
echo "[*] Checking out $GRAMMAR_VERSION"
sh -c 'git stash && git stash drop' 1>/dev/null 2>/dev/null
git checkout "$GRAMMAR_VERSION" || exit 1
echo "[*] Downloading antlr..."
wget -c https://www.antlr.org/download/antlr-4.8-complete.jar
cd ..
echo
echo
echo "[+] All successfully prepared!"
echo "[!] To build for your grammar just do:"
echo " cd grammar-mutator"
echo " make GRAMMAR_FILE=/path/to/your/grammar"
echo "[+] You will find a JSON and RUBY grammar in grammar-mutator/grammars to play with."
echo

View File

@ -0,0 +1,50 @@
#!/bin/sh
##################################################
# AFL++ tool to update a git ref.
# Usage: ./<script>.sh <new commit hash>
# If no commit hash was provided, it'll take HEAD.
##################################################
TOOL="grammar mutator"
VERSION_FILE='./GRAMMAR_VERSION'
REPO_FOLDER='./grammar_mutator'
THIS_SCRIPT=`basename $0`
BRANCH="stable"
NEW_VERSION="$1"
if [ "$NEW_VERSION" = "-h" ]; then
echo "Internal script to update bound $TOOL version."
echo
echo "Usage: $THIS_SCRIPT <new commit hash>"
echo "If no commit hash is provided, will use HEAD."
echo "-h to show this help screen."
exit 1
fi
git submodule init && git submodule update ./grammar_mutator || exit 1
cd "$REPO_FOLDER" || exit 1
git fetch origin $BRANCH 1>/dev/null || exit 1
git stash 1>/dev/null 2>/dev/null
git stash drop 1>/dev/null 2>/dev/null
git checkout $BRANCH
if [ -z "$NEW_VERSION" ]; then
# No version provided, take HEAD.
NEW_VERSION=$(git rev-parse --short HEAD)
fi
if [ -z "$NEW_VERSION" ]; then
echo "Error getting version."
exit 1
fi
git checkout "$NEW_VERSION" || exit 1
cd ..
rm "$VERSION_FILE"
echo "$NEW_VERSION" > "$VERSION_FILE"
echo "Done. New $TOOL version is $NEW_VERSION."

View File

@ -0,0 +1,17 @@
CFLAGS = -O3 -funroll-loops -fPIC -Wl,-Bsymbolic
all: honggfuzz-mutator.so
honggfuzz-mutator.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c
$(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz-mutator.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

View File

@ -0,0 +1,12 @@
# custum mutator: honggfuzz mangle
this is the 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-mutator.so afl-fuzz ...```
> Original repository: https://github.com/google/honggfuzz
> Source commit: d0fbcb0373c32436b8fb922e6937da93b17291f5

View File

View 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

View File

@ -0,0 +1,144 @@
#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) {
free(data);
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);
}

View 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

View 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

View File

@ -0,0 +1 @@
.

View File

@ -0,0 +1 @@
common.h

File diff suppressed because it is too large Load Diff

View 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

View File

@ -0,0 +1 @@
common.h

View File

@ -0,0 +1,35 @@
//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Wrapper functions and macros around builtin functions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_BUILTINS_H
#define LLVM_FUZZER_BUILTINS_H
#include "FuzzerPlatform.h"
#if !LIBFUZZER_MSVC
#include <cstdint>
#define GET_CALLER_PC() __builtin_return_address(0)
namespace fuzzer {
inline uint8_t Bswap(uint8_t x) { return x; }
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); }
inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); }
inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); }
} // namespace fuzzer
#endif // !LIBFUZZER_MSVC
#endif // LLVM_FUZZER_BUILTINS_H

View File

@ -0,0 +1,72 @@
//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Wrapper functions and macros that use intrinsics instead of builtin functions
// which cannot be compiled by MSVC.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_BUILTINS_MSVC_H
#define LLVM_FUZZER_BUILTINS_MSVC_H
#include "FuzzerPlatform.h"
#if LIBFUZZER_MSVC
#include <intrin.h>
#include <cstdint>
#include <cstdlib>
// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent
// from <intrin.h>
#define GET_CALLER_PC() _ReturnAddress()
namespace fuzzer {
inline uint8_t Bswap(uint8_t x) { return x; }
// Use alternatives to __builtin functions from <stdlib.h> and <intrin.h> on
// Windows since the builtins are not supported by MSVC.
inline uint16_t Bswap(uint16_t x) { return _byteswap_ushort(x); }
inline uint32_t Bswap(uint32_t x) { return _byteswap_ulong(x); }
inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); }
// The functions below were mostly copied from
// compiler-rt/lib/builtins/int_lib.h which defines the __builtin functions used
// outside of Windows.
inline uint32_t Clzll(uint64_t X) {
unsigned long LeadZeroIdx = 0;
#if !defined(_M_ARM) && !defined(_M_X64)
// Scan the high 32 bits.
if (_BitScanReverse(&LeadZeroIdx, static_cast<unsigned long>(X >> 32)))
return static_cast<int>(63 - (LeadZeroIdx + 32)); // Create a bit offset from the MSB.
// Scan the low 32 bits.
if (_BitScanReverse(&LeadZeroIdx, static_cast<unsigned long>(X)))
return static_cast<int>(63 - LeadZeroIdx);
#else
if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx;
#endif
return 64;
}
inline uint32_t Clz(uint32_t X) {
unsigned long LeadZeroIdx = 0;
if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx;
return 32;
}
inline int Popcountll(unsigned long long X) {
#if !defined(_M_ARM) && !defined(_M_X64)
return __popcnt(X) + __popcnt(X >> 32);
#else
return __popcnt64(X);
#endif
}
} // namespace fuzzer
#endif // LIBFUZER_MSVC
#endif // LLVM_FUZZER_BUILTINS_MSVC_H

View File

@ -0,0 +1,178 @@
//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// FuzzerCommand represents a command to run in a subprocess. It allows callers
// to manage command line arguments and output and error streams.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_COMMAND_H
#define LLVM_FUZZER_COMMAND_H
#include "FuzzerDefs.h"
#include "FuzzerIO.h"
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
namespace fuzzer {
class Command final {
public:
// This command line flag is used to indicate that the remaining command line
// is immutable, meaning this flag effectively marks the end of the mutable
// argument list.
static inline const char *ignoreRemainingArgs() {
return "-ignore_remaining_args=1";
}
Command() : CombinedOutAndErr(false) {}
explicit Command(const Vector<std::string> &ArgsToAdd)
: Args(ArgsToAdd), CombinedOutAndErr(false) {}
explicit Command(const Command &Other)
: Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr),
OutputFile(Other.OutputFile) {}
Command &operator=(const Command &Other) {
Args = Other.Args;
CombinedOutAndErr = Other.CombinedOutAndErr;
OutputFile = Other.OutputFile;
return *this;
}
~Command() {}
// Returns true if the given Arg is present in Args. Only checks up to
// "-ignore_remaining_args=1".
bool hasArgument(const std::string &Arg) const {
auto i = endMutableArgs();
return std::find(Args.begin(), i, Arg) != i;
}
// Gets all of the current command line arguments, **including** those after
// "-ignore-remaining-args=1".
const Vector<std::string> &getArguments() const { return Args; }
// Adds the given argument before "-ignore_remaining_args=1", or at the end
// if that flag isn't present.
void addArgument(const std::string &Arg) {
Args.insert(endMutableArgs(), Arg);
}
// Adds all given arguments before "-ignore_remaining_args=1", or at the end
// if that flag isn't present.
void addArguments(const Vector<std::string> &ArgsToAdd) {
Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end());
}
// Removes the given argument from the command argument list. Ignores any
// occurrences after "-ignore_remaining_args=1", if present.
void removeArgument(const std::string &Arg) {
auto i = endMutableArgs();
Args.erase(std::remove(Args.begin(), i, Arg), i);
}
// Like hasArgument, but checks for "-[Flag]=...".
bool hasFlag(const std::string &Flag) const {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
};
return std::any_of(Args.begin(), endMutableArgs(), IsMatch);
}
// Returns the value of the first instance of a given flag, or an empty string
// if the flag isn't present. Ignores any occurrences after
// "-ignore_remaining_args=1", if present.
std::string getFlagValue(const std::string &Flag) const {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
};
auto i = endMutableArgs();
auto j = std::find_if(Args.begin(), i, IsMatch);
std::string result;
if (j != i) {
result = j->substr(Arg.length());
}
return result;
}
// Like AddArgument, but adds "-[Flag]=[Value]".
void addFlag(const std::string &Flag, const std::string &Value) {
addArgument("-" + Flag + "=" + Value);
}
// Like RemoveArgument, but removes "-[Flag]=...".
void removeFlag(const std::string &Flag) {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
};
auto i = endMutableArgs();
Args.erase(std::remove_if(Args.begin(), i, IsMatch), i);
}
// Returns whether the command's stdout is being written to an output file.
bool hasOutputFile() const { return !OutputFile.empty(); }
// Returns the currently set output file.
const std::string &getOutputFile() const { return OutputFile; }
// Configures the command to redirect its output to the name file.
void setOutputFile(const std::string &FileName) { OutputFile = FileName; }
// Returns whether the command's stderr is redirected to stdout.
bool isOutAndErrCombined() const { return CombinedOutAndErr; }
// Sets whether to redirect the command's stderr to its stdout.
void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; }
// Returns a string representation of the command. On many systems this will
// be the equivalent command line.
std::string toString() const {
std::stringstream SS;
for (auto arg : getArguments())
SS << arg << " ";
if (hasOutputFile())
SS << ">" << getOutputFile() << " ";
if (isOutAndErrCombined())
SS << "2>&1 ";
std::string result = SS.str();
if (!result.empty())
result = result.substr(0, result.length() - 1);
return result;
}
private:
Command(Command &&Other) = delete;
Command &operator=(Command &&Other) = delete;
Vector<std::string>::iterator endMutableArgs() {
return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
}
Vector<std::string>::const_iterator endMutableArgs() const {
return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
}
// The command arguments. Args[0] is the command name.
Vector<std::string> Args;
// True indicates stderr is redirected to stdout.
bool CombinedOutAndErr;
// If not empty, stdout is redirected to the named file.
std::string OutputFile;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_COMMAND_H

View File

@ -0,0 +1,581 @@
//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::InputCorpus
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_CORPUS
#define LLVM_FUZZER_CORPUS
#include "FuzzerDataFlowTrace.h"
#include "FuzzerDefs.h"
#include "FuzzerIO.h"
#include "FuzzerRandom.h"
#include "FuzzerSHA1.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <chrono>
#include <numeric>
#include <random>
#include <unordered_set>
namespace fuzzer {
struct InputInfo {
Unit U; // The actual input data.
std::chrono::microseconds TimeOfUnit;
uint8_t Sha1[kSHA1NumBytes]; // Checksum.
// Number of features that this input has and no smaller input has.
size_t NumFeatures = 0;
size_t Tmp = 0; // Used by ValidateFeatureSet.
// Stats.
size_t NumExecutedMutations = 0;
size_t NumSuccessfullMutations = 0;
bool NeverReduce = false;
bool MayDeleteFile = false;
bool Reduced = false;
bool HasFocusFunction = false;
Vector<uint32_t> UniqFeatureSet;
Vector<uint8_t> DataFlowTraceForFocusFunction;
// Power schedule.
bool NeedsEnergyUpdate = false;
double Energy = 0.0;
size_t SumIncidence = 0;
Vector<std::pair<uint32_t, uint16_t>> FeatureFreqs;
// Delete feature Idx and its frequency from FeatureFreqs.
bool DeleteFeatureFreq(uint32_t Idx) {
if (FeatureFreqs.empty())
return false;
// Binary search over local feature frequencies sorted by index.
auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(),
std::pair<uint32_t, uint16_t>(Idx, 0));
if (Lower != FeatureFreqs.end() && Lower->first == Idx) {
FeatureFreqs.erase(Lower);
return true;
}
return false;
}
// Assign more energy to a high-entropy seed, i.e., that reveals more
// information about the globally rare features in the neighborhood of the
// seed. Since we do not know the entropy of a seed that has never been
// executed we assign fresh seeds maximum entropy and let II->Energy approach
// the true entropy from above. If ScalePerExecTime is true, the computed
// entropy is scaled based on how fast this input executes compared to the
// average execution time of inputs. The faster an input executes, the more
// energy gets assigned to the input.
void UpdateEnergy(size_t GlobalNumberOfFeatures, bool ScalePerExecTime,
std::chrono::microseconds AverageUnitExecutionTime) {
Energy = 0.0;
SumIncidence = 0;
// Apply add-one smoothing to locally discovered features.
for (auto F : FeatureFreqs) {
size_t LocalIncidence = F.second + 1;
Energy -= LocalIncidence * logl(LocalIncidence);
SumIncidence += LocalIncidence;
}
// Apply add-one smoothing to locally undiscovered features.
// PreciseEnergy -= 0; // since logl(1.0) == 0)
SumIncidence += (GlobalNumberOfFeatures - FeatureFreqs.size());
// Add a single locally abundant feature apply add-one smoothing.
size_t AbdIncidence = NumExecutedMutations + 1;
Energy -= AbdIncidence * logl(AbdIncidence);
SumIncidence += AbdIncidence;
// Normalize.
if (SumIncidence != 0)
Energy = (Energy / SumIncidence) + logl(SumIncidence);
if (ScalePerExecTime) {
// Scaling to favor inputs with lower execution time.
uint32_t PerfScore = 100;
if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 10)
PerfScore = 10;
else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 4)
PerfScore = 25;
else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 2)
PerfScore = 50;
else if (TimeOfUnit.count() * 3 > AverageUnitExecutionTime.count() * 4)
PerfScore = 75;
else if (TimeOfUnit.count() * 4 < AverageUnitExecutionTime.count())
PerfScore = 300;
else if (TimeOfUnit.count() * 3 < AverageUnitExecutionTime.count())
PerfScore = 200;
else if (TimeOfUnit.count() * 2 < AverageUnitExecutionTime.count())
PerfScore = 150;
Energy *= PerfScore;
}
}
// Increment the frequency of the feature Idx.
void UpdateFeatureFrequency(uint32_t Idx) {
NeedsEnergyUpdate = true;
// The local feature frequencies is an ordered vector of pairs.
// If there are no local feature frequencies, push_back preserves order.
// Set the feature frequency for feature Idx32 to 1.
if (FeatureFreqs.empty()) {
FeatureFreqs.push_back(std::pair<uint32_t, uint16_t>(Idx, 1));
return;
}
// Binary search over local feature frequencies sorted by index.
auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(),
std::pair<uint32_t, uint16_t>(Idx, 0));
// If feature Idx32 already exists, increment its frequency.
// Otherwise, insert a new pair right after the next lower index.
if (Lower != FeatureFreqs.end() && Lower->first == Idx) {
Lower->second++;
} else {
FeatureFreqs.insert(Lower, std::pair<uint32_t, uint16_t>(Idx, 1));
}
}
};
struct EntropicOptions {
bool Enabled;
size_t NumberOfRarestFeatures;
size_t FeatureFrequencyThreshold;
bool ScalePerExecTime;
};
class InputCorpus {
static const uint32_t kFeatureSetSize = 1 << 21;
static const uint8_t kMaxMutationFactor = 20;
static const size_t kSparseEnergyUpdates = 100;
size_t NumExecutedMutations = 0;
EntropicOptions Entropic;
public:
InputCorpus(const std::string &OutputCorpus, EntropicOptions Entropic)
: Entropic(Entropic), OutputCorpus(OutputCorpus) {
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
}
~InputCorpus() {
for (auto II : Inputs)
delete II;
}
size_t size() const { return Inputs.size(); }
size_t SizeInBytes() const {
size_t Res = 0;
for (auto II : Inputs)
Res += II->U.size();
return Res;
}
size_t NumActiveUnits() const {
size_t Res = 0;
for (auto II : Inputs)
Res += !II->U.empty();
return Res;
}
size_t MaxInputSize() const {
size_t Res = 0;
for (auto II : Inputs)
Res = std::max(Res, II->U.size());
return Res;
}
void IncrementNumExecutedMutations() { NumExecutedMutations++; }
size_t NumInputsThatTouchFocusFunction() {
return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
return II->HasFocusFunction;
});
}
size_t NumInputsWithDataFlowTrace() {
return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
return !II->DataFlowTraceForFocusFunction.empty();
});
}
bool empty() const { return Inputs.empty(); }
const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile,
bool HasFocusFunction, bool NeverReduce,
std::chrono::microseconds TimeOfUnit,
const Vector<uint32_t> &FeatureSet,
const DataFlowTrace &DFT, const InputInfo *BaseII) {
assert(!U.empty());
if (FeatureDebug)
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
Inputs.push_back(new InputInfo());
InputInfo &II = *Inputs.back();
II.U = U;
II.NumFeatures = NumFeatures;
II.NeverReduce = NeverReduce;
II.TimeOfUnit = TimeOfUnit;
II.MayDeleteFile = MayDeleteFile;
II.UniqFeatureSet = FeatureSet;
II.HasFocusFunction = HasFocusFunction;
// Assign maximal energy to the new seed.
II.Energy = RareFeatures.empty() ? 1.0 : log(RareFeatures.size());
II.SumIncidence = RareFeatures.size();
II.NeedsEnergyUpdate = false;
std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end());
ComputeSHA1(U.data(), U.size(), II.Sha1);
auto Sha1Str = Sha1ToString(II.Sha1);
Hashes.insert(Sha1Str);
if (HasFocusFunction)
if (auto V = DFT.Get(Sha1Str))
II.DataFlowTraceForFocusFunction = *V;
// This is a gross heuristic.
// Ideally, when we add an element to a corpus we need to know its DFT.
// But if we don't, we'll use the DFT of its base input.
if (II.DataFlowTraceForFocusFunction.empty() && BaseII)
II.DataFlowTraceForFocusFunction = BaseII->DataFlowTraceForFocusFunction;
DistributionNeedsUpdate = true;
PrintCorpus();
// ValidateFeatureSet();
return &II;
}
// Debug-only
void PrintUnit(const Unit &U) {
if (!FeatureDebug) return;
for (uint8_t C : U) {
if (C != 'F' && C != 'U' && C != 'Z')
C = '.';
Printf("%c", C);
}
}
// Debug-only
void PrintFeatureSet(const Vector<uint32_t> &FeatureSet) {
if (!FeatureDebug) return;
Printf("{");
for (uint32_t Feature: FeatureSet)
Printf("%u,", Feature);
Printf("}");
}
// Debug-only
void PrintCorpus() {
if (!FeatureDebug) return;
Printf("======= CORPUS:\n");
int i = 0;
for (auto II : Inputs) {
if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) {
Printf("[%2d] ", i);
Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size());
PrintUnit(II->U);
Printf(" ");
PrintFeatureSet(II->UniqFeatureSet);
Printf("\n");
}
i++;
}
}
void Replace(InputInfo *II, const Unit &U) {
assert(II->U.size() > U.size());
Hashes.erase(Sha1ToString(II->Sha1));
DeleteFile(*II);
ComputeSHA1(U.data(), U.size(), II->Sha1);
Hashes.insert(Sha1ToString(II->Sha1));
II->U = U;
II->Reduced = true;
DistributionNeedsUpdate = true;
}
bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); }
bool HasUnit(const std::string &H) { return Hashes.count(H); }
InputInfo &ChooseUnitToMutate(Random &Rand) {
InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)];
assert(!II.U.empty());
return II;
}
InputInfo &ChooseUnitToCrossOverWith(Random &Rand, bool UniformDist) {
if (!UniformDist) {
return ChooseUnitToMutate(Rand);
}
InputInfo &II = *Inputs[Rand(Inputs.size())];
assert(!II.U.empty());
return II;
}
// Returns an index of random unit from the corpus to mutate.
size_t ChooseUnitIdxToMutate(Random &Rand) {
UpdateCorpusDistribution(Rand);
size_t Idx = static_cast<size_t>(CorpusDistribution(Rand));
assert(Idx < Inputs.size());
return Idx;
}
void PrintStats() {
for (size_t i = 0; i < Inputs.size(); i++) {
const auto &II = *Inputs[i];
Printf(" [% 3zd %s] sz: % 5zd runs: % 5zd succ: % 5zd focus: %d\n", i,
Sha1ToString(II.Sha1).c_str(), II.U.size(),
II.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction);
}
}
void PrintFeatureSet() {
for (size_t i = 0; i < kFeatureSetSize; i++) {
if(size_t Sz = GetFeature(i))
Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz);
}
Printf("\n\t");
for (size_t i = 0; i < Inputs.size(); i++)
if (size_t N = Inputs[i]->NumFeatures)
Printf(" %zd=>%zd ", i, N);
Printf("\n");
}
void DeleteFile(const InputInfo &II) {
if (!OutputCorpus.empty() && II.MayDeleteFile)
RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
}
void DeleteInput(size_t Idx) {
InputInfo &II = *Inputs[Idx];
DeleteFile(II);
Unit().swap(II.U);
II.Energy = 0.0;
II.NeedsEnergyUpdate = false;
DistributionNeedsUpdate = true;
if (FeatureDebug)
Printf("EVICTED %zd\n", Idx);
}
void AddRareFeature(uint32_t Idx) {
// Maintain *at least* TopXRarestFeatures many rare features
// and all features with a frequency below ConsideredRare.
// Remove all other features.
while (RareFeatures.size() > Entropic.NumberOfRarestFeatures &&
FreqOfMostAbundantRareFeature > Entropic.FeatureFrequencyThreshold) {
// Find most and second most abbundant feature.
uint32_t MostAbundantRareFeatureIndices[2] = {RareFeatures[0],
RareFeatures[0]};
size_t Delete = 0;
for (size_t i = 0; i < RareFeatures.size(); i++) {
uint32_t Idx2 = RareFeatures[i];
if (GlobalFeatureFreqs[Idx2] >=
GlobalFeatureFreqs[MostAbundantRareFeatureIndices[0]]) {
MostAbundantRareFeatureIndices[1] = MostAbundantRareFeatureIndices[0];
MostAbundantRareFeatureIndices[0] = Idx2;
Delete = i;
}
}
// Remove most abundant rare feature.
RareFeatures[Delete] = RareFeatures.back();
RareFeatures.pop_back();
for (auto II : Inputs) {
if (II->DeleteFeatureFreq(MostAbundantRareFeatureIndices[0]))
II->NeedsEnergyUpdate = true;
}
// Set 2nd most abundant as the new most abundant feature count.
FreqOfMostAbundantRareFeature =
GlobalFeatureFreqs[MostAbundantRareFeatureIndices[1]];
}
// Add rare feature, handle collisions, and update energy.
RareFeatures.push_back(Idx);
GlobalFeatureFreqs[Idx] = 0;
for (auto II : Inputs) {
II->DeleteFeatureFreq(Idx);
// Apply add-one smoothing to this locally undiscovered feature.
// Zero energy seeds will never be fuzzed and remain zero energy.
if (II->Energy > 0.0) {
II->SumIncidence += 1;
II->Energy += logl(II->SumIncidence) / II->SumIncidence;
}
}
DistributionNeedsUpdate = true;
}
bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
assert(NewSize);
Idx = Idx % kFeatureSetSize;
uint32_t OldSize = GetFeature(Idx);
if (OldSize == 0 || (Shrink && OldSize > NewSize)) {
if (OldSize > 0) {
size_t OldIdx = SmallestElementPerFeature[Idx];
InputInfo &II = *Inputs[OldIdx];
assert(II.NumFeatures > 0);
II.NumFeatures--;
if (II.NumFeatures == 0)
DeleteInput(OldIdx);
} else {
NumAddedFeatures++;
if (Entropic.Enabled)
AddRareFeature((uint32_t)Idx);
}
NumUpdatedFeatures++;
if (FeatureDebug)
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
SmallestElementPerFeature[Idx] = Inputs.size();
InputSizesPerFeature[Idx] = NewSize;
return true;
}
return false;
}
// Increment frequency of feature Idx globally and locally.
void UpdateFeatureFrequency(InputInfo *II, size_t Idx) {
uint32_t Idx32 = Idx % kFeatureSetSize;
// Saturated increment.
if (GlobalFeatureFreqs[Idx32] == 0xFFFF)
return;
uint16_t Freq = GlobalFeatureFreqs[Idx32]++;
// Skip if abundant.
if (Freq > FreqOfMostAbundantRareFeature ||
std::find(RareFeatures.begin(), RareFeatures.end(), Idx32) ==
RareFeatures.end())
return;
// Update global frequencies.
if (Freq == FreqOfMostAbundantRareFeature)
FreqOfMostAbundantRareFeature++;
// Update local frequencies.
if (II)
II->UpdateFeatureFrequency(Idx32);
}
size_t NumFeatures() const { return NumAddedFeatures; }
size_t NumFeatureUpdates() const { return NumUpdatedFeatures; }
private:
static const bool FeatureDebug = false;
size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
void ValidateFeatureSet() {
if (FeatureDebug)
PrintFeatureSet();
for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++)
if (GetFeature(Idx))
Inputs[SmallestElementPerFeature[Idx]]->Tmp++;
for (auto II: Inputs) {
if (II->Tmp != II->NumFeatures)
Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures);
assert(II->Tmp == II->NumFeatures);
II->Tmp = 0;
}
}
// Updates the probability distribution for the units in the corpus.
// Must be called whenever the corpus or unit weights are changed.
//
// Hypothesis: inputs that maximize information about globally rare features
// are interesting.
void UpdateCorpusDistribution(Random &Rand) {
// Skip update if no seeds or rare features were added/deleted.
// Sparse updates for local change of feature frequencies,
// i.e., randomly do not skip.
if (!DistributionNeedsUpdate &&
(!Entropic.Enabled || Rand(kSparseEnergyUpdates)))
return;
DistributionNeedsUpdate = false;
size_t N = Inputs.size();
assert(N);
Intervals.resize(N + 1);
Weights.resize(N);
std::iota(Intervals.begin(), Intervals.end(), 0);
std::chrono::microseconds AverageUnitExecutionTime(0);
for (auto II : Inputs) {
AverageUnitExecutionTime += II->TimeOfUnit;
}
AverageUnitExecutionTime /= N;
bool VanillaSchedule = true;
if (Entropic.Enabled) {
for (auto II : Inputs) {
if (II->NeedsEnergyUpdate && II->Energy != 0.0) {
II->NeedsEnergyUpdate = false;
II->UpdateEnergy(RareFeatures.size(), Entropic.ScalePerExecTime,
AverageUnitExecutionTime);
}
}
for (size_t i = 0; i < N; i++) {
if (Inputs[i]->NumFeatures == 0) {
// If the seed doesn't represent any features, assign zero energy.
Weights[i] = 0.;
} else if (Inputs[i]->NumExecutedMutations / kMaxMutationFactor >
NumExecutedMutations / Inputs.size()) {
// If the seed was fuzzed a lot more than average, assign zero energy.
Weights[i] = 0.;
} else {
// Otherwise, simply assign the computed energy.
Weights[i] = Inputs[i]->Energy;
}
// If energy for all seeds is zero, fall back to vanilla schedule.
if (Weights[i] > 0.0)
VanillaSchedule = false;
}
}
if (VanillaSchedule) {
for (size_t i = 0; i < N; i++)
Weights[i] = Inputs[i]->NumFeatures
? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1)
: 0.;
}
if (FeatureDebug) {
for (size_t i = 0; i < N; i++)
Printf("%zd ", Inputs[i]->NumFeatures);
Printf("SCORE\n");
for (size_t i = 0; i < N; i++)
Printf("%f ", Weights[i]);
Printf("Weights\n");
}
CorpusDistribution = std::piecewise_constant_distribution<double>(
Intervals.begin(), Intervals.end(), Weights.begin());
}
std::piecewise_constant_distribution<double> CorpusDistribution;
Vector<double> Intervals;
Vector<double> Weights;
std::unordered_set<std::string> Hashes;
Vector<InputInfo*> Inputs;
size_t NumAddedFeatures = 0;
size_t NumUpdatedFeatures = 0;
uint32_t InputSizesPerFeature[kFeatureSetSize];
uint32_t SmallestElementPerFeature[kFeatureSetSize];
bool DistributionNeedsUpdate = true;
uint16_t FreqOfMostAbundantRareFeature = 0;
uint16_t GlobalFeatureFreqs[kFeatureSetSize] = {};
Vector<uint32_t> RareFeatures;
std::string OutputCorpus;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_CORPUS

View File

@ -0,0 +1,60 @@
//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Cross over test inputs.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include <cstring>
namespace fuzzer {
// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2,
uint8_t *Out, size_t MaxOutSize) {
assert(Size1 || Size2);
MaxOutSize = Rand(MaxOutSize) + 1;
size_t OutPos = 0;
size_t Pos1 = 0;
size_t Pos2 = 0;
size_t * InPos = &Pos1;
size_t InSize = Size1;
const uint8_t *Data = Data1;
bool CurrentlyUsingFirstData = true;
while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) {
// Merge a part of Data into Out.
size_t OutSizeLeft = MaxOutSize - OutPos;
if (*InPos < InSize) {
size_t InSizeLeft = InSize - *InPos;
size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft);
size_t ExtraSize = Rand(MaxExtraSize) + 1;
memcpy(Out + OutPos, Data + *InPos, ExtraSize);
OutPos += ExtraSize;
(*InPos) += ExtraSize;
}
// Use the other input data on the next iteration.
InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
InSize = CurrentlyUsingFirstData ? Size2 : Size1;
Data = CurrentlyUsingFirstData ? Data2 : Data1;
CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
}
return OutPos;
}
} // namespace fuzzer

View File

@ -0,0 +1,344 @@
//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::DataFlowTrace
//===----------------------------------------------------------------------===//
#include "FuzzerDataFlowTrace.h"
#include "FuzzerCommand.h"
#include "FuzzerIO.h"
#include "FuzzerRandom.h"
#include "FuzzerSHA1.h"
#include "FuzzerUtil.h"
#include <cstdlib>
#include <fstream>
#include <numeric>
#include <queue>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace fuzzer {
static const char *kFunctionsTxt = "functions.txt";
bool BlockCoverage::AppendCoverage(const std::string &S) {
std::stringstream SS(S);
return AppendCoverage(SS);
}
// Coverage lines have this form:
// CN X Y Z T
// where N is the number of the function, T is the total number of instrumented
// BBs, and X,Y,Z, if present, are the indecies of covered BB.
// BB #0, which is the entry block, is not explicitly listed.
bool BlockCoverage::AppendCoverage(std::istream &IN) {
std::string L;
while (std::getline(IN, L, '\n')) {
if (L.empty()) continue;
std::stringstream SS(L.c_str() + 1);
size_t FunctionId = 0;
SS >> FunctionId;
if (L[0] == 'F') {
FunctionsWithDFT.insert(FunctionId);
continue;
}
if (L[0] != 'C') continue;
Vector<uint32_t> CoveredBlocks;
while (true) {
uint32_t BB = 0;
SS >> BB;
if (!SS) break;
CoveredBlocks.push_back(BB);
}
if (CoveredBlocks.empty()) return false;
uint32_t NumBlocks = CoveredBlocks.back();
CoveredBlocks.pop_back();
for (auto BB : CoveredBlocks)
if (BB >= NumBlocks) return false;
auto It = Functions.find(FunctionId);
auto &Counters =
It == Functions.end()
? Functions.insert({FunctionId, Vector<uint32_t>(NumBlocks)})
.first->second
: It->second;
if (Counters.size() != NumBlocks) return false; // wrong number of blocks.
Counters[0]++;
for (auto BB : CoveredBlocks)
Counters[BB]++;
}
return true;
}
// Assign weights to each function.
// General principles:
// * any uncovered function gets weight 0.
// * a function with lots of uncovered blocks gets bigger weight.
// * a function with a less frequently executed code gets bigger weight.
Vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const {
Vector<double> Res(NumFunctions);
for (auto It : Functions) {
auto FunctionID = It.first;
auto Counters = It.second;
assert(FunctionID < NumFunctions);
auto &Weight = Res[FunctionID];
// Give higher weight if the function has a DFT.
Weight = FunctionsWithDFT.count(FunctionID) ? 1000. : 1;
// Give higher weight to functions with less frequently seen basic blocks.
Weight /= SmallestNonZeroCounter(Counters);
// Give higher weight to functions with the most uncovered basic blocks.
Weight *= NumberOfUncoveredBlocks(Counters) + 1;
}
return Res;
}
void DataFlowTrace::ReadCoverage(const std::string &DirPath) {
Vector<SizedFile> Files;
GetSizedFilesFromDir(DirPath, &Files);
for (auto &SF : Files) {
auto Name = Basename(SF.File);
if (Name == kFunctionsTxt) continue;
if (!CorporaHashes.count(Name)) continue;
std::ifstream IF(SF.File);
Coverage.AppendCoverage(IF);
}
}
static void DFTStringAppendToVector(Vector<uint8_t> * DFT,
const std::string &DFTString) {
assert(DFT->size() == DFTString.size());
for (size_t I = 0, Len = DFT->size(); I < Len; I++)
(*DFT)[I] = DFTString[I] == '1';
}
// converts a string of '0' and '1' into a Vector<uint8_t>
static Vector<uint8_t> DFTStringToVector(const std::string &DFTString) {
Vector<uint8_t> DFT(DFTString.size());
DFTStringAppendToVector(&DFT, DFTString);
return DFT;
}
static bool ParseError(const char *Err, const std::string &Line) {
Printf("DataFlowTrace: parse error: %s: Line: %s\n", Err, Line.c_str());
return false;
}
// TODO(metzman): replace std::string with std::string_view for
// better performance. Need to figure our how to use string_view on Windows.
static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum,
std::string *DFTString) {
if (!Line.empty() && Line[0] != 'F') return false; // Ignore coverage.
size_t SpacePos = Line.find(' ');
if (SpacePos == std::string::npos)
return ParseError("no space in the trace line", Line);
if (Line.empty() || Line[0] != 'F')
return ParseError("the trace line doesn't start with 'F'", Line);
*FunctionNum = std::atol(Line.c_str() + 1);
const char *Beg = Line.c_str() + SpacePos + 1;
const char *End = Line.c_str() + Line.size();
assert(Beg < End);
size_t Len = End - Beg;
for (size_t I = 0; I < Len; I++) {
if (Beg[I] != '0' && Beg[I] != '1')
return ParseError("the trace should contain only 0 or 1", Line);
}
*DFTString = Beg;
return true;
}
bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
Vector<SizedFile> &CorporaFiles, Random &Rand) {
if (DirPath.empty()) return false;
Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str());
Vector<SizedFile> Files;
GetSizedFilesFromDir(DirPath, &Files);
std::string L;
size_t FocusFuncIdx = SIZE_MAX;
Vector<std::string> FunctionNames;
// Collect the hashes of the corpus files.
for (auto &SF : CorporaFiles)
CorporaHashes.insert(Hash(FileToVector(SF.File)));
// Read functions.txt
std::ifstream IF(DirPlusFile(DirPath, kFunctionsTxt));
size_t NumFunctions = 0;
while (std::getline(IF, L, '\n')) {
FunctionNames.push_back(L);
NumFunctions++;
if (*FocusFunction == L) FocusFuncIdx = NumFunctions - 1;
}
if (!NumFunctions) return false;
if (*FocusFunction == "auto") {
// AUTOFOCUS works like this:
// * reads the coverage data from the DFT files.
// * assigns weights to functions based on coverage.
// * chooses a random function according to the weights.
ReadCoverage(DirPath);
auto Weights = Coverage.FunctionWeights(NumFunctions);
Vector<double> Intervals(NumFunctions + 1);
std::iota(Intervals.begin(), Intervals.end(), 0);
auto Distribution = std::piecewise_constant_distribution<double>(
Intervals.begin(), Intervals.end(), Weights.begin());
FocusFuncIdx = static_cast<size_t>(Distribution(Rand));
*FocusFunction = FunctionNames[FocusFuncIdx];
assert(FocusFuncIdx < NumFunctions);
Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx,
FunctionNames[FocusFuncIdx].c_str());
for (size_t i = 0; i < NumFunctions; i++) {
if (!Weights[i]) continue;
Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i,
Weights[i], Coverage.GetNumberOfBlocks(i),
Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0),
FunctionNames[i].c_str());
}
}
if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1)
return false;
// Read traces.
size_t NumTraceFiles = 0;
size_t NumTracesWithFocusFunction = 0;
for (auto &SF : Files) {
auto Name = Basename(SF.File);
if (Name == kFunctionsTxt) continue;
if (!CorporaHashes.count(Name)) continue; // not in the corpus.
NumTraceFiles++;
// Printf("=== %s\n", Name.c_str());
std::ifstream IF(SF.File);
while (std::getline(IF, L, '\n')) {
size_t FunctionNum = 0;
std::string DFTString;
if (ParseDFTLine(L, &FunctionNum, &DFTString) &&
FunctionNum == FocusFuncIdx) {
NumTracesWithFocusFunction++;
if (FunctionNum >= NumFunctions)
return ParseError("N is greater than the number of functions", L);
Traces[Name] = DFTStringToVector(DFTString);
// Print just a few small traces.
if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16)
Printf("%s => |%s|\n", Name.c_str(), std::string(DFTString).c_str());
break; // No need to parse the following lines.
}
}
}
Printf(
"INFO: DataFlowTrace: %zd trace files, %zd functions, "
"%zd traces with focus function\n",
NumTraceFiles, NumFunctions, NumTracesWithFocusFunction);
return NumTraceFiles > 0;
}
int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath,
const Vector<SizedFile> &CorporaFiles) {
Printf("INFO: collecting data flow: bin: %s dir: %s files: %zd\n",
DFTBinary.c_str(), DirPath.c_str(), CorporaFiles.size());
if (CorporaFiles.empty()) {
Printf("ERROR: can't collect data flow without corpus provided.");
return 1;
}
static char DFSanEnv[] = "DFSAN_OPTIONS=warn_unimplemented=0";
putenv(DFSanEnv);
MkDir(DirPath);
for (auto &F : CorporaFiles) {
// For every input F we need to collect the data flow and the coverage.
// Data flow collection may fail if we request too many DFSan tags at once.
// So, we start from requesting all tags in range [0,Size) and if that fails
// we then request tags in [0,Size/2) and [Size/2, Size), and so on.
// Function number => DFT.
auto OutPath = DirPlusFile(DirPath, Hash(FileToVector(F.File)));
std::unordered_map<size_t, Vector<uint8_t>> DFTMap;
std::unordered_set<std::string> Cov;
Command Cmd;
Cmd.addArgument(DFTBinary);
Cmd.addArgument(F.File);
Cmd.addArgument(OutPath);
Printf("CMD: %s\n", Cmd.toString().c_str());
ExecuteCommand(Cmd);
}
// Write functions.txt if it's currently empty or doesn't exist.
auto FunctionsTxtPath = DirPlusFile(DirPath, kFunctionsTxt);
if (FileToString(FunctionsTxtPath).empty()) {
Command Cmd;
Cmd.addArgument(DFTBinary);
Cmd.setOutputFile(FunctionsTxtPath);
ExecuteCommand(Cmd);
}
return 0;
}
} // namespace fuzzer

View File

@ -0,0 +1,135 @@
//===- FuzzerDataFlowTrace.h - Internal header for the Fuzzer ---*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::DataFlowTrace; reads and handles a data-flow trace.
//
// A data flow trace is generated by e.g. dataflow/DataFlow.cpp
// and is stored on disk in a separate directory.
//
// The trace dir contains a file 'functions.txt' which lists function names,
// oner per line, e.g.
// ==> functions.txt <==
// Func2
// LLVMFuzzerTestOneInput
// Func1
//
// All other files in the dir are the traces, see dataflow/DataFlow.cpp.
// The name of the file is sha1 of the input used to generate the trace.
//
// Current status:
// the data is parsed and the summary is printed, but the data is not yet
// used in any other way.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_DATA_FLOW_TRACE
#define LLVM_FUZZER_DATA_FLOW_TRACE
#include "FuzzerDefs.h"
#include "FuzzerIO.h"
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <string>
namespace fuzzer {
int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath,
const Vector<SizedFile> &CorporaFiles);
class BlockCoverage {
public:
bool AppendCoverage(std::istream &IN);
bool AppendCoverage(const std::string &S);
size_t NumCoveredFunctions() const { return Functions.size(); }
uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) {
auto It = Functions.find(FunctionId);
if (It == Functions.end()) return 0;
const auto &Counters = It->second;
if (BasicBlockId < Counters.size())
return Counters[BasicBlockId];
return 0;
}
uint32_t GetNumberOfBlocks(size_t FunctionId) {
auto It = Functions.find(FunctionId);
if (It == Functions.end()) return 0;
const auto &Counters = It->second;
return Counters.size();
}
uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) {
auto It = Functions.find(FunctionId);
if (It == Functions.end()) return 0;
const auto &Counters = It->second;
uint32_t Result = 0;
for (auto Cnt: Counters)
if (Cnt)
Result++;
return Result;
}
Vector<double> FunctionWeights(size_t NumFunctions) const;
void clear() { Functions.clear(); }
private:
typedef Vector<uint32_t> CoverageVector;
uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const {
uint32_t Res = 0;
for (auto Cnt : Counters)
if (Cnt)
Res++;
return Res;
}
uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const {
return Counters.size() - NumberOfCoveredBlocks(Counters);
}
uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const {
assert(!Counters.empty());
uint32_t Res = Counters[0];
for (auto Cnt : Counters)
if (Cnt)
Res = Min(Res, Cnt);
assert(Res);
return Res;
}
// Function ID => vector of counters.
// Each counter represents how many input files trigger the given basic block.
std::unordered_map<size_t, CoverageVector> Functions;
// Functions that have DFT entry.
std::unordered_set<size_t> FunctionsWithDFT;
};
class DataFlowTrace {
public:
void ReadCoverage(const std::string &DirPath);
bool Init(const std::string &DirPath, std::string *FocusFunction,
Vector<SizedFile> &CorporaFiles, Random &Rand);
void Clear() { Traces.clear(); }
const Vector<uint8_t> *Get(const std::string &InputSha1) const {
auto It = Traces.find(InputSha1);
if (It != Traces.end())
return &It->second;
return nullptr;
}
private:
// Input's sha1 => DFT for the FocusFunction.
std::unordered_map<std::string, Vector<uint8_t> > Traces;
BlockCoverage Coverage;
std::unordered_set<std::string> CorporaHashes;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_DATA_FLOW_TRACE

View File

@ -0,0 +1,75 @@
//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Basic definitions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_DEFS_H
#define LLVM_FUZZER_DEFS_H
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <set>
#include <string>
#include <vector>
namespace fuzzer {
template <class T> T Min(T a, T b) { return a < b ? a : b; }
template <class T> T Max(T a, T b) { return a > b ? a : b; }
class Random;
class Dictionary;
class DictionaryEntry;
class MutationDispatcher;
struct FuzzingOptions;
class InputCorpus;
struct InputInfo;
struct ExternalFunctions;
// Global interface to functions that may or may not be available.
extern ExternalFunctions *EF;
// We are using a custom allocator to give a different symbol name to STL
// containers in order to avoid ODR violations.
template<typename T>
class fuzzer_allocator: public std::allocator<T> {
public:
fuzzer_allocator() = default;
template<class U>
fuzzer_allocator(const fuzzer_allocator<U>&) {}
template<class Other>
struct rebind { typedef fuzzer_allocator<Other> other; };
};
template<typename T>
using Vector = std::vector<T, fuzzer_allocator<T>>;
template<typename T>
using Set = std::set<T, std::less<T>, fuzzer_allocator<T>>;
typedef Vector<uint8_t> Unit;
typedef Vector<Unit> UnitVector;
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
uint8_t *ExtraCountersBegin();
uint8_t *ExtraCountersEnd();
void ClearExtraCounters();
extern bool RunningUserCallback;
} // namespace fuzzer
#endif // LLVM_FUZZER_DEFS_H

View File

@ -0,0 +1,118 @@
//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::Dictionary
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_DICTIONARY_H
#define LLVM_FUZZER_DICTIONARY_H
#include "FuzzerDefs.h"
#include "FuzzerIO.h"
#include "FuzzerUtil.h"
#include <algorithm>
#include <limits>
namespace fuzzer {
// A simple POD sized array of bytes.
template <size_t kMaxSizeT> class FixedWord {
public:
static const size_t kMaxSize = kMaxSizeT;
FixedWord() {}
FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); }
void Set(const uint8_t *B, uint8_t S) {
assert(S <= kMaxSize);
memcpy(Data, B, S);
Size = S;
}
bool operator==(const FixedWord<kMaxSize> &w) const {
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
}
static size_t GetMaxSize() { return kMaxSize; }
const uint8_t *data() const { return Data; }
uint8_t size() const { return Size; }
private:
uint8_t Size = 0;
uint8_t Data[kMaxSize];
};
typedef FixedWord<64> Word;
class DictionaryEntry {
public:
DictionaryEntry() {}
DictionaryEntry(Word W) : W(W) {}
DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {}
const Word &GetW() const { return W; }
bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); }
size_t GetPositionHint() const {
assert(HasPositionHint());
return PositionHint;
}
void IncUseCount() { UseCount++; }
void IncSuccessCount() { SuccessCount++; }
size_t GetUseCount() const { return UseCount; }
size_t GetSuccessCount() const {return SuccessCount; }
void Print(const char *PrintAfter = "\n") {
PrintASCII(W.data(), W.size());
if (HasPositionHint())
Printf("@%zd", GetPositionHint());
Printf("%s", PrintAfter);
}
private:
Word W;
size_t PositionHint = std::numeric_limits<size_t>::max();
size_t UseCount = 0;
size_t SuccessCount = 0;
};
class Dictionary {
public:
static const size_t kMaxDictSize = 1 << 14;
bool ContainsWord(const Word &W) const {
return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
return DE.GetW() == W;
});
}
const DictionaryEntry *begin() const { return &DE[0]; }
const DictionaryEntry *end() const { return begin() + Size; }
DictionaryEntry & operator[] (size_t Idx) {
assert(Idx < Size);
return DE[Idx];
}
void push_back(DictionaryEntry DE) {
if (Size < kMaxDictSize)
this->DE[Size++] = DE;
}
void clear() { Size = 0; }
bool empty() const { return Size == 0; }
size_t size() const { return Size; }
private:
DictionaryEntry DE[kMaxDictSize];
size_t Size = 0;
};
// Parses one dictionary entry.
// If successful, write the enty to Unit and returns true,
// otherwise returns false.
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
// Parses the dictionary file, fills Units, returns true iff all lines
// were parsed successfully.
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units);
} // namespace fuzzer
#endif // LLVM_FUZZER_DICTIONARY_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This defines the external function pointers that
// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The
// EXT_FUNC macro must be defined at the point of inclusion. The signature of
// the macro is:
//
// EXT_FUNC(<name>, <return_type>, <function_signature>, <warn_if_missing>)
//===----------------------------------------------------------------------===//
// Optional user functions
EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false);
EXT_FUNC(LLVMFuzzerCustomMutator, size_t,
(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed),
false);
EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2,
uint8_t *Out, size_t MaxOutSize, unsigned int Seed),
false);
// Sanitizer functions
EXT_FUNC(__lsan_enable, void, (), false);
EXT_FUNC(__lsan_disable, void, (), false);
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
EXT_FUNC(__sanitizer_acquire_crash_state, int, (), true);
EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
(void (*malloc_hook)(const volatile void *, size_t),
void (*free_hook)(const volatile void *)),
false);
EXT_FUNC(__sanitizer_log_write, void, (const char *buf, size_t len), false);
EXT_FUNC(__sanitizer_purge_allocator, void, (), false);
EXT_FUNC(__sanitizer_print_memory_profile, void, (size_t, size_t), false);
EXT_FUNC(__sanitizer_print_stack_trace, void, (), true);
EXT_FUNC(__sanitizer_symbolize_pc, void,
(void *, const char *fmt, char *out_buf, size_t out_buf_size), false);
EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int,
(void *pc, char *module_path,
size_t module_path_len,void **pc_offset), false);
EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false);
EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false);
EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false);
EXT_FUNC(__msan_unpoison_param, void, (size_t n), false);

View File

@ -0,0 +1,34 @@
//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Defines an interface to (possibly optional) functions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H
#define LLVM_FUZZER_EXT_FUNCTIONS_H
#include <stddef.h>
#include <stdint.h>
namespace fuzzer {
struct ExternalFunctions {
// Initialize function pointers. Functions that are not available will be set
// to nullptr. Do not call this constructor before ``main()`` has been
// entered.
ExternalFunctions();
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
RETURN_TYPE(*NAME) FUNC_SIG = nullptr
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
};
} // namespace fuzzer
#endif

View File

@ -0,0 +1,60 @@
//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Implementation for operating systems that support dlsym(). We only use it on
// Apple platforms for now. We don't use this approach on Linux because it
// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker.
// That is a complication we don't wish to expose to clients right now.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_APPLE
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include <dlfcn.h>
using namespace fuzzer;
template <typename T>
static T GetFnPtr(const char *FnName, bool WarnIfMissing) {
dlerror(); // Clear any previous errors.
void *Fn = dlsym(RTLD_DEFAULT, FnName);
if (Fn == nullptr) {
if (WarnIfMissing) {
const char *ErrorMsg = dlerror();
Printf("WARNING: Failed to find function \"%s\".", FnName);
if (ErrorMsg) Printf(" Reason %s.", ErrorMsg);
Printf("\n");
}
}
return reinterpret_cast<T>(Fn);
}
namespace fuzzer {
ExternalFunctions::ExternalFunctions() {
\
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
this->NAME = GetFnPtr<decltype(ExternalFunctions::NAME)>(#NAME, WARN)
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
} // namespace fuzzer
#endif // LIBFUZZER_APPLE

View File

@ -0,0 +1,63 @@
//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Implementation for Linux. This relies on the linker's support for weak
// symbols. We don't use this approach on Apple platforms because it requires
// clients of LibFuzzer to pass ``-U _<symbol_name>`` to the linker to allow
// weak symbols to be undefined. That is a complication we don't want to expose
// to clients right now.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \
LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
extern "C" {
// Declare these symbols as weak to allow them to be optionally defined.
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
__attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
using namespace fuzzer;
static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) {
if (FnPtr == nullptr && WarnIfMissing) {
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
}
}
namespace fuzzer {
ExternalFunctions::ExternalFunctions() {
\
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
this->NAME = ::NAME; \
CheckFnPtr(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(::NAME)), \
#NAME, WARN);
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
} // namespace fuzzer
#endif

View File

@ -0,0 +1,95 @@
//=== FuzzerExtWindows.cpp - Interface to external functions --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Implementation of FuzzerExtFunctions for Windows. Uses alternatename when
// compiled with MSVC. Uses weak aliases when compiled with clang. Unfortunately
// the method each compiler supports is not supported by the other.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_WINDOWS
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
using namespace fuzzer;
// Intermediate macro to ensure the parameter is expanded before stringified.
#define STRINGIFY_(A) #A
#define STRINGIFY(A) STRINGIFY_(A)
#if LIBFUZZER_MSVC
// Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h
#if defined(_M_IX86) || defined(__i386__)
#define WIN_SYM_PREFIX "_"
#else
#define WIN_SYM_PREFIX
#endif
// Declare external functions as having alternativenames, so that we can
// determine if they are not defined.
#define EXTERNAL_FUNC(Name, Default) \
__pragma( \
comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \
Name) "=" WIN_SYM_PREFIX STRINGIFY(Default)))
#else
// Declare external functions as weak to allow them to default to a
// specified function if not defined explicitly. We must use weak symbols
// because clang's support for alternatename is not 100%, see
// https://bugs.llvm.org/show_bug.cgi?id=40218 for more details.
#define EXTERNAL_FUNC(Name, Default) \
__attribute__((weak, alias(STRINGIFY(Default))))
#endif // LIBFUZZER_MSVC
extern "C" {
\
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
RETURN_TYPE NAME##Def FUNC_SIG { \
\
Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
exit(1); \
\
} \
EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
template <typename T>
static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
if (Fun == FunDef) {
if (WarnIfMissing)
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
return nullptr;
}
return Fun;
}
namespace fuzzer {
ExternalFunctions::ExternalFunctions() {
\
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
} // namespace fuzzer
#endif // LIBFUZZER_WINDOWS

View File

@ -0,0 +1,71 @@
//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Extra coverage counters defined by user code.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#include <cstdint>
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \
LIBFUZZER_OPENBSD || LIBFUZZER_FUCHSIA || LIBFUZZER_EMSCRIPTEN
__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters;
__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters;
namespace fuzzer {
uint8_t *ExtraCountersBegin() {
return &__start___libfuzzer_extra_counters;
}
uint8_t *ExtraCountersEnd() {
return &__stop___libfuzzer_extra_counters;
}
ATTRIBUTE_NO_SANITIZE_ALL
void ClearExtraCounters() { // hand-written memset, don't asan-ify.
uintptr_t *Beg = reinterpret_cast<uintptr_t *>(ExtraCountersBegin());
uintptr_t *End = reinterpret_cast<uintptr_t *>(ExtraCountersEnd());
for (; Beg < End; Beg++) {
*Beg = 0;
__asm__ __volatile__("" : : : "memory");
}
}
} // namespace fuzzer
#else
// TODO: implement for other platforms.
namespace fuzzer {
uint8_t *ExtraCountersBegin() {
return nullptr;
}
uint8_t *ExtraCountersEnd() {
return nullptr;
}
void ClearExtraCounters() {
}
} // namespace fuzzer
#endif

View File

@ -0,0 +1,198 @@
//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the
// point of inclusion. We are not using any flag parsing library for better
// portability and independence.
//===----------------------------------------------------------------------===//
FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.")
FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.")
FUZZER_FLAG_INT(runs, -1,
"Number of individual test runs (-1 for infinite runs).")
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
"If 0, libFuzzer tries to guess a good value based on the corpus "
"and reports it. ")
FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, "
"then try larger inputs over time. Specifies the rate at which the length "
"limit is increased (smaller == faster). If 0, immediately try inputs with "
"size up to max_len. Default value is 0, if LLVMFuzzerCustomMutator is used.")
FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files "
"to use as an additional seed corpus. Alternatively, an \"@\" followed by "
"the name of a file containing the comma-separated list.")
FUZZER_FLAG_INT(keep_seed, 0, "If 1, keep seed inputs in the corpus even if "
"they do not produce new coverage. When used with |reduce_inputs==1|, the "
"seed inputs will never be reduced. This option can be useful when seeds are"
"not properly formed for the fuzz target but still have useful snippets.")
FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.")
FUZZER_FLAG_INT(cross_over_uniform_dist, 0, "Experimental. If 1, use a "
"uniform probability distribution when choosing inputs to cross over with. "
"Some of the inputs in the corpus may never get chosen for mutation "
"depending on the input mutation scheduling policy. With this flag, all "
"inputs, regardless of the input mutation scheduling policy, can be chosen "
"as an input to cross over with. This can be particularly useful with "
"|keep_seed==1|; all the initial seed inputs, even though they do not "
"increase coverage because they are not properly formed, will still be "
"chosen as an input to cross over with.")
FUZZER_FLAG_INT(mutate_depth, 5,
"Apply this number of consecutive mutations to each input.")
FUZZER_FLAG_INT(reduce_depth, 0, "Experimental/internal. "
"Reduce depth if mutations lose unique features")
FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup")
FUZZER_FLAG_INT(prefer_small, 1,
"If 1, always prefer smaller inputs during the corpus shuffle.")
FUZZER_FLAG_INT(
timeout, 1200,
"Timeout in seconds (if positive). "
"If one unit runs more than this number of seconds the process will abort.")
FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug "
"this exit code will be used.")
FUZZER_FLAG_INT(timeout_exitcode, 70, "When libFuzzer reports a timeout "
"this exit code will be used.")
FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
"time in seconds to run the fuzzer.")
FUZZER_FLAG_INT(help, 0, "Print help.")
FUZZER_FLAG_INT(fork, 0, "Experimental mode where fuzzing happens "
"in a subprocess")
FUZZER_FLAG_INT(ignore_timeouts, 1, "Ignore timeouts in fork mode")
FUZZER_FLAG_INT(ignore_ooms, 1, "Ignore OOMs in fork mode")
FUZZER_FLAG_INT(ignore_crashes, 0, "Ignore crashes in fork mode")
FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
"merged into the 1-st corpus. Only interesting units will be taken. "
"This flag can be used to minimize a corpus.")
FUZZER_FLAG_STRING(stop_file, "Stop fuzzing ASAP if this file exists")
FUZZER_FLAG_STRING(merge_inner, "internal flag")
FUZZER_FLAG_STRING(merge_control_file,
"Specify a control file used for the merge process. "
"If a merge process gets killed it tries to leave this file "
"in a state suitable for resuming the merge. "
"By default a temporary file will be used."
"The same file can be used for multistep merge process.")
FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided"
" crash input. Use with -runs=N or -max_total_time=N to limit "
"the number attempts."
" Use with -exact_artifact_path to specify the output."
" Combine with ASAN_OPTIONS=dedup_token_length=3 (or similar) to ensure that"
" the minimized input triggers the same crash."
)
FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided"
" crash input to make it contain fewer original bytes."
" Use with -exact_artifact_path to specify the output."
)
FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
FUZZER_FLAG_STRING(features_dir, "internal flag. Used to dump feature sets on disk."
"Every time a new input is added to the corpus, a corresponding file in the features_dir"
" is created containing the unique features of that input."
" Features are stored in binary format.")
FUZZER_FLAG_STRING(mutation_graph_file, "Saves a graph (in DOT format) to"
" mutation_graph_file. The graph contains a vertex for each input that has"
" unique coverage; directed edges are provided between parents and children"
" where the child has unique coverage, and are recorded with the type of"
" mutation that caused the child.")
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
FUZZER_FLAG_INT(use_memmem, 1,
"Use hints from intercepting memmem, strstr, etc")
FUZZER_FLAG_INT(use_value_profile, 0,
"Experimental. Use value profile to guide fuzzing.")
FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations")
FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus inputs.")
FUZZER_FLAG_INT(reduce_inputs, 1,
"Try to reduce the size of inputs while preserving their full feature sets")
FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
" this number of jobs in separate worker processes"
" with stdout/stderr redirected to fuzz-JOB.log.")
FUZZER_FLAG_UNSIGNED(workers, 0,
"Number of simultaneous worker processes to run the jobs."
" If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.")
FUZZER_FLAG_INT(reload, 1,
"Reload the main corpus every <N> seconds to get new units"
" discovered by other processes. If 0, disabled")
FUZZER_FLAG_INT(report_slow_units, 10,
"Report slowest units if they run for more than this number of seconds.")
FUZZER_FLAG_INT(only_ascii, 0,
"If 1, generate only ASCII (isprint+isspace) inputs.")
FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.")
FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, "
"timeout, or slow inputs) as "
"$(artifact_prefix)file")
FUZZER_FLAG_STRING(exact_artifact_path,
"Write the single artifact on failure (crash, timeout) "
"as $(exact_artifact_path). This overrides -artifact_prefix "
"and will not use checksum in the file name. Do not "
"use the same path for several parallel processes.")
FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.")
FUZZER_FLAG_INT(print_funcs, 2, "If >=1, print out at most this number of "
"newly covered functions.")
FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.")
FUZZER_FLAG_INT(print_corpus_stats, 0,
"If 1, print statistics on corpus elements at exit.")
FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text"
" at exit.")
FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.")
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.")
FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.")
FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.")
FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.")
FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.")
FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.")
FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.")
FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; "
"if 2, close stderr; if 3, close both. "
"Be careful, this will also close e.g. stderr of asan.")
FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled "
"try to detect memory leaks during fuzzing (i.e. not only at shut down).")
FUZZER_FLAG_INT(purge_allocator_interval, 1, "Purge allocator caches and "
"quarantines every <N> seconds. When rss_limit_mb is specified (>0), "
"purging starts when RSS exceeds 50% of rss_limit_mb. Pass "
"purge_allocator_interval=-1 to disable this functionality.")
FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. "
"If >= 2 will also print stack traces.")
FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon"
"reaching this limit of RSS memory usage.")
FUZZER_FLAG_INT(malloc_limit_mb, 0, "If non-zero, the fuzzer will exit "
"if the target tries to allocate this number of Mb with one malloc call. "
"If zero (default) same limit as rss_limit_mb is applied.")
FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates"
" from the given source location. Example: -exit_on_src_pos=foo.cc:123. "
"Used primarily for testing libFuzzer itself.")
FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
" was added to the corpus. "
"Used primarily for testing libFuzzer itself.")
FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed "
"after this one. Useful for fuzzers that need to do their own "
"argument parsing.")
FUZZER_FLAG_STRING(focus_function, "Experimental. "
"Fuzzing will focus on inputs that trigger calls to this function. "
"If -focus_function=auto and -data_flow_trace is used, libFuzzer "
"will choose the focus functions automatically. Disables -entropic when "
"specified.")
FUZZER_FLAG_INT(entropic, 1, "Enables entropic power schedule.")
FUZZER_FLAG_INT(entropic_feature_frequency_threshold, 0xFF, "Experimental. If "
"entropic is enabled, all features which are observed less often than "
"the specified value are considered as rare.")
FUZZER_FLAG_INT(entropic_number_of_rarest_features, 100, "Experimental. If "
"entropic is enabled, we keep track of the frequencies only for the "
"Top-X least abundant features (union features that are considered as "
"rare).")
FUZZER_FLAG_INT(entropic_scale_per_exec_time, 0, "Experimental. If 1, "
"the Entropic power schedule gets scaled based on the input execution "
"time. Inputs with lower execution time get scheduled more (up to 30x). "
"Note that, if 1, fuzzer stops from being deterministic even if a "
"non-zero random seed is given.")
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
FUZZER_DEPRECATED_FLAG(use_clang_coverage)
FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace")
FUZZER_FLAG_STRING(collect_data_flow,
"Experimental: collect the data flow trace")
FUZZER_FLAG_INT(create_missing_dirs, 0, "Automatically attempt to create "
"directories for arguments that would normally expect them to already "
"exist (i.e. artifact_prefix, exact_artifact_path, features_dir, corpus)")

View File

@ -0,0 +1,501 @@
//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Spawn and orchestrate separate fuzzing processes.
//===----------------------------------------------------------------------===//
#include "FuzzerCommand.h"
#include "FuzzerFork.h"
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include "FuzzerMerge.h"
#include "FuzzerSHA1.h"
#include "FuzzerTracePC.h"
#include "FuzzerUtil.h"
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <fstream>
#include <memory>
#include <mutex>
#include <queue>
#include <sstream>
#include <thread>
namespace fuzzer {
struct Stats {
size_t number_of_executed_units = 0;
size_t peak_rss_mb = 0;
size_t average_exec_per_sec = 0;
};
static Stats ParseFinalStatsFromLog(const std::string &LogPath) {
std::ifstream In(LogPath);
std::string Line;
Stats Res;
struct {
const char *Name;
size_t * Var;
} NameVarPairs[] = {
{"stat::number_of_executed_units:", &Res.number_of_executed_units},
{"stat::peak_rss_mb:", &Res.peak_rss_mb},
{"stat::average_exec_per_sec:", &Res.average_exec_per_sec},
{nullptr, nullptr},
};
while (std::getline(In, Line, '\n')) {
if (Line.find("stat::") != 0) continue;
std::istringstream ISS(Line);
std::string Name;
size_t Val;
ISS >> Name >> Val;
for (size_t i = 0; NameVarPairs[i].Name; i++)
if (Name == NameVarPairs[i].Name) *NameVarPairs[i].Var = Val;
}
return Res;
}
struct FuzzJob {
// Inputs.
Command Cmd;
std::string CorpusDir;
std::string FeaturesDir;
std::string LogPath;
std::string SeedListPath;
std::string CFPath;
size_t JobId;
int DftTimeInSeconds = 0;
// Fuzzing Outputs.
int ExitCode;
~FuzzJob() {
RemoveFile(CFPath);
RemoveFile(LogPath);
RemoveFile(SeedListPath);
RmDirRecursive(CorpusDir);
RmDirRecursive(FeaturesDir);
}
};
struct GlobalEnv {
Vector<std::string> Args;
Vector<std::string> CorpusDirs;
std::string MainCorpusDir;
std::string TempDir;
std::string DFTDir;
std::string DataFlowBinary;
Set<uint32_t> Features, Cov;
Set<std::string> FilesWithDFT;
Vector<std::string> Files;
Random * Rand;
std::chrono::system_clock::time_point ProcessStartTime;
int Verbosity = 0;
size_t NumTimeouts = 0;
size_t NumOOMs = 0;
size_t NumCrashes = 0;
size_t NumRuns = 0;
std::string StopFile() {
return DirPlusFile(TempDir, "STOP");
}
size_t secondsSinceProcessStartUp() const {
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - ProcessStartTime)
.count();
}
FuzzJob *CreateNewJob(size_t JobId) {
Command Cmd(Args);
Cmd.removeFlag("fork");
Cmd.removeFlag("runs");
Cmd.removeFlag("collect_data_flow");
for (auto &C : CorpusDirs) // Remove all corpora from the args.
Cmd.removeArgument(C);
Cmd.addFlag("reload", "0"); // working in an isolated dir, no reload.
Cmd.addFlag("print_final_stats", "1");
Cmd.addFlag("print_funcs", "0"); // no need to spend time symbolizing.
Cmd.addFlag("max_total_time", std::to_string(std::min((size_t)300, JobId)));
Cmd.addFlag("stop_file", StopFile());
if (!DataFlowBinary.empty()) {
Cmd.addFlag("data_flow_trace", DFTDir);
if (!Cmd.hasFlag("focus_function")) Cmd.addFlag("focus_function", "auto");
}
auto Job = new FuzzJob;
std::string Seeds;
if (size_t CorpusSubsetSize =
std::min(Files.size(), (size_t)sqrt(Files.size() + 2))) {
auto Time1 = std::chrono::system_clock::now();
for (size_t i = 0; i < CorpusSubsetSize; i++) {
auto &SF = Files[Rand->SkewTowardsLast(Files.size())];
Seeds += (Seeds.empty() ? "" : ",") + SF;
CollectDFT(SF);
}
auto Time2 = std::chrono::system_clock::now();
Job->DftTimeInSeconds = duration_cast<seconds>(Time2 - Time1).count();
}
if (!Seeds.empty()) {
Job->SeedListPath =
DirPlusFile(TempDir, std::to_string(JobId) + ".seeds");
WriteToFile(Seeds, Job->SeedListPath);
Cmd.addFlag("seed_inputs", "@" + Job->SeedListPath);
}
Job->LogPath = DirPlusFile(TempDir, std::to_string(JobId) + ".log");
Job->CorpusDir = DirPlusFile(TempDir, "C" + std::to_string(JobId));
Job->FeaturesDir = DirPlusFile(TempDir, "F" + std::to_string(JobId));
Job->CFPath = DirPlusFile(TempDir, std::to_string(JobId) + ".merge");
Job->JobId = JobId;
Cmd.addArgument(Job->CorpusDir);
Cmd.addFlag("features_dir", Job->FeaturesDir);
for (auto &D : {Job->CorpusDir, Job->FeaturesDir}) {
RmDirRecursive(D);
MkDir(D);
}
Cmd.setOutputFile(Job->LogPath);
Cmd.combineOutAndErr();
Job->Cmd = Cmd;
if (Verbosity >= 2)
Printf("Job %zd/%p Created: %s\n", JobId, Job,
Job->Cmd.toString().c_str());
// Start from very short runs and gradually increase them.
return Job;
}
void RunOneMergeJob(FuzzJob *Job) {
auto Stats = ParseFinalStatsFromLog(Job->LogPath);
NumRuns += Stats.number_of_executed_units;
Vector<SizedFile> TempFiles, MergeCandidates;
// Read all newly created inputs and their feature sets.
// Choose only those inputs that have new features.
GetSizedFilesFromDir(Job->CorpusDir, &TempFiles);
std::sort(TempFiles.begin(), TempFiles.end());
for (auto &F : TempFiles) {
auto FeatureFile = F.File;
FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir);
auto FeatureBytes = FileToVector(FeatureFile, 0, false);
assert((FeatureBytes.size() % sizeof(uint32_t)) == 0);
Vector<uint32_t> NewFeatures(FeatureBytes.size() / sizeof(uint32_t));
memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size());
for (auto Ft : NewFeatures) {
if (!Features.count(Ft)) {
MergeCandidates.push_back(F);
break;
}
}
}
// if (!FilesToAdd.empty() || Job->ExitCode != 0)
Printf(
"#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd "
"oom/timeout/crash: %zd/%zd/%zd time: %zds job: %zd dft_time: %d\n",
NumRuns, Cov.size(), Features.size(), Files.size(),
Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes,
secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds);
if (MergeCandidates.empty()) return;
Vector<std::string> FilesToAdd;
Set<uint32_t> NewFeatures, NewCov;
CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features,
&NewFeatures, Cov, &NewCov, Job->CFPath, false);
for (auto &Path : FilesToAdd) {
auto U = FileToVector(Path);
auto NewPath = DirPlusFile(MainCorpusDir, Hash(U));
WriteToFile(U, NewPath);
Files.push_back(NewPath);
}
Features.insert(NewFeatures.begin(), NewFeatures.end());
Cov.insert(NewCov.begin(), NewCov.end());
for (auto Idx : NewCov)
if (auto *TE = TPC.PCTableEntryByIdx(Idx))
if (TPC.PcIsFuncEntry(TE))
PrintPC(" NEW_FUNC: %p %F %L\n", "",
TPC.GetNextInstructionPc(TE->PC));
}
void CollectDFT(const std::string &InputPath) {
if (DataFlowBinary.empty()) return;
if (!FilesWithDFT.insert(InputPath).second) return;
Command Cmd(Args);
Cmd.removeFlag("fork");
Cmd.removeFlag("runs");
Cmd.addFlag("data_flow_trace", DFTDir);
Cmd.addArgument(InputPath);
for (auto &C : CorpusDirs) // Remove all corpora from the args.
Cmd.removeArgument(C);
Cmd.setOutputFile(DirPlusFile(TempDir, "dft.log"));
Cmd.combineOutAndErr();
// Printf("CollectDFT: %s\n", Cmd.toString().c_str());
ExecuteCommand(Cmd);
}
};
struct JobQueue {
std::queue<FuzzJob *> Qu;
std::mutex Mu;
std::condition_variable Cv;
void Push(FuzzJob *Job) {
{
std::lock_guard<std::mutex> Lock(Mu);
Qu.push(Job);
}
Cv.notify_one();
}
FuzzJob *Pop() {
std::unique_lock<std::mutex> Lk(Mu);
// std::lock_guard<std::mutex> Lock(Mu);
Cv.wait(Lk, [&] { return !Qu.empty(); });
assert(!Qu.empty());
auto Job = Qu.front();
Qu.pop();
return Job;
}
};
void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) {
while (auto Job = FuzzQ->Pop()) {
// Printf("WorkerThread: job %p\n", Job);
Job->ExitCode = ExecuteCommand(Job->Cmd);
MergeQ->Push(Job);
}
}
// This is just a skeleton of an experimental -fork=1 feature.
void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
const Vector<std::string> &Args,
const Vector<std::string> &CorpusDirs, int NumJobs) {
Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs);
GlobalEnv Env;
Env.Args = Args;
Env.CorpusDirs = CorpusDirs;
Env.Rand = &Rand;
Env.Verbosity = Options.Verbosity;
Env.ProcessStartTime = std::chrono::system_clock::now();
Env.DataFlowBinary = Options.CollectDataFlow;
Vector<SizedFile> SeedFiles;
for (auto &Dir : CorpusDirs)
GetSizedFilesFromDir(Dir, &SeedFiles);
std::sort(SeedFiles.begin(), SeedFiles.end());
Env.TempDir = TempPath("FuzzWithFork", ".dir");
Env.DFTDir = DirPlusFile(Env.TempDir, "DFT");
RmDirRecursive(Env.TempDir); // in case there is a leftover from old runs.
MkDir(Env.TempDir);
MkDir(Env.DFTDir);
if (CorpusDirs.empty())
MkDir(Env.MainCorpusDir = DirPlusFile(Env.TempDir, "C"));
else
Env.MainCorpusDir = CorpusDirs[0];
if (Options.KeepSeed) {
for (auto &File : SeedFiles)
Env.Files.push_back(File.File);
} else {
auto CFPath = DirPlusFile(Env.TempDir, "merge.txt");
CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features,
{}, &Env.Cov, CFPath, false);
RemoveFile(CFPath);
}
Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs,
Env.Files.size(), Env.TempDir.c_str());
int ExitCode = 0;
JobQueue FuzzQ, MergeQ;
auto StopJobs = [&]() {
for (int i = 0; i < NumJobs; i++)
FuzzQ.Push(nullptr);
MergeQ.Push(nullptr);
WriteToFile(Unit({1}), Env.StopFile());
};
size_t JobId = 1;
Vector<std::thread> Threads;
for (int t = 0; t < NumJobs; t++) {
Threads.push_back(std::thread(WorkerThread, &FuzzQ, &MergeQ));
FuzzQ.Push(Env.CreateNewJob(JobId++));
}
while (true) {
std::unique_ptr<FuzzJob> Job(MergeQ.Pop());
if (!Job) break;
ExitCode = Job->ExitCode;
if (ExitCode == Options.InterruptExitCode) {
Printf("==%lu== libFuzzer: a child was interrupted; exiting\n", GetPid());
StopJobs();
break;
}
Fuzzer::MaybeExitGracefully();
Env.RunOneMergeJob(Job.get());
// Continue if our crash is one of the ignorred ones.
if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode)
Env.NumTimeouts++;
else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode)
Env.NumOOMs++;
else if (ExitCode != 0) {
Env.NumCrashes++;
if (Options.IgnoreCrashes) {
std::ifstream In(Job->LogPath);
std::string Line;
while (std::getline(In, Line, '\n'))
if (Line.find("ERROR:") != Line.npos ||
Line.find("runtime error:") != Line.npos)
Printf("%s\n", Line.c_str());
} else {
// And exit if we don't ignore this crash.
Printf("INFO: log from the inner process:\n%s",
FileToString(Job->LogPath).c_str());
StopJobs();
break;
}
}
// Stop if we are over the time budget.
// This is not precise, since other threads are still running
// and we will wait while joining them.
// We also don't stop instantly: other jobs need to finish.
if (Options.MaxTotalTimeSec > 0 &&
Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) {
Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n",
Env.secondsSinceProcessStartUp());
StopJobs();
break;
}
if (Env.NumRuns >= Options.MaxNumberOfRuns) {
Printf("INFO: fuzzed for %zd iterations, wrapping up soon\n",
Env.NumRuns);
StopJobs();
break;
}
FuzzQ.Push(Env.CreateNewJob(JobId++));
}
for (auto &T : Threads)
T.join();
// The workers have terminated. Don't try to remove the directory before they
// terminate to avoid a race condition preventing cleanup on Windows.
RmDirRecursive(Env.TempDir);
// Use the exit code from the last child process.
Printf("INFO: exiting: %d time: %zds\n", ExitCode,
Env.secondsSinceProcessStartUp());
exit(ExitCode);
}
} // namespace fuzzer

View File

@ -0,0 +1,24 @@
//===- FuzzerFork.h - run fuzzing in sub-processes --------------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_FORK_H
#define LLVM_FUZZER_FORK_H
#include "FuzzerDefs.h"
#include "FuzzerOptions.h"
#include "FuzzerRandom.h"
#include <string>
namespace fuzzer {
void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
const Vector<std::string> &Args,
const Vector<std::string> &CorpusDirs, int NumJobs);
} // namespace fuzzer
#endif // LLVM_FUZZER_FORK_H

View File

@ -0,0 +1,255 @@
//===- FuzzerIO.cpp - IO utils. -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO functions.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include "FuzzerUtil.h"
#include <algorithm>
#include <cstdarg>
#include <fstream>
#include <iterator>
#include <sys/stat.h>
#include <sys/types.h>
namespace fuzzer {
static FILE *OutputFile = stderr;
long GetEpoch(const std::string &Path) {
struct stat St;
if (stat(Path.c_str(), &St)) return 0; // Can't stat, be conservative.
return St.st_mtime;
}
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
std::ifstream T(Path, std::ios::binary);
if (ExitOnError && !T) {
Printf("No such directory: %s; exiting\n", Path.c_str());
exit(1);
}
T.seekg(0, T.end);
auto EndPos = T.tellg();
if (EndPos < 0) return {};
size_t FileLen = EndPos;
if (MaxSize) FileLen = std::min(FileLen, MaxSize);
T.seekg(0, T.beg);
Unit Res(FileLen);
T.read(reinterpret_cast<char *>(Res.data()), FileLen);
return Res;
}
std::string FileToString(const std::string &Path) {
std::ifstream T(Path, std::ios::binary);
return std::string((std::istreambuf_iterator<char>(T)),
std::istreambuf_iterator<char>());
}
void CopyFileToErr(const std::string &Path) {
Printf("%s", FileToString(Path).c_str());
}
void WriteToFile(const Unit &U, const std::string &Path) {
WriteToFile(U.data(), U.size(), Path);
}
void WriteToFile(const std::string &Data, const std::string &Path) {
WriteToFile(reinterpret_cast<const uint8_t *>(Data.c_str()), Data.size(),
Path);
}
void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
return;
// Use raw C interface because this function may be called from a sig handler.
FILE *Out = fopen(Path.c_str(), "wb");
if (!Out) return;
fwrite(Data, sizeof(Data[0]), Size, Out);
fclose(Out);
}
void AppendToFile(const std::string &Data, const std::string &Path) {
return;
AppendToFile(reinterpret_cast<const uint8_t *>(Data.data()), Data.size(),
Path);
}
void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
return;
FILE *Out = fopen(Path.c_str(), "a");
if (!Out) return;
fwrite(Data, sizeof(Data[0]), Size, Out);
fclose(Out);
}
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, long *Epoch,
size_t MaxSize, bool ExitOnError) {
long E = Epoch ? *Epoch : 0;
Vector<std::string> Files;
ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/ true);
size_t NumLoaded = 0;
for (size_t i = 0; i < Files.size(); i++) {
auto &X = Files[i];
if (Epoch && GetEpoch(X) < E) continue;
NumLoaded++;
if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024)
Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path);
auto S = FileToVector(X, MaxSize, ExitOnError);
if (!S.empty()) V->push_back(S);
}
}
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) {
Vector<std::string> Files;
ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/ true);
for (auto &File : Files)
if (size_t Size = FileSize(File)) V->push_back({File, Size});
}
std::string DirPlusFile(const std::string &DirPath,
const std::string &FileName) {
return DirPath + GetSeparator() + FileName;
}
void DupAndCloseStderr() {
int OutputFd = DuplicateFile(2);
if (OutputFd >= 0) {
FILE *NewOutputFile = OpenFile(OutputFd, "w");
if (NewOutputFile) {
OutputFile = NewOutputFile;
if (EF->__sanitizer_set_report_fd)
EF->__sanitizer_set_report_fd(
reinterpret_cast<void *>(GetHandleFromFd(OutputFd)));
DiscardOutput(2);
}
}
}
void CloseStdout() {
DiscardOutput(1);
}
void Printf(const char *Fmt, ...) {
va_list ap;
va_start(ap, Fmt);
vfprintf(OutputFile, Fmt, ap);
va_end(ap);
fflush(OutputFile);
}
void VPrintf(bool Verbose, const char *Fmt, ...) {
return;
if (!Verbose) return;
va_list ap;
va_start(ap, Fmt);
vfprintf(OutputFile, Fmt, ap);
va_end(ap);
fflush(OutputFile);
}
static bool MkDirRecursiveInner(const std::string &Leaf) {
// Prevent chance of potential infinite recursion
if (Leaf == ".") return true;
const std::string &Dir = DirName(Leaf);
if (IsDirectory(Dir)) {
MkDir(Leaf);
return IsDirectory(Leaf);
}
bool ret = MkDirRecursiveInner(Dir);
if (!ret) {
// Give up early if a previous MkDir failed
return ret;
}
MkDir(Leaf);
return IsDirectory(Leaf);
}
bool MkDirRecursive(const std::string &Dir) {
if (Dir.empty()) return false;
if (IsDirectory(Dir)) return true;
return MkDirRecursiveInner(Dir);
}
void RmDirRecursive(const std::string &Dir) {
IterateDirRecursive(
Dir, [](const std::string &Path) {},
[](const std::string &Path) { RmDir(Path); },
[](const std::string &Path) { RemoveFile(Path); });
}
std::string TempPath(const char *Prefix, const char *Extension) {
return DirPlusFile(TmpDir(), std::string("libFuzzerTemp.") + Prefix +
std::to_string(GetPid()) + Extension);
}
} // namespace fuzzer

View File

@ -0,0 +1,112 @@
//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO interface.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_IO_H
#define LLVM_FUZZER_IO_H
#include "FuzzerDefs.h"
namespace fuzzer {
long GetEpoch(const std::string &Path);
Unit FileToVector(const std::string &Path, size_t MaxSize = 0,
bool ExitOnError = true);
std::string FileToString(const std::string &Path);
void CopyFileToErr(const std::string &Path);
void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path);
// Write Data.c_str() to the file without terminating null character.
void WriteToFile(const std::string &Data, const std::string &Path);
void WriteToFile(const Unit &U, const std::string &Path);
void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path);
void AppendToFile(const std::string &Data, const std::string &Path);
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
long *Epoch, size_t MaxSize, bool ExitOnError);
// Returns "Dir/FileName" or equivalent for the current OS.
std::string DirPlusFile(const std::string &DirPath,
const std::string &FileName);
// Returns the name of the dir, similar to the 'dirname' utility.
std::string DirName(const std::string &FileName);
// Returns path to a TmpDir.
std::string TmpDir();
std::string TempPath(const char *Prefix, const char *Extension);
bool IsInterestingCoverageFile(const std::string &FileName);
void DupAndCloseStderr();
void CloseStdout();
void Printf(const char *Fmt, ...);
void VPrintf(bool Verbose, const char *Fmt, ...);
// Print using raw syscalls, useful when printing at early init stages.
void RawPrint(const char *Str);
// Platform specific functions:
bool IsFile(const std::string &Path);
bool IsDirectory(const std::string &Path);
size_t FileSize(const std::string &Path);
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
Vector<std::string> *V, bool TopDir);
bool MkDirRecursive(const std::string &Dir);
void RmDirRecursive(const std::string &Dir);
// Iterate files and dirs inside Dir, recursively.
// Call DirPreCallback/DirPostCallback on dirs before/after
// calling FileCallback on files.
void IterateDirRecursive(const std::string &Dir,
void (*DirPreCallback)(const std::string &Dir),
void (*DirPostCallback)(const std::string &Dir),
void (*FileCallback)(const std::string &Dir));
struct SizedFile {
std::string File;
size_t Size;
bool operator<(const SizedFile &B) const { return Size < B.Size; }
};
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
char GetSeparator();
bool IsSeparator(char C);
// Similar to the basename utility: returns the file name w/o the dir prefix.
std::string Basename(const std::string &Path);
FILE* OpenFile(int Fd, const char *Mode);
int CloseFile(int Fd);
int DuplicateFile(int Fd);
void RemoveFile(const std::string &Path);
void RenameFile(const std::string &OldPath, const std::string &NewPath);
intptr_t GetHandleFromFd(int fd);
void MkDir(const std::string &Path);
void RmDir(const std::string &Path);
const std::string &getDevNull();
} // namespace fuzzer
#endif // LLVM_FUZZER_IO_H

View File

@ -0,0 +1,223 @@
//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO functions implementation using Posix API.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include <cstdarg>
#include <cstdio>
#include <dirent.h>
#include <fstream>
#include <iterator>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
namespace fuzzer {
bool IsFile(const std::string &Path) {
struct stat St;
if (stat(Path.c_str(), &St)) return false;
return S_ISREG(St.st_mode);
}
bool IsDirectory(const std::string &Path) {
struct stat St;
if (stat(Path.c_str(), &St)) return false;
return S_ISDIR(St.st_mode);
}
size_t FileSize(const std::string &Path) {
struct stat St;
if (stat(Path.c_str(), &St)) return 0;
return St.st_size;
}
std::string Basename(const std::string &Path) {
size_t Pos = Path.rfind(GetSeparator());
if (Pos == std::string::npos) return Path;
assert(Pos < Path.size());
return Path.substr(Pos + 1);
}
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
Vector<std::string> *V, bool TopDir) {
auto E = GetEpoch(Dir);
if (Epoch)
if (E && *Epoch >= E) return;
DIR *D = opendir(Dir.c_str());
if (!D) {
Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str());
exit(1);
}
while (auto E = readdir(D)) {
std::string Path = DirPlusFile(Dir, E->d_name);
if (E->d_type == DT_REG || E->d_type == DT_LNK ||
(E->d_type == DT_UNKNOWN && IsFile(Path)))
V->push_back(Path);
else if ((E->d_type == DT_DIR ||
(E->d_type == DT_UNKNOWN && IsDirectory(Path))) &&
*E->d_name != '.')
ListFilesInDirRecursive(Path, Epoch, V, false);
}
closedir(D);
if (Epoch && TopDir) *Epoch = E;
}
void IterateDirRecursive(const std::string &Dir,
void (*DirPreCallback)(const std::string &Dir),
void (*DirPostCallback)(const std::string &Dir),
void (*FileCallback)(const std::string &Dir)) {
DirPreCallback(Dir);
DIR *D = opendir(Dir.c_str());
if (!D) return;
while (auto E = readdir(D)) {
std::string Path = DirPlusFile(Dir, E->d_name);
if (E->d_type == DT_REG || E->d_type == DT_LNK ||
(E->d_type == DT_UNKNOWN && IsFile(Path)))
FileCallback(Path);
else if ((E->d_type == DT_DIR ||
(E->d_type == DT_UNKNOWN && IsDirectory(Path))) &&
*E->d_name != '.')
IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback);
}
closedir(D);
DirPostCallback(Dir);
}
char GetSeparator() {
return '/';
}
bool IsSeparator(char C) {
return C == '/';
}
FILE *OpenFile(int Fd, const char *Mode) {
return fdopen(Fd, Mode);
}
int CloseFile(int fd) {
return close(fd);
}
int DuplicateFile(int Fd) {
return dup(Fd);
}
void RemoveFile(const std::string &Path) {
unlink(Path.c_str());
}
void RenameFile(const std::string &OldPath, const std::string &NewPath) {
rename(OldPath.c_str(), NewPath.c_str());
}
intptr_t GetHandleFromFd(int fd) {
return static_cast<intptr_t>(fd);
}
std::string DirName(const std::string &FileName) {
char *Tmp = new char[FileName.size() + 1];
memcpy(Tmp, FileName.c_str(), FileName.size() + 1);
std::string Res = dirname(Tmp);
delete[] Tmp;
return Res;
}
std::string TmpDir() {
if (auto Env = getenv("TMPDIR")) return Env;
return "/tmp";
}
bool IsInterestingCoverageFile(const std::string &FileName) {
if (FileName.find("compiler-rt/lib/") != std::string::npos)
return false; // sanitizer internal.
if (FileName.find("/usr/lib/") != std::string::npos) return false;
if (FileName.find("/usr/include/") != std::string::npos) return false;
if (FileName == "<null>") return false;
return true;
}
void RawPrint(const char *Str) {
write(2, Str, strlen(Str));
}
void MkDir(const std::string &Path) {
mkdir(Path.c_str(), 0700);
}
void RmDir(const std::string &Path) {
rmdir(Path.c_str());
}
const std::string &getDevNull() {
static const std::string devNull = "/dev/null";
return devNull;
}
} // namespace fuzzer
#endif // LIBFUZZER_POSIX

View File

@ -0,0 +1,513 @@
//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO functions implementation for Windows.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_WINDOWS
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include <cstdarg>
#include <cstdio>
#include <fstream>
#include <io.h>
#include <iterator>
#include <sys/stat.h>
#include <sys/types.h>
#include <windows.h>
namespace fuzzer {
static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
if (FileAttributes & FILE_ATTRIBUTE_NORMAL) return true;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) return false;
HANDLE FileHandle(CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
if (FileHandle == INVALID_HANDLE_VALUE) {
Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
GetLastError());
return false;
}
DWORD FileType = GetFileType(FileHandle);
if (FileType == FILE_TYPE_UNKNOWN) {
Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
GetLastError());
CloseHandle(FileHandle);
return false;
}
if (FileType != FILE_TYPE_DISK) {
CloseHandle(FileHandle);
return false;
}
CloseHandle(FileHandle);
return true;
}
bool IsFile(const std::string &Path) {
DWORD Att = GetFileAttributesA(Path.c_str());
if (Att == INVALID_FILE_ATTRIBUTES) {
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
Path.c_str(), GetLastError());
return false;
}
return IsFile(Path, Att);
}
static bool IsDir(DWORD FileAttrs) {
if (FileAttrs == INVALID_FILE_ATTRIBUTES) return false;
return FileAttrs & FILE_ATTRIBUTE_DIRECTORY;
}
bool IsDirectory(const std::string &Path) {
DWORD Att = GetFileAttributesA(Path.c_str());
if (Att == INVALID_FILE_ATTRIBUTES) {
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
Path.c_str(), GetLastError());
return false;
}
return IsDir(Att);
}
std::string Basename(const std::string &Path) {
size_t Pos = Path.find_last_of("/\\");
if (Pos == std::string::npos) return Path;
assert(Pos < Path.size());
return Path.substr(Pos + 1);
}
size_t FileSize(const std::string &Path) {
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
DWORD LastError = GetLastError();
if (LastError != ERROR_FILE_NOT_FOUND)
Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
Path.c_str(), LastError);
return 0;
}
ULARGE_INTEGER size;
size.HighPart = attr.nFileSizeHigh;
size.LowPart = attr.nFileSizeLow;
return size.QuadPart;
}
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
Vector<std::string> *V, bool TopDir) {
auto E = GetEpoch(Dir);
if (Epoch)
if (E && *Epoch >= E) return;
std::string Path(Dir);
assert(!Path.empty());
if (Path.back() != '\\') Path.push_back('\\');
Path.push_back('*');
// Get the first directory entry.
WIN32_FIND_DATAA FindInfo;
HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
if (FindHandle == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) return;
Printf("No such file or directory: %s; exiting\n", Dir.c_str());
exit(1);
}
do {
std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
size_t FilenameLen = strlen(FindInfo.cFileName);
if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
(FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
FindInfo.cFileName[1] == '.'))
continue;
ListFilesInDirRecursive(FileName, Epoch, V, false);
} else if (IsFile(FileName, FindInfo.dwFileAttributes))
V->push_back(FileName);
} while (FindNextFileA(FindHandle, &FindInfo));
DWORD LastError = GetLastError();
if (LastError != ERROR_NO_MORE_FILES)
Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
FindClose(FindHandle);
if (Epoch && TopDir) *Epoch = E;
}
void IterateDirRecursive(const std::string &Dir,
void (*DirPreCallback)(const std::string &Dir),
void (*DirPostCallback)(const std::string &Dir),
void (*FileCallback)(const std::string &Dir)) {
// TODO(metzman): Implement ListFilesInDirRecursive via this function.
DirPreCallback(Dir);
DWORD DirAttrs = GetFileAttributesA(Dir.c_str());
if (!IsDir(DirAttrs)) return;
std::string TargetDir(Dir);
assert(!TargetDir.empty());
if (TargetDir.back() != '\\') TargetDir.push_back('\\');
TargetDir.push_back('*');
WIN32_FIND_DATAA FindInfo;
// Find the directory's first file.
HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo);
if (FindHandle == INVALID_HANDLE_VALUE) {
DWORD LastError = GetLastError();
if (LastError != ERROR_FILE_NOT_FOUND) {
// If the directory isn't empty, then something abnormal is going on.
Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
LastError);
}
return;
}
do {
std::string Path = DirPlusFile(Dir, FindInfo.cFileName);
DWORD PathAttrs = FindInfo.dwFileAttributes;
if (IsDir(PathAttrs)) {
// Is Path the current directory (".") or the parent ("..")?
if (strcmp(FindInfo.cFileName, ".") == 0 ||
strcmp(FindInfo.cFileName, "..") == 0)
continue;
IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback);
} else if (PathAttrs != INVALID_FILE_ATTRIBUTES) {
FileCallback(Path);
}
} while (FindNextFileA(FindHandle, &FindInfo));
DWORD LastError = GetLastError();
if (LastError != ERROR_NO_MORE_FILES)
Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
LastError);
FindClose(FindHandle);
DirPostCallback(Dir);
}
char GetSeparator() {
return '\\';
}
FILE *OpenFile(int Fd, const char *Mode) {
return _fdopen(Fd, Mode);
}
int CloseFile(int Fd) {
return _close(Fd);
}
int DuplicateFile(int Fd) {
return _dup(Fd);
}
void RemoveFile(const std::string &Path) {
_unlink(Path.c_str());
}
void RenameFile(const std::string &OldPath, const std::string &NewPath) {
rename(OldPath.c_str(), NewPath.c_str());
}
intptr_t GetHandleFromFd(int fd) {
return _get_osfhandle(fd);
}
bool IsSeparator(char C) {
return C == '\\' || C == '/';
}
// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
// Returns number of characters considered if successful.
static size_t ParseDrive(const std::string &FileName, const size_t Offset,
bool Relative = true) {
if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':') return 0;
if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
if (!Relative) // Accept relative path?
return 0;
else
return 2;
}
return 3;
}
// Parse a file name, like: SomeFile.txt
// Returns number of characters considered if successful.
static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
size_t Pos = Offset;
const size_t End = FileName.size();
for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
;
return Pos - Offset;
}
// Parse a directory ending in separator, like: `SomeDir\`
// Returns number of characters considered if successful.
static size_t ParseDir(const std::string &FileName, const size_t Offset) {
size_t Pos = Offset;
const size_t End = FileName.size();
if (Pos >= End || IsSeparator(FileName[Pos])) return 0;
for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
;
if (Pos >= End) return 0;
++Pos; // Include separator.
return Pos - Offset;
}
// Parse a servername and share, like: `SomeServer\SomeShare\`
// Returns number of characters considered if successful.
static size_t ParseServerAndShare(const std::string &FileName,
const size_t Offset) {
size_t Pos = Offset, Res;
if (!(Res = ParseDir(FileName, Pos))) return 0;
Pos += Res;
if (!(Res = ParseDir(FileName, Pos))) return 0;
Pos += Res;
return Pos - Offset;
}
// Parse the given Ref string from the position Offset, to exactly match the
// given string Patt. Returns number of characters considered if successful.
static size_t ParseCustomString(const std::string &Ref, size_t Offset,
const char *Patt) {
size_t Len = strlen(Patt);
if (Offset + Len > Ref.size()) return 0;
return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
}
// Parse a location, like:
// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
// Returns number of characters considered if successful.
static size_t ParseLocation(const std::string &FileName) {
size_t Pos = 0, Res;
if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
Pos += Res;
if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
Pos += Res;
if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res;
return 0;
}
if ((Res = ParseDrive(FileName, Pos, false))) return Pos + Res;
return 0;
}
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
++Pos;
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
++Pos;
if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res;
return 0;
}
return Pos;
}
if ((Res = ParseDrive(FileName, Pos))) return Pos + Res;
return Pos;
}
std::string DirName(const std::string &FileName) {
size_t LocationLen = ParseLocation(FileName);
size_t DirLen = 0, Res;
while ((Res = ParseDir(FileName, LocationLen + DirLen)))
DirLen += Res;
size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
if (LocationLen + DirLen + FileLen != FileName.size()) {
Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
exit(1);
}
if (DirLen) {
--DirLen; // Remove trailing separator.
if (!FileLen) { // Path ended in separator.
assert(DirLen);
// Remove file name from Dir.
while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
--DirLen;
if (DirLen) // Remove trailing separator.
--DirLen;
}
}
if (!LocationLen) { // Relative path.
if (!DirLen) return ".";
return std::string(".\\").append(FileName, 0, DirLen);
}
return FileName.substr(0, LocationLen + DirLen);
}
std::string TmpDir() {
std::string Tmp;
Tmp.resize(MAX_PATH + 1);
DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]);
if (Size == 0) {
Printf("Couldn't get Tmp path.\n");
exit(1);
}
Tmp.resize(Size);
return Tmp;
}
bool IsInterestingCoverageFile(const std::string &FileName) {
if (FileName.find("Program Files") != std::string::npos) return false;
if (FileName.find("compiler-rt\\lib\\") != std::string::npos)
return false; // sanitizer internal.
if (FileName == "<null>") return false;
return true;
}
void RawPrint(const char *Str) {
_write(2, Str, strlen(Str));
}
void MkDir(const std::string &Path) {
if (CreateDirectoryA(Path.c_str(), nullptr)) return;
Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
GetLastError());
}
void RmDir(const std::string &Path) {
if (RemoveDirectoryA(Path.c_str())) return;
Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
GetLastError());
}
const std::string &getDevNull() {
static const std::string devNull = "NUL";
return devNull;
}
} // namespace fuzzer
#endif // LIBFUZZER_WINDOWS

View File

@ -0,0 +1,290 @@
//===-- FuzzerInterceptors.cpp --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Intercept certain libc functions to aid fuzzing.
// Linked only when other RTs that define their own interceptors are not linked.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_LINUX
#define GET_CALLER_PC() __builtin_return_address(0)
#define PTR_TO_REAL(x) real_##x
#define REAL(x) __interception::PTR_TO_REAL(x)
#define FUNC_TYPE(x) x##_type
#define DEFINE_REAL(ret_type, func, ...) \
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
namespace __interception { \
\
FUNC_TYPE(func) PTR_TO_REAL(func); \
\
}
#include <cassert>
#include <cstdint>
#include <dlfcn.h> // for dlsym()
static void *getFuncAddr(const char *name, uintptr_t wrapper_addr) {
void *addr = dlsym(RTLD_NEXT, name);
if (!addr) {
// If the lookup using RTLD_NEXT failed, the sanitizer runtime library is
// later in the library search order than the DSO that we are trying to
// intercept, which means that we cannot intercept this function. We still
// want the address of the real definition, though, so look it up using
// RTLD_DEFAULT.
addr = dlsym(RTLD_DEFAULT, name);
// In case `name' is not loaded, dlsym ends up finding the actual wrapper.
// We don't want to intercept the wrapper and have it point to itself.
if (reinterpret_cast<uintptr_t>(addr) == wrapper_addr) addr = nullptr;
}
return addr;
}
static int FuzzerInited = 0;
static bool FuzzerInitIsRunning;
static void fuzzerInit();
static void ensureFuzzerInited() {
assert(!FuzzerInitIsRunning);
if (!FuzzerInited) { fuzzerInit(); }
}
static int internal_strcmp_strncmp(const char *s1, const char *s2, bool strncmp,
size_t n) {
size_t i = 0;
while (true) {
if (strncmp) {
if (i == n) break;
i++;
}
unsigned c1 = *s1;
unsigned c2 = *s2;
if (c1 != c2) return (c1 < c2) ? -1 : 1;
if (c1 == 0) break;
s1++;
s2++;
}
return 0;
}
static int internal_strncmp(const char *s1, const char *s2, size_t n) {
return internal_strcmp_strncmp(s1, s2, true, n);
}
static int internal_strcmp(const char *s1, const char *s2) {
return internal_strcmp_strncmp(s1, s2, false, 0);
}
static int internal_memcmp(const void *s1, const void *s2, size_t n) {
const uint8_t *t1 = static_cast<const uint8_t *>(s1);
const uint8_t *t2 = static_cast<const uint8_t *>(s2);
for (size_t i = 0; i < n; ++i, ++t1, ++t2)
if (*t1 != *t2) return *t1 < *t2 ? -1 : 1;
return 0;
}
static size_t internal_strlen(const char *s) {
size_t i = 0;
while (s[i])
i++;
return i;
}
static char *internal_strstr(const char *haystack, const char *needle) {
// This is O(N^2), but we are not using it in hot places.
size_t len1 = internal_strlen(haystack);
size_t len2 = internal_strlen(needle);
if (len1 < len2) return nullptr;
for (size_t pos = 0; pos <= len1 - len2; pos++) {
if (internal_memcmp(haystack + pos, needle, len2) == 0)
return const_cast<char *>(haystack) + pos;
}
return nullptr;
}
extern "C" {
// Weak hooks forward-declared to avoid dependency on
// <sanitizer/common_interface_defs.h>.
void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1,
const void *s2, size_t n, int result);
void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1,
const char *s2, size_t n, int result);
void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
const char *s2, size_t n, int result);
void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1,
const char *s2, int result);
void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
const char *s2, int result);
void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
const char *s2, char *result);
void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
const char *s2, char *result);
void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
const void *s2, size_t len2, void *result);
DEFINE_REAL(int, bcmp, const void *, const void *, size_t)
DEFINE_REAL(int, memcmp, const void *, const void *, size_t)
DEFINE_REAL(int, strncmp, const char *, const char *, size_t)
DEFINE_REAL(int, strcmp, const char *, const char *)
DEFINE_REAL(int, strncasecmp, const char *, const char *, size_t)
DEFINE_REAL(int, strcasecmp, const char *, const char *)
DEFINE_REAL(char *, strstr, const char *, const char *)
DEFINE_REAL(char *, strcasestr, const char *, const char *)
DEFINE_REAL(void *, memmem, const void *, size_t, const void *, size_t)
ATTRIBUTE_INTERFACE int bcmp(const char *s1, const char *s2, size_t n) {
if (!FuzzerInited) return internal_memcmp(s1, s2, n);
int result = REAL(bcmp)(s1, s2, n);
__sanitizer_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, result);
return result;
}
ATTRIBUTE_INTERFACE int memcmp(const void *s1, const void *s2, size_t n) {
if (!FuzzerInited) return internal_memcmp(s1, s2, n);
int result = REAL(memcmp)(s1, s2, n);
__sanitizer_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, result);
return result;
}
ATTRIBUTE_INTERFACE int strncmp(const char *s1, const char *s2, size_t n) {
if (!FuzzerInited) return internal_strncmp(s1, s2, n);
int result = REAL(strncmp)(s1, s2, n);
__sanitizer_weak_hook_strncmp(GET_CALLER_PC(), s1, s2, n, result);
return result;
}
ATTRIBUTE_INTERFACE int strcmp(const char *s1, const char *s2) {
if (!FuzzerInited) return internal_strcmp(s1, s2);
int result = REAL(strcmp)(s1, s2);
__sanitizer_weak_hook_strcmp(GET_CALLER_PC(), s1, s2, result);
return result;
}
ATTRIBUTE_INTERFACE int strncasecmp(const char *s1, const char *s2, size_t n) {
ensureFuzzerInited();
int result = REAL(strncasecmp)(s1, s2, n);
__sanitizer_weak_hook_strncasecmp(GET_CALLER_PC(), s1, s2, n, result);
return result;
}
ATTRIBUTE_INTERFACE int strcasecmp(const char *s1, const char *s2) {
ensureFuzzerInited();
int result = REAL(strcasecmp)(s1, s2);
__sanitizer_weak_hook_strcasecmp(GET_CALLER_PC(), s1, s2, result);
return result;
}
ATTRIBUTE_INTERFACE char *strstr(const char *s1, const char *s2) {
if (!FuzzerInited) return internal_strstr(s1, s2);
char *result = REAL(strstr)(s1, s2);
__sanitizer_weak_hook_strstr(GET_CALLER_PC(), s1, s2, result);
return result;
}
ATTRIBUTE_INTERFACE char *strcasestr(const char *s1, const char *s2) {
ensureFuzzerInited();
char *result = REAL(strcasestr)(s1, s2);
__sanitizer_weak_hook_strcasestr(GET_CALLER_PC(), s1, s2, result);
return result;
}
ATTRIBUTE_INTERFACE
void *memmem(const void *s1, size_t len1, const void *s2, size_t len2) {
ensureFuzzerInited();
void *result = REAL(memmem)(s1, len1, s2, len2);
__sanitizer_weak_hook_memmem(GET_CALLER_PC(), s1, len1, s2, len2, result);
return result;
}
__attribute__((section(".preinit_array"),
used)) static void (*__local_fuzzer_preinit)(void) = fuzzerInit;
} // extern "C"
static void fuzzerInit() {
assert(!FuzzerInitIsRunning);
if (FuzzerInited) return;
FuzzerInitIsRunning = true;
REAL(bcmp) = reinterpret_cast<memcmp_type>(
getFuncAddr("bcmp", reinterpret_cast<uintptr_t>(&bcmp)));
REAL(memcmp) = reinterpret_cast<memcmp_type>(
getFuncAddr("memcmp", reinterpret_cast<uintptr_t>(&memcmp)));
REAL(strncmp) = reinterpret_cast<strncmp_type>(
getFuncAddr("strncmp", reinterpret_cast<uintptr_t>(&strncmp)));
REAL(strcmp) = reinterpret_cast<strcmp_type>(
getFuncAddr("strcmp", reinterpret_cast<uintptr_t>(&strcmp)));
REAL(strncasecmp) = reinterpret_cast<strncasecmp_type>(
getFuncAddr("strncasecmp", reinterpret_cast<uintptr_t>(&strncasecmp)));
REAL(strcasecmp) = reinterpret_cast<strcasecmp_type>(
getFuncAddr("strcasecmp", reinterpret_cast<uintptr_t>(&strcasecmp)));
REAL(strstr) = reinterpret_cast<strstr_type>(
getFuncAddr("strstr", reinterpret_cast<uintptr_t>(&strstr)));
REAL(strcasestr) = reinterpret_cast<strcasestr_type>(
getFuncAddr("strcasestr", reinterpret_cast<uintptr_t>(&strcasestr)));
REAL(memmem) = reinterpret_cast<memmem_type>(
getFuncAddr("memmem", reinterpret_cast<uintptr_t>(&memmem)));
FuzzerInitIsRunning = false;
FuzzerInited = 1;
}
#endif

View File

@ -0,0 +1,79 @@
//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Define the interface between libFuzzer and the library being tested.
//===----------------------------------------------------------------------===//
// NOTE: the libFuzzer interface is thin and in the majority of cases
// you should not include this file into your target. In 95% of cases
// all you need is to define the following function in your file:
// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
// WARNING: keep the interface in C.
#ifndef LLVM_FUZZER_INTERFACE_H
#define LLVM_FUZZER_INTERFACE_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Define FUZZER_INTERFACE_VISIBILITY to set default visibility in a way that
// doesn't break MSVC.
#if defined(_WIN32)
#define FUZZER_INTERFACE_VISIBILITY __declspec(dllexport)
#else
#define FUZZER_INTERFACE_VISIBILITY __attribute__((visibility("default")))
#endif
// Mandatory user-provided target function.
// Executes the code under test with [Data, Data+Size) as the input.
// libFuzzer will invoke this function *many* times with different inputs.
// Must return 0.
FUZZER_INTERFACE_VISIBILITY int
LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
// Optional user-provided initialization function.
// If provided, this function will be called by libFuzzer once at startup.
// It may read and modify argc/argv.
// Must return 0.
FUZZER_INTERFACE_VISIBILITY int LLVMFuzzerInitialize(int *argc, char ***argv);
// Optional user-provided custom mutator.
// Mutates raw data in [Data, Data+Size) inplace.
// Returns the new size, which is not greater than MaxSize.
// Given the same Seed produces the same mutation.
FUZZER_INTERFACE_VISIBILITY size_t
LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
unsigned int Seed);
// Optional user-provided custom cross-over function.
// Combines pieces of Data1 & Data2 together into Out.
// Returns the new size, which is not greater than MaxOutSize.
// Should produce the same mutation given the same Seed.
FUZZER_INTERFACE_VISIBILITY size_t
LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2, uint8_t *Out,
size_t MaxOutSize, unsigned int Seed);
// Experimental, may go away in future.
// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator.
// Mutates raw data in [Data, Data+Size) inplace.
// Returns the new size, which is not greater than MaxSize.
FUZZER_INTERFACE_VISIBILITY size_t
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
#undef FUZZER_INTERFACE_VISIBILITY
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // LLVM_FUZZER_INTERFACE_H

View File

@ -0,0 +1,173 @@
//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Define the main class fuzzer::Fuzzer and most functions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_INTERNAL_H
#define LLVM_FUZZER_INTERNAL_H
#include "FuzzerDataFlowTrace.h"
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerInterface.h"
#include "FuzzerOptions.h"
#include "FuzzerSHA1.h"
#include "FuzzerValueBitMap.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <climits>
#include <cstdlib>
#include <string.h>
namespace fuzzer {
using namespace std::chrono;
class Fuzzer {
public:
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
FuzzingOptions Options);
~Fuzzer();
void Loop(Vector<SizedFile> &CorporaFiles);
void ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles);
void MinimizeCrashLoop(const Unit &U);
void RereadOutputCorpus(size_t MaxSize);
size_t secondsSinceProcessStartUp() {
return duration_cast<seconds>(system_clock::now() - ProcessStartTime)
.count();
}
bool TimedOut() {
return Options.MaxTotalTimeSec > 0 &&
secondsSinceProcessStartUp() >
static_cast<size_t>(Options.MaxTotalTimeSec);
}
size_t execPerSec() {
size_t Seconds = secondsSinceProcessStartUp();
return Seconds ? TotalNumberOfRuns / Seconds : 0;
}
size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; }
static void StaticAlarmCallback();
static void StaticCrashSignalCallback();
static void StaticExitCallback();
static void StaticInterruptCallback();
static void StaticFileSizeExceedCallback();
static void StaticGracefulExitCallback();
void ExecuteCallback(const uint8_t *Data, size_t Size);
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
InputInfo *II = nullptr, bool ForceAddToCorpus = false,
bool *FoundUniqFeatures = nullptr);
// Merge Corpora[1:] into Corpora[0].
void Merge(const Vector<std::string> &Corpora);
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
MutationDispatcher &GetMD() { return MD; }
void PrintFinalStats();
void SetMaxInputLen(size_t MaxInputLen);
void SetMaxMutationLen(size_t MaxMutationLen);
void RssLimitCallback();
bool InFuzzingThread() const { return IsMyThread; }
size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const;
void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
bool DuringInitialCorpusExecution);
void HandleMalloc(size_t Size);
static void MaybeExitGracefully();
std::string WriteToOutputCorpus(const Unit &U);
private:
void AlarmCallback();
void CrashCallback();
void ExitCallback();
void CrashOnOverwrittenData();
void InterruptCallback();
void MutateAndTestOne();
void PurgeAllocator();
void ReportNewCoverage(InputInfo *II, const Unit &U);
void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size);
void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0,
size_t Features = 0);
void PrintStatusForNewUnit(const Unit &U, const char *Text);
void CheckExitOnSrcPosOrItem();
static void StaticDeathCallback();
void DumpCurrentUnit(const char *Prefix);
void DeathCallback();
void AllocateCurrentUnitData();
uint8_t *CurrentUnitData = nullptr;
std::atomic<size_t> CurrentUnitSize;
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
bool GracefulExitRequested = false;
size_t TotalNumberOfRuns = 0;
size_t NumberOfNewUnitsAdded = 0;
size_t LastCorpusUpdateRun = 0;
bool HasMoreMallocsThanFrees = false;
size_t NumberOfLeakDetectionAttempts = 0;
system_clock::time_point LastAllocatorPurgeAttemptTime = system_clock::now();
UserCallback CB;
InputCorpus &Corpus;
MutationDispatcher &MD;
FuzzingOptions Options;
DataFlowTrace DFT;
system_clock::time_point ProcessStartTime = system_clock::now();
system_clock::time_point UnitStartTime, UnitStopTime;
long TimeOfLongestUnitInSeconds = 0;
long EpochOfLastReadOfOutputCorpus = 0;
size_t MaxInputLen = 0;
size_t MaxMutationLen = 0;
size_t TmpMaxMutationLen = 0;
Vector<uint32_t> UniqFeatureSetTmp;
// Need to know our own thread.
static thread_local bool IsMyThread;
};
struct ScopedEnableMsanInterceptorChecks {
ScopedEnableMsanInterceptorChecks() {
if (EF->__msan_scoped_enable_interceptor_checks)
EF->__msan_scoped_enable_interceptor_checks();
}
~ScopedEnableMsanInterceptorChecks() {
if (EF->__msan_scoped_disable_interceptor_checks)
EF->__msan_scoped_disable_interceptor_checks();
}
};
struct ScopedDisableMsanInterceptorChecks {
ScopedDisableMsanInterceptorChecks() {
if (EF->__msan_scoped_disable_interceptor_checks)
EF->__msan_scoped_disable_interceptor_checks();
}
~ScopedDisableMsanInterceptorChecks() {
if (EF->__msan_scoped_enable_interceptor_checks)
EF->__msan_scoped_enable_interceptor_checks();
}
};
} // namespace fuzzer
#endif // LLVM_FUZZER_INTERNAL_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
//===- FuzzerMain.cpp - main() function and flags -------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// main() and flags.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#include "FuzzerPlatform.h"
extern "C" {
// This function should be defined by the user.
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
} // extern "C"
ATTRIBUTE_INTERFACE int main(int argc, char **argv) {
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
}

View File

@ -0,0 +1,485 @@
//===- FuzzerMerge.cpp - merging corpora ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Merging corpora.
//===----------------------------------------------------------------------===//
#include "FuzzerCommand.h"
#include "FuzzerMerge.h"
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include "FuzzerTracePC.h"
#include "FuzzerUtil.h"
#include <fstream>
#include <iterator>
#include <set>
#include <sstream>
#include <unordered_set>
namespace fuzzer {
bool Merger::Parse(const std::string &Str, bool ParseCoverage) {
std::istringstream SS(Str);
return Parse(SS, ParseCoverage);
}
void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
if (!Parse(IS, ParseCoverage)) {
Printf("MERGE: failed to parse the control file (unexpected error)\n");
exit(1);
}
}
// The control file example:
//
// 3 # The number of inputs
// 1 # The number of inputs in the first corpus, <= the previous number
// file0
// file1
// file2 # One file name per line.
// STARTED 0 123 # FileID, file size
// FT 0 1 4 6 8 # FileID COV1 COV2 ...
// COV 0 7 8 9 # FileID COV1 COV1
// STARTED 1 456 # If FT is missing, the input crashed while processing.
// STARTED 2 567
// FT 2 8 9
// COV 2 11 12
bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
LastFailure.clear();
std::string Line;
// Parse NumFiles.
if (!std::getline(IS, Line, '\n')) return false;
std::istringstream L1(Line);
size_t NumFiles = 0;
L1 >> NumFiles;
if (NumFiles == 0 || NumFiles > 10000000) return false;
// Parse NumFilesInFirstCorpus.
if (!std::getline(IS, Line, '\n')) return false;
std::istringstream L2(Line);
NumFilesInFirstCorpus = NumFiles + 1;
L2 >> NumFilesInFirstCorpus;
if (NumFilesInFirstCorpus > NumFiles) return false;
// Parse file names.
Files.resize(NumFiles);
for (size_t i = 0; i < NumFiles; i++)
if (!std::getline(IS, Files[i].Name, '\n')) return false;
// Parse STARTED, FT, and COV lines.
size_t ExpectedStartMarker = 0;
const size_t kInvalidStartMarker = -1;
size_t LastSeenStartMarker = kInvalidStartMarker;
Vector<uint32_t> TmpFeatures;
Set<uint32_t> PCs;
while (std::getline(IS, Line, '\n')) {
std::istringstream ISS1(Line);
std::string Marker;
size_t N;
ISS1 >> Marker;
ISS1 >> N;
if (Marker == "STARTED") {
// STARTED FILE_ID FILE_SIZE
if (ExpectedStartMarker != N) return false;
ISS1 >> Files[ExpectedStartMarker].Size;
LastSeenStartMarker = ExpectedStartMarker;
assert(ExpectedStartMarker < Files.size());
ExpectedStartMarker++;
} else if (Marker == "FT") {
// FT FILE_ID COV1 COV2 COV3 ...
size_t CurrentFileIdx = N;
if (CurrentFileIdx != LastSeenStartMarker) return false;
LastSeenStartMarker = kInvalidStartMarker;
if (ParseCoverage) {
TmpFeatures.clear(); // use a vector from outer scope to avoid resizes.
while (ISS1 >> N)
TmpFeatures.push_back(N);
std::sort(TmpFeatures.begin(), TmpFeatures.end());
Files[CurrentFileIdx].Features = TmpFeatures;
}
} else if (Marker == "COV") {
size_t CurrentFileIdx = N;
if (ParseCoverage)
while (ISS1 >> N)
if (PCs.insert(N).second) Files[CurrentFileIdx].Cov.push_back(N);
} else {
return false;
}
}
if (LastSeenStartMarker != kInvalidStartMarker)
LastFailure = Files[LastSeenStartMarker].Name;
FirstNotProcessedFile = ExpectedStartMarker;
return true;
}
size_t Merger::ApproximateMemoryConsumption() const {
size_t Res = 0;
for (const auto &F : Files)
Res += sizeof(F) + F.Features.size() * sizeof(F.Features[0]);
return Res;
}
// Decides which files need to be merged (add those to NewFiles).
// Returns the number of new features added.
size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
Set<uint32_t> * NewFeatures,
const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
Vector<std::string> *NewFiles) {
NewFiles->clear();
assert(NumFilesInFirstCorpus <= Files.size());
Set<uint32_t> AllFeatures = InitialFeatures;
// What features are in the initial corpus?
for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
auto &Cur = Files[i].Features;
AllFeatures.insert(Cur.begin(), Cur.end());
}
// Remove all features that we already know from all other inputs.
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
auto & Cur = Files[i].Features;
Vector<uint32_t> Tmp;
std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
Cur.swap(Tmp);
}
// Sort. Give preference to
// * smaller files
// * files with more features.
std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(),
[&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool {
if (a.Size != b.Size) return a.Size < b.Size;
return a.Features.size() > b.Features.size();
});
// One greedy pass: add the file's features to AllFeatures.
// If new features were added, add this file to NewFiles.
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
auto &Cur = Files[i].Features;
// Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(),
// Files[i].Size, Cur.size());
bool FoundNewFeatures = false;
for (auto Fe : Cur) {
if (AllFeatures.insert(Fe).second) {
FoundNewFeatures = true;
NewFeatures->insert(Fe);
}
}
if (FoundNewFeatures) NewFiles->push_back(Files[i].Name);
for (auto Cov : Files[i].Cov)
if (InitialCov.find(Cov) == InitialCov.end()) NewCov->insert(Cov);
}
return NewFeatures->size();
}
Set<uint32_t> Merger::AllFeatures() const {
Set<uint32_t> S;
for (auto &File : Files)
S.insert(File.Features.begin(), File.Features.end());
return S;
}
// Inner process. May crash if the target crashes.
void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str());
Merger M;
std::ifstream IF(CFPath);
M.ParseOrExit(IF, false);
IF.close();
if (!M.LastFailure.empty())
Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n",
M.LastFailure.c_str());
Printf(
"MERGE-INNER: %zd total files;"
" %zd processed earlier; will process %zd files now\n",
M.Files.size(), M.FirstNotProcessedFile,
M.Files.size() - M.FirstNotProcessedFile);
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
Set<size_t> AllFeatures;
auto PrintStatsWrapper = [this, &AllFeatures](const char *Where) {
this->PrintStats(Where, "\n", 0, AllFeatures.size());
};
Set<const TracePC::PCTableEntry *> AllPCs;
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
Fuzzer::MaybeExitGracefully();
auto U = FileToVector(M.Files[i].Name);
if (U.size() > MaxInputLen) {
U.resize(MaxInputLen);
U.shrink_to_fit();
}
// Write the pre-run marker.
OF << "STARTED " << i << " " << U.size() << "\n";
OF.flush(); // Flush is important since Command::Execute may crash.
// Run.
TPC.ResetMaps();
ExecuteCallback(U.data(), U.size());
// Collect coverage. We are iterating over the files in this order:
// * First, files in the initial corpus ordered by size, smallest first.
// * Then, all other files, smallest first.
// So it makes no sense to record all features for all files, instead we
// only record features that were not seen before.
Set<size_t> UniqFeatures;
TPC.CollectFeatures([&](size_t Feature) {
if (AllFeatures.insert(Feature).second) UniqFeatures.insert(Feature);
});
TPC.UpdateObservedPCs();
// Show stats.
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
PrintStatsWrapper("pulse ");
if (TotalNumberOfRuns == M.NumFilesInFirstCorpus)
PrintStatsWrapper("LOADED");
// Write the post-run marker and the coverage.
OF << "FT " << i;
for (size_t F : UniqFeatures)
OF << " " << F;
OF << "\n";
OF << "COV " << i;
TPC.ForEachObservedPC([&](const TracePC::PCTableEntry *TE) {
if (AllPCs.insert(TE).second) OF << " " << TPC.PCTableEntryIdx(TE);
});
OF << "\n";
OF.flush();
}
PrintStatsWrapper("DONE ");
}
static size_t WriteNewControlFile(const std::string & CFPath,
const Vector<SizedFile> & OldCorpus,
const Vector<SizedFile> & NewCorpus,
const Vector<MergeFileInfo> &KnownFiles) {
std::unordered_set<std::string> FilesToSkip;
for (auto &SF : KnownFiles)
FilesToSkip.insert(SF.Name);
Vector<std::string> FilesToUse;
auto MaybeUseFile = [=, &FilesToUse](std::string Name) {
if (FilesToSkip.find(Name) == FilesToSkip.end()) FilesToUse.push_back(Name);
};
for (auto &SF : OldCorpus)
MaybeUseFile(SF.File);
auto FilesToUseFromOldCorpus = FilesToUse.size();
for (auto &SF : NewCorpus)
MaybeUseFile(SF.File);
RemoveFile(CFPath);
std::ofstream ControlFile(CFPath);
ControlFile << FilesToUse.size() << "\n";
ControlFile << FilesToUseFromOldCorpus << "\n";
for (auto &FN : FilesToUse)
ControlFile << FN << "\n";
if (!ControlFile) {
Printf("MERGE-OUTER: failed to write to the control file: %s\n",
CFPath.c_str());
exit(1);
}
return FilesToUse.size();
}
// Outer process. Does not call the target code and thus should not fail.
void CrashResistantMerge(const Vector<std::string> &Args,
const Vector<SizedFile> & OldCorpus,
const Vector<SizedFile> & NewCorpus,
Vector<std::string> * NewFiles,
const Set<uint32_t> & InitialFeatures,
Set<uint32_t> * NewFeatures,
const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
const std::string &CFPath, bool V /*Verbose*/) {
if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge.
size_t NumAttempts = 0;
Vector<MergeFileInfo> KnownFiles;
if (FileSize(CFPath)) {
VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n",
CFPath.c_str());
Merger M;
std::ifstream IF(CFPath);
if (M.Parse(IF, /*ParseCoverage=*/true)) {
VPrintf(V,
"MERGE-OUTER: control file ok, %zd files total,"
" first not processed file %zd\n",
M.Files.size(), M.FirstNotProcessedFile);
if (!M.LastFailure.empty())
VPrintf(V,
"MERGE-OUTER: '%s' will be skipped as unlucky "
"(merge has stumbled on it the last time)\n",
M.LastFailure.c_str());
if (M.FirstNotProcessedFile >= M.Files.size()) {
// Merge has already been completed with the given merge control file.
if (M.Files.size() == OldCorpus.size() + NewCorpus.size()) {
VPrintf(
V,
"MERGE-OUTER: nothing to do, merge has been completed before\n");
exit(0);
}
// Number of input files likely changed, start merge from scratch, but
// reuse coverage information from the given merge control file.
VPrintf(
V,
"MERGE-OUTER: starting merge from scratch, but reusing coverage "
"information from the given control file\n");
KnownFiles = M.Files;
} else {
// There is a merge in progress, continue.
NumAttempts = M.Files.size() - M.FirstNotProcessedFile;
}
} else {
VPrintf(V, "MERGE-OUTER: bad control file, will overwrite it\n");
}
}
if (!NumAttempts) {
// The supplied control file is empty or bad, create a fresh one.
VPrintf(V,
"MERGE-OUTER: "
"%zd files, %zd in the initial corpus, %zd processed earlier\n",
OldCorpus.size() + NewCorpus.size(), OldCorpus.size(),
KnownFiles.size());
NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles);
}
// Execute the inner process until it passes.
// Every inner process should execute at least one input.
Command BaseCmd(Args);
BaseCmd.removeFlag("merge");
BaseCmd.removeFlag("fork");
BaseCmd.removeFlag("collect_data_flow");
for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) {
Fuzzer::MaybeExitGracefully();
VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt);
Command Cmd(BaseCmd);
Cmd.addFlag("merge_control_file", CFPath);
Cmd.addFlag("merge_inner", "1");
if (!V) {
Cmd.setOutputFile(getDevNull());
Cmd.combineOutAndErr();
}
auto ExitCode = ExecuteCommand(Cmd);
if (!ExitCode) {
VPrintf(V, "MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt);
break;
}
}
// Read the control file and do the merge.
Merger M;
std::ifstream IF(CFPath);
IF.seekg(0, IF.end);
VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n",
(size_t)IF.tellg());
IF.seekg(0, IF.beg);
M.ParseOrExit(IF, true);
IF.close();
VPrintf(V,
"MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n",
M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb());
M.Files.insert(M.Files.end(), KnownFiles.begin(), KnownFiles.end());
M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles);
VPrintf(V,
"MERGE-OUTER: %zd new files with %zd new features added; "
"%zd new coverage edges\n",
NewFiles->size(), NewFeatures->size(), NewCov->size());
}
} // namespace fuzzer

View File

@ -0,0 +1,87 @@
//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Merging Corpora.
//
// The task:
// Take the existing corpus (possibly empty) and merge new inputs into
// it so that only inputs with new coverage ('features') are added.
// The process should tolerate the crashes, OOMs, leaks, etc.
//
// Algorithm:
// The outer process collects the set of files and writes their names
// into a temporary "control" file, then repeatedly launches the inner
// process until all inputs are processed.
// The outer process does not actually execute the target code.
//
// The inner process reads the control file and sees a) list of all the inputs
// and b) the last processed input. Then it starts processing the inputs one
// by one. Before processing every input it writes one line to control file:
// STARTED INPUT_ID INPUT_SIZE
// After processing an input it writes the following lines:
// FT INPUT_ID Feature1 Feature2 Feature3 ...
// COV INPUT_ID Coverage1 Coverage2 Coverage3 ...
// If a crash happens while processing an input the last line in the control
// file will be "STARTED INPUT_ID" and so the next process will know
// where to resume.
//
// Once all inputs are processed by the inner process(es) the outer process
// reads the control files and does the merge based entirely on the contents
// of control file.
// It uses a single pass greedy algorithm choosing first the smallest inputs
// within the same size the inputs that have more new features.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_MERGE_H
#define LLVM_FUZZER_MERGE_H
#include "FuzzerDefs.h"
#include <istream>
#include <ostream>
#include <set>
#include <vector>
namespace fuzzer {
struct MergeFileInfo {
std::string Name;
size_t Size = 0;
Vector<uint32_t> Features, Cov;
};
struct Merger {
Vector<MergeFileInfo> Files;
size_t NumFilesInFirstCorpus = 0;
size_t FirstNotProcessedFile = 0;
std::string LastFailure;
bool Parse(std::istream &IS, bool ParseCoverage);
bool Parse(const std::string &Str, bool ParseCoverage);
void ParseOrExit(std::istream &IS, bool ParseCoverage);
size_t Merge(const Set<uint32_t> &InitialFeatures, Set<uint32_t> *NewFeatures,
const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
Vector<std::string> *NewFiles);
size_t ApproximateMemoryConsumption() const;
Set<uint32_t> AllFeatures() const;
};
void CrashResistantMerge(const Vector<std::string> &Args,
const Vector<SizedFile> &OldCorpus,
const Vector<SizedFile> &NewCorpus,
Vector<std::string> *NewFiles,
const Set<uint32_t> &InitialFeatures,
Set<uint32_t> *NewFeatures,
const Set<uint32_t> &InitialCov,
Set<uint32_t> *NewCov,
const std::string &CFPath,
bool Verbose);
} // namespace fuzzer
#endif // LLVM_FUZZER_MERGE_H

View File

@ -0,0 +1,747 @@
//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Mutate a test input.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerOptions.h"
#include "FuzzerTracePC.h"
#include <random>
#include <chrono>
namespace fuzzer {
const size_t Dictionary::kMaxDictSize;
static const size_t kMaxMutationsToPrint = 10;
static void PrintASCII(const Word &W, const char *PrintAfter) {
PrintASCII(W.data(), W.size(), PrintAfter);
}
MutationDispatcher::MutationDispatcher(Random & Rand,
const FuzzingOptions &Options)
: Rand(Rand), Options(Options) {
DefaultMutators.insert(
DefaultMutators.begin(),
{
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
"InsertRepeatedBytes"},
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
"ManualDict"},
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
"PersAutoDict"},
});
if (Options.UseCmp)
DefaultMutators.push_back(
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
if (EF->LLVMFuzzerCustomMutator)
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
else
Mutators = DefaultMutators;
if (EF->LLVMFuzzerCustomCrossOver)
Mutators.push_back(
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
}
static char RandCh(Random &Rand) {
if (Rand.RandBool()) return Rand(256);
const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
return Special[Rand(sizeof(Special) - 1)];
}
size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
size_t MaxSize) {
return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand());
}
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size == 0) return 0;
if (!CrossOverWith) return 0;
const Unit &Other = *CrossOverWith;
if (Other.empty()) return 0;
CustomCrossOverInPlaceHere.resize(MaxSize);
auto & U = CustomCrossOverInPlaceHere;
size_t NewSize = EF->LLVMFuzzerCustomCrossOver(
Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand());
if (!NewSize) return 0;
assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
memcpy(Data, U.data(), NewSize);
return NewSize;
}
size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize || Size == 0) return 0;
size_t ShuffleAmount =
Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
size_t ShuffleStart = Rand(Size - ShuffleAmount);
assert(ShuffleStart + ShuffleAmount <= Size);
unsigned num = std::chrono::system_clock::now().time_since_epoch().count();
std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, std::default_random_engine(num));
//std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand);
return Size;
}
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size <= 1) return 0;
size_t N = Rand(Size / 2) + 1;
assert(N < Size);
size_t Idx = Rand(Size - N + 1);
// Erase Data[Idx:Idx+N].
memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
// Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
return Size - N;
}
size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size >= MaxSize) return 0;
size_t Idx = Rand(Size + 1);
// Insert new value at Data[Idx].
memmove(Data + Idx + 1, Data + Idx, Size - Idx);
Data[Idx] = RandCh(Rand);
return Size + 1;
}
size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
size_t Size,
size_t MaxSize) {
const size_t kMinBytesToInsert = 3;
if (Size + kMinBytesToInsert >= MaxSize) return 0;
size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
assert(Size + N <= MaxSize && N);
size_t Idx = Rand(Size + 1);
// Insert new values at Data[Idx].
memmove(Data + Idx + N, Data + Idx, Size - Idx);
// Give preference to 0x00 and 0xff.
uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255);
for (size_t i = 0; i < N; i++)
Data[Idx + i] = Byte;
return Size + N;
}
size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
size_t Idx = Rand(Size);
Data[Idx] = RandCh(Rand);
return Size;
}
size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
size_t Idx = Rand(Size);
Data[Idx] ^= 1 << Rand(8);
return Size;
}
size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
size_t Size,
size_t MaxSize) {
return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
}
size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
size_t MaxSize,
DictionaryEntry &DE) {
const Word &W = DE.GetW();
bool UsePositionHint = DE.HasPositionHint() &&
DE.GetPositionHint() + W.size() < Size &&
Rand.RandBool();
if (Rand.RandBool()) { // Insert W.
if (Size + W.size() > MaxSize) return 0;
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
memcpy(Data + Idx, W.data(), W.size());
Size += W.size();
} else { // Overwrite some bytes with W.
if (W.size() > Size) return 0;
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size());
memcpy(Data + Idx, W.data(), W.size());
}
return Size;
}
// Somewhere in the past we have observed a comparison instructions
// with arguments Arg1 Arg2. This function tries to guess a dictionary
// entry that will satisfy that comparison.
// It first tries to find one of the arguments (possibly swapped) in the
// input and if it succeeds it creates a DE with a position hint.
// Otherwise it creates a DE with one of the arguments w/o a position hint.
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
const void *Arg1, const void *Arg2, const void *Arg1Mutation,
const void *Arg2Mutation, size_t ArgSize, const uint8_t *Data,
size_t Size) {
bool HandleFirst = Rand.RandBool();
const void * ExistingBytes, *DesiredBytes;
Word W;
const uint8_t *End = Data + Size;
for (int Arg = 0; Arg < 2; Arg++) {
ExistingBytes = HandleFirst ? Arg1 : Arg2;
DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation;
HandleFirst = !HandleFirst;
W.Set(reinterpret_cast<const uint8_t *>(DesiredBytes), ArgSize);
const size_t kMaxNumPositions = 8;
size_t Positions[kMaxNumPositions];
size_t NumPositions = 0;
for (const uint8_t *Cur = Data;
Cur < End && NumPositions < kMaxNumPositions; Cur++) {
Cur =
(const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize);
if (!Cur) break;
Positions[NumPositions++] = Cur - Data;
}
if (!NumPositions) continue;
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
}
DictionaryEntry DE(W);
return DE;
}
template <class T>
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
if (Rand.RandBool()) Arg1 = Bswap(Arg1);
if (Rand.RandBool()) Arg2 = Bswap(Arg2);
T Arg1Mutation = Arg1 + Rand(-1, 1);
T Arg2Mutation = Arg2 + Rand(-1, 1);
return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation,
sizeof(Arg1), Data, Size);
}
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) {
return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(),
Arg2.data(), Arg1.size(), Data, Size);
}
size_t MutationDispatcher::Mutate_AddWordFromTORC(uint8_t *Data, size_t Size,
size_t MaxSize) {
Word W;
DictionaryEntry DE;
switch (Rand(4)) {
case 0: {
auto X = TPC.TORC8.Get(Rand.Rand());
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
} break;
case 1: {
auto X = TPC.TORC4.Get(Rand.Rand());
if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool())
DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data,
Size);
else
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
} break;
case 2: {
auto X = TPC.TORCW.Get(Rand.Rand());
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
} break;
case 3:
if (Options.UseMemmem) {
auto X = TPC.MMT.Get(Rand.Rand());
DE = DictionaryEntry(X);
}
break;
default:
assert(0);
}
if (!DE.GetW().size()) return 0;
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
if (!Size) return 0;
DictionaryEntry &DERef =
CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
kCmpDictionaryEntriesDequeSize];
DERef = DE;
CurrentDictionaryEntrySequence.push_back(&DERef);
return Size;
}
size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
uint8_t *Data, size_t Size, size_t MaxSize) {
return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
}
size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
size_t Size, size_t MaxSize) {
if (Size > MaxSize) return 0;
if (D.empty()) return 0;
DictionaryEntry &DE = D[Rand(D.size())];
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
if (!Size) return 0;
DE.IncUseCount();
CurrentDictionaryEntrySequence.push_back(&DE);
return Size;
}
// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
// Returns ToSize.
size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
uint8_t *To, size_t ToSize) {
// Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
size_t ToBeg = Rand(ToSize);
size_t CopySize = Rand(ToSize - ToBeg) + 1;
assert(ToBeg + CopySize <= ToSize);
CopySize = std::min(CopySize, FromSize);
size_t FromBeg = Rand(FromSize - CopySize + 1);
assert(FromBeg + CopySize <= FromSize);
memmove(To + ToBeg, From + FromBeg, CopySize);
return ToSize;
}
// Inserts part of From[0,ToSize) into To.
// Returns new size of To on success or 0 on failure.
size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
uint8_t *To, size_t ToSize,
size_t MaxToSize) {
if (ToSize >= MaxToSize) return 0;
size_t AvailableSpace = MaxToSize - ToSize;
size_t MaxCopySize = std::min(AvailableSpace, FromSize);
size_t CopySize = Rand(MaxCopySize) + 1;
size_t FromBeg = Rand(FromSize - CopySize + 1);
assert(FromBeg + CopySize <= FromSize);
size_t ToInsertPos = Rand(ToSize + 1);
assert(ToInsertPos + CopySize <= MaxToSize);
size_t TailSize = ToSize - ToInsertPos;
if (To == From) {
MutateInPlaceHere.resize(MaxToSize);
memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
} else {
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
memmove(To + ToInsertPos, From + FromBeg, CopySize);
}
return ToSize + CopySize;
}
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize || Size == 0) return 0;
// If Size == MaxSize, `InsertPartOf(...)` will
// fail so there's no point using it in this case.
if (Size == MaxSize || Rand.RandBool())
return CopyPartOf(Data, Size, Data, Size);
else
return InsertPartOf(Data, Size, Data, Size, MaxSize);
}
size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
size_t B = Rand(Size);
while (B < Size && !isdigit(Data[B]))
B++;
if (B == Size) return 0;
size_t E = B;
while (E < Size && isdigit(Data[E]))
E++;
assert(B < E);
// now we have digits in [B, E).
// strtol and friends don't accept non-zero-teminated data, parse it manually.
uint64_t Val = Data[B] - '0';
for (size_t i = B + 1; i < E; i++)
Val = Val * 10 + Data[i] - '0';
// Mutate the integer value.
switch (Rand(5)) {
case 0:
Val++;
break;
case 1:
Val--;
break;
case 2:
Val /= 2;
break;
case 3:
Val *= 2;
break;
case 4:
Val = Rand(Val * Val);
break;
default:
assert(0);
}
// Just replace the bytes with the new ones, don't bother moving bytes.
for (size_t i = B; i < E; i++) {
size_t Idx = E + B - i - 1;
assert(Idx >= B && Idx < E);
Data[Idx] = (Val % 10) + '0';
Val /= 10;
}
return Size;
}
template <class T>
size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
if (Size < sizeof(T)) return 0;
size_t Off = Rand(Size - sizeof(T) + 1);
assert(Off + sizeof(T) <= Size);
T Val;
if (Off < 64 && !Rand(4)) {
Val = Size;
if (Rand.RandBool()) Val = Bswap(Val);
} else {
memcpy(&Val, Data + Off, sizeof(Val));
T Add = Rand(21);
Add -= 10;
if (Rand.RandBool())
Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
else
Val = Val + Add; // Add assuming current endiannes.
if (Add == 0 || Rand.RandBool()) // Maybe negate.
Val = -Val;
}
memcpy(Data + Off, &Val, sizeof(Val));
return Size;
}
size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
switch (Rand(4)) {
case 3:
return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
case 2:
return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
case 1:
return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
case 0:
return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
default:
assert(0);
}
return 0;
}
size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
if (Size == 0) return 0;
if (!CrossOverWith) return 0;
const Unit &O = *CrossOverWith;
if (O.empty()) return 0;
size_t NewSize = 0;
switch (Rand(3)) {
case 0:
MutateInPlaceHere.resize(MaxSize);
NewSize = CrossOver(Data, Size, O.data(), O.size(),
MutateInPlaceHere.data(), MaxSize);
memcpy(Data, MutateInPlaceHere.data(), NewSize);
break;
case 1:
NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize);
if (!NewSize) NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
break;
case 2:
NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
break;
default:
assert(0);
}
assert(NewSize > 0 && "CrossOver returned empty unit");
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
return NewSize;
}
void MutationDispatcher::StartMutationSequence() {
CurrentMutatorSequence.clear();
CurrentDictionaryEntrySequence.clear();
}
// Copy successful dictionary entries to PersistentAutoDictionary.
void MutationDispatcher::RecordSuccessfulMutationSequence() {
for (auto DE : CurrentDictionaryEntrySequence) {
// PersistentAutoDictionary.AddWithSuccessCountOne(DE);
DE->IncSuccessCount();
assert(DE->GetW().size());
// Linear search is fine here as this happens seldom.
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
PersistentAutoDictionary.push_back({DE->GetW(), 1});
}
}
void MutationDispatcher::PrintRecommendedDictionary() {
Vector<DictionaryEntry> V;
for (auto &DE : PersistentAutoDictionary)
if (!ManualDictionary.ContainsWord(DE.GetW())) V.push_back(DE);
if (V.empty()) return;
Printf("###### Recommended dictionary. ######\n");
for (auto &DE : V) {
assert(DE.GetW().size());
Printf("\"");
PrintASCII(DE.GetW(), "\"");
Printf(" # Uses: %zd\n", DE.GetUseCount());
}
Printf("###### End of recommended dictionary. ######\n");
}
const char *MutationDispatcher::WriteMutationSequence() {
static std::string buf;
buf = "";
for (size_t i = 0; i < CurrentMutatorSequence.size(); i++) {
buf = buf + " " + CurrentMutatorSequence[i].Name;
}
return buf.c_str();
}
void MutationDispatcher::PrintMutationSequence(bool Verbose) {
return;
Printf("MS: %zd ", CurrentMutatorSequence.size());
size_t EntriesToPrint =
Verbose ? CurrentMutatorSequence.size()
: std::min(kMaxMutationsToPrint, CurrentMutatorSequence.size());
for (size_t i = 0; i < EntriesToPrint; i++)
Printf("%s-", CurrentMutatorSequence[i].Name);
if (!CurrentDictionaryEntrySequence.empty()) {
Printf(" DE: ");
EntriesToPrint = Verbose ? CurrentDictionaryEntrySequence.size()
: std::min(kMaxMutationsToPrint,
CurrentDictionaryEntrySequence.size());
for (size_t i = 0; i < EntriesToPrint; i++) {
Printf("\"");
PrintASCII(CurrentDictionaryEntrySequence[i]->GetW(), "\"-");
}
}
}
std::string MutationDispatcher::MutationSequence() {
std::string MS;
for (auto M : CurrentMutatorSequence) {
MS += M.Name;
MS += "-";
}
return MS;
}
size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
return MutateImpl(Data, Size, MaxSize, Mutators);
}
size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
size_t MaxSize) {
return MutateImpl(Data, Size, MaxSize, DefaultMutators);
}
// Mutates Data in place, returns new size.
size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
size_t MaxSize,
Vector<Mutator> &Mutators) {
assert(MaxSize > 0);
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
// in which case they will return 0.
// Try several times before returning un-mutated data.
for (int Iter = 0; Iter < 100; Iter++) {
auto M = Mutators[Rand(Mutators.size())];
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
if (NewSize && NewSize <= MaxSize) {
if (Options.OnlyASCII) ToASCII(Data, NewSize);
CurrentMutatorSequence.push_back(M);
return NewSize;
}
}
*Data = ' ';
return 1; // Fallback, should not happen frequently.
}
// Mask represents the set of Data bytes that are worth mutating.
size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size,
size_t MaxSize,
const Vector<uint8_t> &Mask) {
size_t MaskedSize = std::min(Size, Mask.size());
// * Copy the worthy bytes into a temporary array T
// * Mutate T
// * Copy T back.
// This is totally unoptimized.
auto &T = MutateWithMaskTemp;
if (T.size() < Size) T.resize(Size);
size_t OneBits = 0;
for (size_t I = 0; I < MaskedSize; I++)
if (Mask[I]) T[OneBits++] = Data[I];
if (!OneBits) return 0;
assert(!T.empty());
size_t NewSize = Mutate(T.data(), OneBits, OneBits);
assert(NewSize <= OneBits);
(void)NewSize;
// Even if NewSize < OneBits we still use all OneBits bytes.
for (size_t I = 0, J = 0; I < MaskedSize; I++)
if (Mask[I]) Data[I] = T[J++];
return Size;
}
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
ManualDictionary.push_back({W, std::numeric_limits<size_t>::max()});
}
} // namespace fuzzer

View File

@ -0,0 +1,160 @@
//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::MutationDispatcher
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_MUTATE_H
#define LLVM_FUZZER_MUTATE_H
#include "FuzzerDefs.h"
#include "FuzzerDictionary.h"
#include "FuzzerOptions.h"
#include "FuzzerRandom.h"
namespace fuzzer {
class MutationDispatcher {
public:
MutationDispatcher(Random &Rand, const FuzzingOptions &Options);
~MutationDispatcher() {}
/// Indicate that we are about to start a new sequence of mutations.
void StartMutationSequence();
/// Print the current sequence of mutations. Only prints the full sequence
/// when Verbose is true.
const char *WriteMutationSequence();
void PrintMutationSequence(bool Verbose = true);
/// Return the current sequence of mutations.
std::string MutationSequence();
/// Indicate that the current sequence of mutations was successful.
void RecordSuccessfulMutationSequence();
/// Mutates data by invoking user-provided mutator.
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by invoking user-provided crossover.
size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by shuffling bytes.
size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by erasing bytes.
size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by inserting a byte.
size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by inserting several repeated bytes.
size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by chanding one byte.
size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by chanding one bit.
size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by copying/inserting a part of data into a different place.
size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by adding a word from the manual dictionary.
size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
size_t MaxSize);
/// Mutates data by adding a word from the TORC.
size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by adding a word from the persistent automatic dictionary.
size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
size_t MaxSize);
/// Tries to find an ASCII integer in Data, changes it to another ASCII int.
size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
/// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
/// CrossOver Data with CrossOverWith.
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
/// Applies one of the configured mutations.
/// Returns the new size of data which could be up to MaxSize.
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
/// Applies one of the configured mutations to the bytes of Data
/// that have '1' in Mask.
/// Mask.size() should be >= Size.
size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize,
const Vector<uint8_t> &Mask);
/// Applies one of the default mutations. Provided as a service
/// to mutation authors.
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
/// Creates a cross-over of two pieces of Data, returns its size.
size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
size_t Size2, uint8_t *Out, size_t MaxOutSize);
void AddWordToManualDictionary(const Word &W);
void PrintRecommendedDictionary();
void SetCrossOverWith(const Unit *U) { CrossOverWith = U; }
Random &GetRand() { return Rand; }
private:
struct Mutator {
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
const char *Name;
};
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
size_t MaxSize);
size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
Vector<Mutator> &Mutators);
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
size_t ToSize, size_t MaxToSize);
size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
size_t ToSize);
size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
DictionaryEntry &DE);
template <class T>
DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
const uint8_t *Data, size_t Size);
DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2,
const uint8_t *Data, size_t Size);
DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2,
const void *Arg1Mutation,
const void *Arg2Mutation,
size_t ArgSize,
const uint8_t *Data, size_t Size);
Random &Rand;
const FuzzingOptions Options;
// Dictionary provided by the user via -dict=DICT_FILE.
Dictionary ManualDictionary;
// Temporary dictionary modified by the fuzzer itself,
// recreated periodically.
Dictionary TempAutoDictionary;
// Persistent dictionary modified by the fuzzer, consists of
// entries that led to successful discoveries in the past mutations.
Dictionary PersistentAutoDictionary;
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
static const size_t kCmpDictionaryEntriesDequeSize = 16;
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
size_t CmpDictionaryEntriesDequeIdx = 0;
const Unit *CrossOverWith = nullptr;
Vector<uint8_t> MutateInPlaceHere;
Vector<uint8_t> MutateWithMaskTemp;
// CustomCrossOver needs its own buffer as a custom implementation may call
// LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere.
Vector<uint8_t> CustomCrossOverInPlaceHere;
Vector<Mutator> Mutators;
Vector<Mutator> DefaultMutators;
Vector<Mutator> CurrentMutatorSequence;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_MUTATE_H

View File

@ -0,0 +1,90 @@
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::FuzzingOptions
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_OPTIONS_H
#define LLVM_FUZZER_OPTIONS_H
#include "FuzzerDefs.h"
namespace fuzzer {
struct FuzzingOptions {
int Verbosity = 1;
size_t MaxLen = 0;
size_t LenControl = 1000;
bool KeepSeed = false;
int UnitTimeoutSec = 300;
int TimeoutExitCode = 70;
int OOMExitCode = 71;
int InterruptExitCode = 72;
int ErrorExitCode = 77;
bool IgnoreTimeouts = true;
bool IgnoreOOMs = true;
bool IgnoreCrashes = false;
int MaxTotalTimeSec = 0;
int RssLimitMb = 0;
int MallocLimitMb = 0;
bool DoCrossOver = true;
bool CrossOverUniformDist = false;
int MutateDepth = 5;
bool ReduceDepth = false;
bool UseCounters = false;
bool UseMemmem = true;
bool UseCmp = false;
int UseValueProfile = false;
bool Shrink = false;
bool ReduceInputs = false;
int ReloadIntervalSec = 1;
bool ShuffleAtStartUp = true;
bool PreferSmall = true;
size_t MaxNumberOfRuns = -1L;
int ReportSlowUnits = 10;
bool OnlyASCII = false;
bool Entropic = true;
size_t EntropicFeatureFrequencyThreshold = 0xFF;
size_t EntropicNumberOfRarestFeatures = 100;
bool EntropicScalePerExecTime = false;
std::string OutputCorpus;
std::string ArtifactPrefix = "./";
std::string ExactArtifactPath;
std::string ExitOnSrcPos;
std::string ExitOnItem;
std::string FocusFunction;
std::string DataFlowTrace;
std::string CollectDataFlow;
std::string FeaturesDir;
std::string MutationGraphFile;
std::string StopFile;
bool SaveArtifacts = true;
bool PrintNEW = true; // Print a status line when new units are found;
bool PrintNewCovPcs = false;
int PrintNewCovFuncs = 0;
bool PrintFinalStats = false;
bool PrintCorpusStats = false;
bool PrintCoverage = false;
bool DumpCoverage = false;
bool DetectLeaks = true;
int PurgeAllocatorIntervalSec = 1;
int TraceMalloc = 0;
bool HandleAbrt = false;
bool HandleAlrm = false;
bool HandleBus = false;
bool HandleFpe = false;
bool HandleIll = false;
bool HandleInt = false;
bool HandleSegv = false;
bool HandleTerm = false;
bool HandleXfsz = false;
bool HandleUsr1 = false;
bool HandleUsr2 = false;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_OPTIONS_H

View File

@ -0,0 +1,163 @@
//===-- FuzzerPlatform.h --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Common platform macros.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_PLATFORM_H
#define LLVM_FUZZER_PLATFORM_H
// Platform detection.
#ifdef __linux__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 1
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#define LIBFUZZER_EMSCRIPTEN 0
#elif __APPLE__
#define LIBFUZZER_APPLE 1
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#define LIBFUZZER_EMSCRIPTEN 0
#elif __NetBSD__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 1
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#define LIBFUZZER_EMSCRIPTEN 0
#elif __FreeBSD__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 1
#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#define LIBFUZZER_EMSCRIPTEN 0
#elif __OpenBSD__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 1
#define LIBFUZZER_WINDOWS 0
#define LIBFUZZER_EMSCRIPTEN 0
#elif _WIN32
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 1
#define LIBFUZZER_EMSCRIPTEN 0
#elif __Fuchsia__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 1
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#define LIBFUZZER_EMSCRIPTEN 0
#elif __EMSCRIPTEN__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#define LIBFUZZER_EMSCRIPTEN 1
#else
#error "Support for your platform has not been implemented"
#endif
#if defined(_MSC_VER) && !defined(__clang__)
// MSVC compiler is being used.
#define LIBFUZZER_MSVC 1
#else
#define LIBFUZZER_MSVC 0
#endif
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#define LIBFUZZER_POSIX \
(LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || \
LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN)
#ifdef __x86_64
#if __has_attribute(target)
#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt")))
#else
#define ATTRIBUTE_TARGET_POPCNT
#endif
#else
#define ATTRIBUTE_TARGET_POPCNT
#endif
#ifdef __clang__ // avoid gcc warning.
#if __has_attribute(no_sanitize)
#define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
#else
#define ATTRIBUTE_NO_SANITIZE_MEMORY
#endif
#define ALWAYS_INLINE __attribute__((always_inline))
#else
#define ATTRIBUTE_NO_SANITIZE_MEMORY
#define ALWAYS_INLINE
#endif // __clang__
#if LIBFUZZER_WINDOWS
#define ATTRIBUTE_NO_SANITIZE_ADDRESS
#else
#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
#endif
#if LIBFUZZER_WINDOWS
#define ATTRIBUTE_ALIGNED(X) __declspec(align(X))
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
// This is used for __sancov_lowest_stack which is needed for
// -fsanitize-coverage=stack-depth. That feature is not yet available on
// Windows, so make the symbol static to avoid linking errors.
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC static
#define ATTRIBUTE_NOINLINE __declspec(noinline)
#else
#define ATTRIBUTE_ALIGNED(X) __attribute__((aligned(X)))
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local
#define ATTRIBUTE_NOINLINE __attribute__((noinline))
#endif
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS
#elif __has_feature(memory_sanitizer)
#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY
#else
#define ATTRIBUTE_NO_SANITIZE_ALL
#endif
#else
#define ATTRIBUTE_NO_SANITIZE_ALL
#endif
#endif // LLVM_FUZZER_PLATFORM_H

View File

@ -0,0 +1,38 @@
//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::Random
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_RANDOM_H
#define LLVM_FUZZER_RANDOM_H
#include <random>
namespace fuzzer {
class Random : public std::minstd_rand {
public:
Random(unsigned int seed) : std::minstd_rand(seed) {}
result_type operator()() { return this->std::minstd_rand::operator()(); }
size_t Rand() { return this->operator()(); }
size_t RandBool() { return Rand() % 2; }
size_t SkewTowardsLast(size_t n) {
size_t T = this->operator()(n * n);
size_t Res = sqrt(T);
return Res;
}
size_t operator()(size_t n) { return n ? Rand() % n : 0; }
intptr_t operator()(intptr_t From, intptr_t To) {
assert(From < To);
intptr_t RangeSize = To - From + 1;
return operator()(RangeSize) + From;
}
};
} // namespace fuzzer
#endif // LLVM_FUZZER_RANDOM_H

View File

@ -0,0 +1,269 @@
//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This code is taken from public domain
// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c)
// and modified by adding anonymous namespace, adding an interface
// function fuzzer::ComputeSHA1() and removing unnecessary code.
//
// lib/Fuzzer can not use SHA1 implementation from openssl because
// openssl may not be available and because we may be fuzzing openssl itself.
// For the same reason we do not want to depend on SHA1 from LLVM tree.
//===----------------------------------------------------------------------===//
#include "FuzzerSHA1.h"
#include "FuzzerDefs.h"
#include "FuzzerPlatform.h"
/* This code is public-domain - it is based on libcrypt
* placed in the public domain by Wei Dai and other contributors.
*/
#include <iomanip>
#include <sstream>
#include <stdint.h>
#include <string.h>
namespace { // Added for LibFuzzer
#ifdef __BIG_ENDIAN__
#define SHA_BIG_ENDIAN
// Windows is always little endian and MSVC doesn't have <endian.h>
#elif defined __LITTLE_ENDIAN__ || LIBFUZZER_WINDOWS
/* override */
#elif defined __BYTE_ORDER
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define SHA_BIG_ENDIAN
#endif
#else // ! defined __LITTLE_ENDIAN__
#include <endian.h> // machine/endian.h
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define SHA_BIG_ENDIAN
#endif
#endif
/* header */
#define HASH_LENGTH 20
#define BLOCK_LENGTH 64
typedef struct sha1nfo {
uint32_t buffer[BLOCK_LENGTH / 4];
uint32_t state[HASH_LENGTH / 4];
uint32_t byteCount;
uint8_t bufferOffset;
uint8_t keyBuffer[BLOCK_LENGTH];
uint8_t innerHash[HASH_LENGTH];
} sha1nfo;
/* public API - prototypes - TODO: doxygen*/
/**
*/
void sha1_init(sha1nfo *s);
/**
*/
void sha1_writebyte(sha1nfo *s, uint8_t data);
/**
*/
void sha1_write(sha1nfo *s, const char *data, size_t len);
/**
*/
uint8_t *sha1_result(sha1nfo *s);
/* code */
#define SHA1_K0 0x5a827999
#define SHA1_K20 0x6ed9eba1
#define SHA1_K40 0x8f1bbcdc
#define SHA1_K60 0xca62c1d6
void sha1_init(sha1nfo *s) {
s->state[0] = 0x67452301;
s->state[1] = 0xefcdab89;
s->state[2] = 0x98badcfe;
s->state[3] = 0x10325476;
s->state[4] = 0xc3d2e1f0;
s->byteCount = 0;
s->bufferOffset = 0;
}
uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
return ((number << bits) | (number >> (32 - bits)));
}
void sha1_hashBlock(sha1nfo *s) {
uint8_t i;
uint32_t a, b, c, d, e, t;
a = s->state[0];
b = s->state[1];
c = s->state[2];
d = s->state[3];
e = s->state[4];
for (i = 0; i < 80; i++) {
if (i >= 16) {
t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^
s->buffer[(i + 2) & 15] ^ s->buffer[i & 15];
s->buffer[i & 15] = sha1_rol32(t, 1);
}
if (i < 20) {
t = (d ^ (b & (c ^ d))) + SHA1_K0;
} else if (i < 40) {
t = (b ^ c ^ d) + SHA1_K20;
} else if (i < 60) {
t = ((b & c) | (d & (b | c))) + SHA1_K40;
} else {
t = (b ^ c ^ d) + SHA1_K60;
}
t += sha1_rol32(a, 5) + e + s->buffer[i & 15];
e = d;
d = c;
c = sha1_rol32(b, 30);
b = a;
a = t;
}
s->state[0] += a;
s->state[1] += b;
s->state[2] += c;
s->state[3] += d;
s->state[4] += e;
}
void sha1_addUncounted(sha1nfo *s, uint8_t data) {
uint8_t *const b = (uint8_t *)s->buffer;
#ifdef SHA_BIG_ENDIAN
b[s->bufferOffset] = data;
#else
b[s->bufferOffset ^ 3] = data;
#endif
s->bufferOffset++;
if (s->bufferOffset == BLOCK_LENGTH) {
sha1_hashBlock(s);
s->bufferOffset = 0;
}
}
void sha1_writebyte(sha1nfo *s, uint8_t data) {
++s->byteCount;
sha1_addUncounted(s, data);
}
void sha1_write(sha1nfo *s, const char *data, size_t len) {
for (; len--;)
sha1_writebyte(s, (uint8_t)*data++);
}
void sha1_pad(sha1nfo *s) {
// Implement SHA-1 padding (fips180-2 §5.1.1)
// Pad with 0x80 followed by 0x00 until the end of the block
sha1_addUncounted(s, 0x80);
while (s->bufferOffset != 56)
sha1_addUncounted(s, 0x00);
// Append length in the last 8 bytes
sha1_addUncounted(s, 0); // We're only using 32 bit lengths
sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths
sha1_addUncounted(s, 0); // So zero pad the top bits
sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8
sha1_addUncounted(
s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as
sha1_addUncounted(s, s->byteCount >> 13); // byte.
sha1_addUncounted(s, s->byteCount >> 5);
sha1_addUncounted(s, s->byteCount << 3);
}
uint8_t *sha1_result(sha1nfo *s) {
// Pad to complete the last block
sha1_pad(s);
#ifndef SHA_BIG_ENDIAN
// Swap byte order back
int i;
for (i = 0; i < 5; i++) {
s->state[i] = (((s->state[i]) << 24) & 0xff000000) |
(((s->state[i]) << 8) & 0x00ff0000) |
(((s->state[i]) >> 8) & 0x0000ff00) |
(((s->state[i]) >> 24) & 0x000000ff);
}
#endif
// Return pointer to hash (20 characters)
return (uint8_t *)s->state;
}
} // namespace
namespace fuzzer {
// The rest is added for LibFuzzer
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) {
sha1nfo s;
sha1_init(&s);
sha1_write(&s, (const char *)Data, Len);
memcpy(Out, sha1_result(&s), HASH_LENGTH);
}
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) {
std::stringstream SS;
for (int i = 0; i < kSHA1NumBytes; i++)
SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i];
return SS.str();
}
std::string Hash(const Unit &U) {
uint8_t Hash[kSHA1NumBytes];
ComputeSHA1(U.data(), U.size(), Hash);
return Sha1ToString(Hash);
}
} // namespace fuzzer

View File

@ -0,0 +1,32 @@
//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// SHA1 utils.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_SHA1_H
#define LLVM_FUZZER_SHA1_H
#include "FuzzerDefs.h"
#include <cstddef>
#include <stdint.h>
namespace fuzzer {
// Private copy of SHA1 implementation.
static const int kSHA1NumBytes = 20;
// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'.
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out);
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]);
std::string Hash(const Unit &U);
} // namespace fuzzer
#endif // LLVM_FUZZER_SHA1_H

View File

@ -0,0 +1,819 @@
//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Trace PCs.
// This module implements __sanitizer_cov_trace_pc_guard[_init],
// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation.
//
//===----------------------------------------------------------------------===//
#include "FuzzerTracePC.h"
#include "FuzzerBuiltins.h"
#include "FuzzerBuiltinsMsvc.h"
#include "FuzzerCorpus.h"
#include "FuzzerDefs.h"
#include "FuzzerDictionary.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include "FuzzerPlatform.h"
#include "FuzzerUtil.h"
#include "FuzzerValueBitMap.h"
#include <set>
// Used by -fsanitize-coverage=stack-depth to track stack depth
ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack;
namespace fuzzer {
TracePC TPC;
size_t TracePC::GetTotalPCCoverage() {
return ObservedPCs.size();
}
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
if (Start == Stop) return;
if (NumModules && Modules[NumModules - 1].Start() == Start) return;
assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
auto & M = Modules[NumModules++];
uint8_t *AlignedStart = RoundUpByPage(Start);
uint8_t *AlignedStop = RoundDownByPage(Stop);
size_t NumFullPages = AlignedStop > AlignedStart
? (AlignedStop - AlignedStart) / PageSize()
: 0;
bool NeedFirst = Start < AlignedStart || !NumFullPages;
bool NeedLast = Stop > AlignedStop && AlignedStop >= AlignedStart;
M.NumRegions = NumFullPages + NeedFirst + NeedLast;
;
assert(M.NumRegions > 0);
M.Regions = new Module::Region[M.NumRegions];
assert(M.Regions);
size_t R = 0;
if (NeedFirst)
M.Regions[R++] = {Start, std::min(Stop, AlignedStart), true, false};
for (uint8_t *P = AlignedStart; P < AlignedStop; P += PageSize())
M.Regions[R++] = {P, P + PageSize(), true, true};
if (NeedLast) M.Regions[R++] = {AlignedStop, Stop, true, false};
assert(R == M.NumRegions);
assert(M.Size() == (size_t)(Stop - Start));
assert(M.Stop() == Stop);
assert(M.Start() == Start);
NumInline8bitCounters += M.Size();
}
void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) {
const PCTableEntry *B = reinterpret_cast<const PCTableEntry *>(Start);
const PCTableEntry *E = reinterpret_cast<const PCTableEntry *>(Stop);
if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return;
assert(NumPCTables < sizeof(ModulePCTable) / sizeof(ModulePCTable[0]));
ModulePCTable[NumPCTables++] = {B, E};
NumPCsInPCTables += E - B;
}
void TracePC::PrintModuleInfo() {
if (NumModules) {
Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ",
NumModules, NumInline8bitCounters);
for (size_t i = 0; i < NumModules; i++)
Printf("%zd [%p, %p), ", Modules[i].Size(), Modules[i].Start(),
Modules[i].Stop());
Printf("\n");
}
if (NumPCTables) {
Printf("INFO: Loaded %zd PC tables (%zd PCs): ", NumPCTables,
NumPCsInPCTables);
for (size_t i = 0; i < NumPCTables; i++) {
Printf("%zd [%p,%p), ", ModulePCTable[i].Stop - ModulePCTable[i].Start,
ModulePCTable[i].Start, ModulePCTable[i].Stop);
}
Printf("\n");
if (NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables) {
Printf(
"ERROR: The size of coverage PC tables does not match the\n"
"number of instrumented PCs. This might be a compiler bug,\n"
"please contact the libFuzzer developers.\n"
"Also check https://bugs.llvm.org/show_bug.cgi?id=34636\n"
"for possible workarounds (tl;dr: don't use the old GNU ld)\n");
_Exit(1);
}
}
if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin())
Printf("INFO: %zd Extra Counters\n", NumExtraCounters);
}
ATTRIBUTE_NO_SANITIZE_ALL
void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
const uintptr_t kBits = 12;
const uintptr_t kMask = (1 << kBits) - 1;
uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits);
ValueProfileMap.AddValueModPrime(Idx);
}
/// \return the address of the previous instruction.
/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h`
inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
#if defined(__arm__)
// T32 (Thumb) branch instructions might be 16 or 32 bit long,
// so we return (pc-2) in that case in order to be safe.
// For A32 mode we return (pc-4) because all instructions are 32 bit long.
return (PC - 3) & (~1);
#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__)
// PCs are always 4 byte aligned.
return PC - 4;
#elif defined(__sparc__) || defined(__mips__)
return PC - 8;
#else
return PC - 1;
#endif
}
/// \return the address of the next instruction.
/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cpp`
ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) {
#if defined(__mips__)
return PC + 8;
#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \
defined(__aarch64__)
return PC + 4;
#else
return PC + 1;
#endif
}
void TracePC::UpdateObservedPCs() {
Vector<uintptr_t> CoveredFuncs;
auto ObservePC = [&](const PCTableEntry *TE) {
if (ObservedPCs.insert(TE).second && DoPrintNewPCs) {
PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p",
GetNextInstructionPc(TE->PC));
Printf("\n");
}
};
auto Observe = [&](const PCTableEntry *TE) {
if (PcIsFuncEntry(TE))
if (++ObservedFuncs[TE->PC] == 1 && NumPrintNewFuncs)
CoveredFuncs.push_back(TE->PC);
ObservePC(TE);
};
if (NumPCsInPCTables) {
if (NumInline8bitCounters == NumPCsInPCTables) {
for (size_t i = 0; i < NumModules; i++) {
auto &M = Modules[i];
assert(M.Size() ==
(size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
for (size_t r = 0; r < M.NumRegions; r++) {
auto &R = M.Regions[r];
if (!R.Enabled) continue;
for (uint8_t *P = R.Start; P < R.Stop; P++)
if (*P) Observe(&ModulePCTable[i].Start[M.Idx(P)]);
}
}
}
}
for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N;
i++) {
Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size());
PrintPC("%p %F %L", "%p", GetNextInstructionPc(CoveredFuncs[i]));
Printf("\n");
}
}
uintptr_t TracePC::PCTableEntryIdx(const PCTableEntry *TE) {
size_t TotalTEs = 0;
for (size_t i = 0; i < NumPCTables; i++) {
auto &M = ModulePCTable[i];
if (TE >= M.Start && TE < M.Stop) return TotalTEs + TE - M.Start;
TotalTEs += M.Stop - M.Start;
}
assert(0);
return 0;
}
const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) {
for (size_t i = 0; i < NumPCTables; i++) {
auto & M = ModulePCTable[i];
size_t Size = M.Stop - M.Start;
if (Idx < Size) return &M.Start[Idx];
Idx -= Size;
}
return nullptr;
}
static std::string GetModuleName(uintptr_t PC) {
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
void *OffsetRaw = nullptr;
if (!EF->__sanitizer_get_module_and_offset_for_pc(
reinterpret_cast<void *>(PC), ModulePathRaw, sizeof(ModulePathRaw),
&OffsetRaw))
return "";
return ModulePathRaw;
}
template <class CallBack>
void TracePC::IterateCoveredFunctions(CallBack CB) {
for (size_t i = 0; i < NumPCTables; i++) {
auto &M = ModulePCTable[i];
assert(M.Start < M.Stop);
auto ModuleName = GetModuleName(M.Start->PC);
for (auto NextFE = M.Start; NextFE < M.Stop;) {
auto FE = NextFE;
assert(PcIsFuncEntry(FE) && "Not a function entry point");
do {
NextFE++;
} while (NextFE < M.Stop && !(PcIsFuncEntry(NextFE)));
CB(FE, NextFE, ObservedFuncs[FE->PC]);
}
}
}
void TracePC::SetFocusFunction(const std::string &FuncName) {
// This function should be called once.
assert(!FocusFunctionCounterPtr);
// "auto" is not a valid function name. If this function is called with "auto"
// that means the auto focus functionality failed.
if (FuncName.empty() || FuncName == "auto") return;
for (size_t M = 0; M < NumModules; M++) {
auto & PCTE = ModulePCTable[M];
size_t N = PCTE.Stop - PCTE.Start;
for (size_t I = 0; I < N; I++) {
if (!(PcIsFuncEntry(&PCTE.Start[I]))) continue; // not a function entry.
auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC));
if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ')
Name = Name.substr(3, std::string::npos);
if (FuncName != Name) continue;
Printf("INFO: Focus function is set to '%s'\n", Name.c_str());
FocusFunctionCounterPtr = Modules[M].Start() + I;
return;
}
}
Printf(
"ERROR: Failed to set focus function. Make sure the function name is "
"valid (%s) and symbolization is enabled.\n",
FuncName.c_str());
exit(1);
}
bool TracePC::ObservedFocusFunction() {
return FocusFunctionCounterPtr && *FocusFunctionCounterPtr;
}
void TracePC::PrintCoverage() {
if (!EF->__sanitizer_symbolize_pc ||
!EF->__sanitizer_get_module_and_offset_for_pc) {
Printf(
"INFO: __sanitizer_symbolize_pc or "
"__sanitizer_get_module_and_offset_for_pc is not available,"
" not printing coverage\n");
return;
}
Printf("COVERAGE:\n");
auto CoveredFunctionCallback = [&](const PCTableEntry *First,
const PCTableEntry *Last,
uintptr_t Counter) {
assert(First < Last);
auto VisualizePC = GetNextInstructionPc(First->PC);
std::string FileStr = DescribePC("%s", VisualizePC);
if (!IsInterestingCoverageFile(FileStr)) return;
std::string FunctionStr = DescribePC("%F", VisualizePC);
if (FunctionStr.find("in ") == 0) FunctionStr = FunctionStr.substr(3);
std::string LineStr = DescribePC("%l", VisualizePC);
size_t NumEdges = Last - First;
Vector<uintptr_t> UncoveredPCs;
for (auto TE = First; TE < Last; TE++)
if (!ObservedPCs.count(TE)) UncoveredPCs.push_back(TE->PC);
Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter);
Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges);
Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(),
LineStr.c_str());
if (Counter)
for (auto PC : UncoveredPCs)
Printf(" UNCOVERED_PC: %s\n",
DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str());
};
IterateCoveredFunctions(CoveredFunctionCallback);
}
// Value profile.
// We keep track of various values that affect control flow.
// These values are inserted into a bit-set-based hash map.
// Every new bit in the map is treated as a new coverage.
//
// For memcmp/strcmp/etc the interesting value is the length of the common
// prefix of the parameters.
// For cmp instructions the interesting value is a XOR of the parameters.
// The interesting value is mixed up with the PC and is then added to the map.
ATTRIBUTE_NO_SANITIZE_ALL
void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
size_t n, bool StopAtZero) {
if (!n) return;
size_t Len = std::min(n, Word::GetMaxSize());
const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
uint8_t B1[Word::kMaxSize];
uint8_t B2[Word::kMaxSize];
// Copy the data into locals in this non-msan-instrumented function
// to avoid msan complaining further.
size_t Hash = 0; // Compute some simple hash of both strings.
for (size_t i = 0; i < Len; i++) {
B1[i] = A1[i];
B2[i] = A2[i];
size_t T = B1[i];
Hash ^= (T << 8) | B2[i];
}
size_t I = 0;
uint8_t HammingDistance = 0;
for (; I < Len; I++) {
if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) {
HammingDistance = Popcountll(B1[I] ^ B2[I]);
break;
}
}
size_t PC = reinterpret_cast<size_t>(caller_pc);
size_t Idx = (PC & 4095) | (I << 12);
Idx += HammingDistance;
ValueProfileMap.AddValue(Idx);
TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len));
}
template <class T>
ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE ATTRIBUTE_NO_SANITIZE_ALL void
TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) {
uint64_t ArgXor = Arg1 ^ Arg2;
if (sizeof(T) == 4)
TORC4.Insert(ArgXor, Arg1, Arg2);
else if (sizeof(T) == 8)
TORC8.Insert(ArgXor, Arg1, Arg2);
uint64_t HammingDistance = Popcountll(ArgXor); // [0,64]
uint64_t AbsoluteDistance = (Arg1 == Arg2 ? 0 : Clzll(Arg1 - Arg2) + 1);
ValueProfileMap.AddValue(PC * 128 + HammingDistance);
ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance);
}
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
size_t Len = 0;
for (; Len < MaxLen && S[Len]; Len++) {}
return Len;
}
// Finds min of (strlen(S1), strlen(S2)).
// Needed bacause one of these strings may actually be non-zero terminated.
static size_t InternalStrnlen2(const char *S1, const char *S2) {
size_t Len = 0;
for (; S1[Len] && S2[Len]; Len++) {}
return Len;
}
void TracePC::ClearInlineCounters() {
IterateCounterRegions([](const Module::Region &R) {
if (R.Enabled) memset(R.Start, 0, R.Stop - R.Start);
});
}
ATTRIBUTE_NO_SANITIZE_ALL
void TracePC::RecordInitialStack() {
int stack;
__sancov_lowest_stack = InitialStack = reinterpret_cast<uintptr_t>(&stack);
}
uintptr_t TracePC::GetMaxStackOffset() const {
return InitialStack - __sancov_lowest_stack; // Stack grows down
}
void WarnAboutDeprecatedInstrumentation(const char *flag) {
// Use RawPrint because Printf cannot be used on Windows before OutputFile is
// initialized.
RawPrint(flag);
RawPrint(
" is no longer supported by libFuzzer.\n"
"Please either migrate to a compiler that supports -fsanitize=fuzzer\n"
"or use an older version of libFuzzer\n");
exit(1);
}
} // namespace fuzzer
extern "C" {
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
fuzzer::WarnAboutDeprecatedInstrumentation(
"-fsanitize-coverage=trace-pc-guard");
}
// Best-effort support for -fsanitize-coverage=trace-pc, which is available
// in both Clang and GCC.
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc() {
fuzzer::WarnAboutDeprecatedInstrumentation("-fsanitize-coverage=trace-pc");
}
ATTRIBUTE_INTERFACE
void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
fuzzer::WarnAboutDeprecatedInstrumentation(
"-fsanitize-coverage=trace-pc-guard");
}
ATTRIBUTE_INTERFACE
void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) {
fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop);
}
ATTRIBUTE_INTERFACE
void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
const uintptr_t *pcs_end) {
fuzzer::TPC.HandlePCsInit(pcs_beg, pcs_end);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCallerCallee(PC, Callee);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
// Now the __sanitizer_cov_trace_const_cmp[1248] callbacks just mimic
// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however,
// should be changed later to make full use of instrumentation.
void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
uint64_t N = Cases[0];
uint64_t ValSizeInBits = Cases[1];
uint64_t *Vals = Cases + 2;
// Skip the most common and the most boring case: all switch values are small.
// We may want to skip this at compile-time, but it will make the
// instrumentation less general.
if (Vals[N - 1] < 256) return;
// Also skip small inputs values, they won't give good signal.
if (Val < 256) return;
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
size_t i;
uint64_t Smaller = 0;
uint64_t Larger = ~(uint64_t)0;
// Find two switch values such that Smaller < Val < Larger.
// Use 0 and 0xfff..f as the defaults.
for (i = 0; i < N; i++) {
if (Val < Vals[i]) {
Larger = Vals[i];
break;
}
if (Val > Vals[i]) Smaller = Vals[i];
}
// Apply HandleCmp to {Val,Smaller} and {Val, Larger},
// use i as the PC modifier for HandleCmp.
if (ValSizeInBits == 16) {
fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast<uint16_t>(Val),
(uint16_t)(Smaller));
fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast<uint16_t>(Val),
(uint16_t)(Larger));
} else if (ValSizeInBits == 32) {
fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast<uint32_t>(Val),
(uint32_t)(Smaller));
fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast<uint32_t>(Val),
(uint32_t)(Larger));
} else {
fuzzer::TPC.HandleCmp(PC + 2 * i, Val, Smaller);
fuzzer::TPC.HandleCmp(PC + 2 * i + 1, Val, Larger);
}
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_div4(uint32_t Val) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_div8(uint64_t Val) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_gep(uintptr_t Idx) {
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
__sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
size_t n, int result) {
if (!fuzzer::RunningUserCallback) return;
if (result == 0) return; // No reason to mutate.
if (n <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ false);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
__sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
size_t n, int result) {
if (!fuzzer::RunningUserCallback) return;
if (result == 0) return; // No reason to mutate.
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
n = std::min(n, Len1);
n = std::min(n, Len2);
if (n <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ true);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
__sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2,
int result) {
if (!fuzzer::RunningUserCallback) return;
if (result == 0) return; // No reason to mutate.
size_t N = fuzzer::InternalStrnlen2(s1, s2);
if (N <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/ true);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
__sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
const char *s2, size_t n, int result) {
if (!fuzzer::RunningUserCallback) return;
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
__sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
const char *s2, int result) {
if (!fuzzer::RunningUserCallback) return;
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
__sanitizer_weak_hook_strstr(void *called_pc, const char *s1, const char *s2,
char *result) {
if (!fuzzer::RunningUserCallback) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
__sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
const char *s2, char *result) {
if (!fuzzer::RunningUserCallback) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
__sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
const void *s2, size_t len2, void *result) {
if (!fuzzer::RunningUserCallback) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), len2);
}
} // extern "C"

View File

@ -0,0 +1,291 @@
//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::TracePC
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_TRACE_PC
#define LLVM_FUZZER_TRACE_PC
#include "FuzzerDefs.h"
#include "FuzzerDictionary.h"
#include "FuzzerValueBitMap.h"
#include <set>
#include <unordered_map>
namespace fuzzer {
// TableOfRecentCompares (TORC) remembers the most recently performed
// comparisons of type T.
// We record the arguments of CMP instructions in this table unconditionally
// because it seems cheaper this way than to compute some expensive
// conditions inside __sanitizer_cov_trace_cmp*.
// After the unit has been executed we may decide to use the contents of
// this table to populate a Dictionary.
template<class T, size_t kSizeT>
struct TableOfRecentCompares {
static const size_t kSize = kSizeT;
struct Pair {
T A, B;
};
ATTRIBUTE_NO_SANITIZE_ALL
void Insert(size_t Idx, const T &Arg1, const T &Arg2) {
Idx = Idx % kSize;
Table[Idx].A = Arg1;
Table[Idx].B = Arg2;
}
Pair Get(size_t I) { return Table[I % kSize]; }
Pair Table[kSize];
};
template <size_t kSizeT>
struct MemMemTable {
static const size_t kSize = kSizeT;
Word MemMemWords[kSize];
Word EmptyWord;
void Add(const uint8_t *Data, size_t Size) {
if (Size <= 2) return;
Size = std::min(Size, Word::GetMaxSize());
size_t Idx = SimpleFastHash(Data, Size) % kSize;
MemMemWords[Idx].Set(Data, Size);
}
const Word &Get(size_t Idx) {
for (size_t i = 0; i < kSize; i++) {
const Word &W = MemMemWords[(Idx + i) % kSize];
if (W.size()) return W;
}
EmptyWord.Set(nullptr, 0);
return EmptyWord;
}
};
class TracePC {
public:
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop);
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
template <class T> void HandleCmp(uintptr_t PC, T Arg1, T Arg2);
size_t GetTotalPCCoverage();
void SetUseCounters(bool UC) { UseCounters = UC; }
void SetUseValueProfileMask(uint32_t VPMask) { UseValueProfileMask = VPMask; }
void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; }
void UpdateObservedPCs();
template <class Callback> void CollectFeatures(Callback CB) const;
void ResetMaps() {
ValueProfileMap.Reset();
ClearExtraCounters();
ClearInlineCounters();
}
void ClearInlineCounters();
void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize);
void PrintFeatureSet();
void PrintModuleInfo();
void PrintCoverage();
template<class CallBack>
void IterateCoveredFunctions(CallBack CB);
void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
size_t n, bool StopAtZero);
TableOfRecentCompares<uint32_t, 32> TORC4;
TableOfRecentCompares<uint64_t, 32> TORC8;
TableOfRecentCompares<Word, 32> TORCW;
MemMemTable<1024> MMT;
void RecordInitialStack();
uintptr_t GetMaxStackOffset() const;
template<class CallBack>
void ForEachObservedPC(CallBack CB) {
for (auto PC : ObservedPCs)
CB(PC);
}
void SetFocusFunction(const std::string &FuncName);
bool ObservedFocusFunction();
struct PCTableEntry {
uintptr_t PC, PCFlags;
};
uintptr_t PCTableEntryIdx(const PCTableEntry *TE);
const PCTableEntry *PCTableEntryByIdx(uintptr_t Idx);
static uintptr_t GetNextInstructionPc(uintptr_t PC);
bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; }
private:
bool UseCounters = false;
uint32_t UseValueProfileMask = false;
bool DoPrintNewPCs = false;
size_t NumPrintNewFuncs = 0;
// Module represents the array of 8-bit counters split into regions
// such that every region, except maybe the first and the last one, is one
// full page.
struct Module {
struct Region {
uint8_t *Start, *Stop;
bool Enabled;
bool OneFullPage;
};
Region *Regions;
size_t NumRegions;
uint8_t *Start() { return Regions[0].Start; }
uint8_t *Stop() { return Regions[NumRegions - 1].Stop; }
size_t Size() { return Stop() - Start(); }
size_t Idx(uint8_t *P) {
assert(P >= Start() && P < Stop());
return P - Start();
}
};
Module Modules[4096];
size_t NumModules; // linker-initialized.
size_t NumInline8bitCounters;
template <class Callback>
void IterateCounterRegions(Callback CB) {
for (size_t m = 0; m < NumModules; m++)
for (size_t r = 0; r < Modules[m].NumRegions; r++)
CB(Modules[m].Regions[r]);
}
struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096];
size_t NumPCTables;
size_t NumPCsInPCTables;
Set<const PCTableEntry*> ObservedPCs;
std::unordered_map<uintptr_t, uintptr_t> ObservedFuncs; // PC => Counter.
uint8_t *FocusFunctionCounterPtr = nullptr;
ValueBitMap ValueProfileMap;
uintptr_t InitialStack;
};
template <class Callback>
// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value);
ATTRIBUTE_NO_SANITIZE_ALL
size_t ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End,
size_t FirstFeature, Callback Handle8bitCounter) {
typedef uintptr_t LargeType;
const size_t Step = sizeof(LargeType) / sizeof(uint8_t);
const size_t StepMask = Step - 1;
auto P = Begin;
// Iterate by 1 byte until either the alignment boundary or the end.
for (; reinterpret_cast<uintptr_t>(P) & StepMask && P < End; P++)
if (uint8_t V = *P)
Handle8bitCounter(FirstFeature, P - Begin, V);
// Iterate by Step bytes at a time.
for (; P < End; P += Step)
if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P)) {
Bundle = HostToLE(Bundle);
for (size_t I = 0; I < Step; I++, Bundle >>= 8)
if (uint8_t V = Bundle & 0xff)
Handle8bitCounter(FirstFeature, P - Begin + I, V);
}
// Iterate by 1 byte until the end.
for (; P < End; P++)
if (uint8_t V = *P)
Handle8bitCounter(FirstFeature, P - Begin, V);
return End - Begin;
}
// Given a non-zero Counter returns a number in the range [0,7].
template<class T>
unsigned CounterToFeature(T Counter) {
// Returns a feature number by placing Counters into buckets as illustrated
// below.
//
// Counter bucket: [1] [2] [3] [4-7] [8-15] [16-31] [32-127] [128+]
// Feature number: 0 1 2 3 4 5 6 7
//
// This is a heuristic taken from AFL (see
// http://lcamtuf.coredump.cx/afl/technical_details.txt).
//
// This implementation may change in the future so clients should
// not rely on it.
assert(Counter);
unsigned Bit = 0;
/**/ if (Counter >= 128) Bit = 7;
else if (Counter >= 32) Bit = 6;
else if (Counter >= 16) Bit = 5;
else if (Counter >= 8) Bit = 4;
else if (Counter >= 4) Bit = 3;
else if (Counter >= 3) Bit = 2;
else if (Counter >= 2) Bit = 1;
return Bit;
}
template <class Callback> // void Callback(size_t Feature)
ATTRIBUTE_NO_SANITIZE_ADDRESS
ATTRIBUTE_NOINLINE
void TracePC::CollectFeatures(Callback HandleFeature) const {
auto Handle8bitCounter = [&](size_t FirstFeature,
size_t Idx, uint8_t Counter) {
if (UseCounters)
HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter));
else
HandleFeature(FirstFeature + Idx);
};
size_t FirstFeature = 0;
for (size_t i = 0; i < NumModules; i++) {
for (size_t r = 0; r < Modules[i].NumRegions; r++) {
if (!Modules[i].Regions[r].Enabled) continue;
FirstFeature += 8 * ForEachNonZeroByte(Modules[i].Regions[r].Start,
Modules[i].Regions[r].Stop,
FirstFeature, Handle8bitCounter);
}
}
FirstFeature +=
8 * ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(),
FirstFeature, Handle8bitCounter);
if (UseValueProfileMask) {
ValueProfileMap.ForEach([&](size_t Idx) {
HandleFeature(FirstFeature + Idx);
});
FirstFeature += ValueProfileMap.SizeInBits();
}
// Step function, grows similar to 8 * Log_2(A).
auto StackDepthStepFunction = [](uint32_t A) -> uint32_t {
if (!A) return A;
uint32_t Log2 = Log(A);
if (Log2 < 3) return A;
Log2 -= 3;
return (Log2 + 1) * 8 + ((A >> Log2) & 7);
};
assert(StackDepthStepFunction(1024) == 64);
assert(StackDepthStepFunction(1024 * 4) == 80);
assert(StackDepthStepFunction(1024 * 1024) == 144);
if (auto MaxStackOffset = GetMaxStackOffset())
HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8));
}
extern TracePC TPC;
} // namespace fuzzer
#endif // LLVM_FUZZER_TRACE_PC

View File

@ -0,0 +1,314 @@
//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils.
//===----------------------------------------------------------------------===//
#include "FuzzerUtil.h"
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include <cassert>
#include <chrono>
#include <cstring>
#include <errno.h>
#include <mutex>
#include <signal.h>
#include <sstream>
#include <stdio.h>
#include <sys/types.h>
#include <thread>
namespace fuzzer {
void PrintHexArray(const uint8_t *Data, size_t Size, const char *PrintAfter) {
for (size_t i = 0; i < Size; i++)
Printf("0x%x,", (unsigned)Data[i]);
Printf("%s", PrintAfter);
}
void Print(const Unit &v, const char *PrintAfter) {
PrintHexArray(v.data(), v.size(), PrintAfter);
}
void PrintASCIIByte(uint8_t Byte) {
if (Byte == '\\')
Printf("\\\\");
else if (Byte == '"')
Printf("\\\"");
else if (Byte >= 32 && Byte < 127)
Printf("%c", Byte);
else
Printf("\\x%02x", Byte);
}
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) {
for (size_t i = 0; i < Size; i++)
PrintASCIIByte(Data[i]);
Printf("%s", PrintAfter);
}
void PrintASCII(const Unit &U, const char *PrintAfter) {
PrintASCII(U.data(), U.size(), PrintAfter);
}
bool ToASCII(uint8_t *Data, size_t Size) {
bool Changed = false;
for (size_t i = 0; i < Size; i++) {
uint8_t &X = Data[i];
auto NewX = X;
NewX &= 127;
if (!isspace(NewX) && !isprint(NewX)) NewX = ' ';
Changed |= NewX != X;
X = NewX;
}
return Changed;
}
bool IsASCII(const Unit &U) {
return IsASCII(U.data(), U.size());
}
bool IsASCII(const uint8_t *Data, size_t Size) {
for (size_t i = 0; i < Size; i++)
if (!(isprint(Data[i]) || isspace(Data[i]))) return false;
return true;
}
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) {
U->clear();
if (Str.empty()) return false;
size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R].
// Skip spaces from both sides.
while (L < R && isspace(Str[L]))
L++;
while (R > L && isspace(Str[R]))
R--;
if (R - L < 2) return false;
// Check the closing "
if (Str[R] != '"') return false;
R--;
// Find the opening "
while (L < R && Str[L] != '"')
L++;
if (L >= R) return false;
assert(Str[L] == '\"');
L++;
assert(L <= R);
for (size_t Pos = L; Pos <= R; Pos++) {
uint8_t V = (uint8_t)Str[Pos];
if (!isprint(V) && !isspace(V)) return false;
if (V == '\\') {
// Handle '\\'
if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) {
U->push_back(Str[Pos + 1]);
Pos++;
continue;
}
// Handle '\xAB'
if (Pos + 3 <= R && Str[Pos + 1] == 'x' && isxdigit(Str[Pos + 2]) &&
isxdigit(Str[Pos + 3])) {
char Hex[] = "0xAA";
Hex[2] = Str[Pos + 2];
Hex[3] = Str[Pos + 3];
U->push_back(strtol(Hex, nullptr, 16));
Pos += 3;
continue;
}
return false; // Invalid escape.
} else {
// Any other character.
U->push_back(V);
}
}
return true;
}
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units) {
if (Text.empty()) {
Printf("ParseDictionaryFile: file does not exist or is empty\n");
return false;
}
std::istringstream ISS(Text);
Units->clear();
Unit U;
int LineNo = 0;
std::string S;
while (std::getline(ISS, S, '\n')) {
LineNo++;
size_t Pos = 0;
while (Pos < S.size() && isspace(S[Pos]))
Pos++; // Skip spaces.
if (Pos == S.size()) continue; // Empty line.
if (S[Pos] == '#') continue; // Comment line.
if (ParseOneDictionaryEntry(S, &U)) {
Units->push_back(U);
} else {
Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo,
S.c_str());
return false;
}
}
return true;
}
// Code duplicated (and tested) in llvm/include/llvm/Support/Base64.h
std::string Base64(const Unit &U) {
static const char Table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string Buffer;
Buffer.resize(((U.size() + 2) / 3) * 4);
size_t i = 0, j = 0;
for (size_t n = U.size() / 3 * 3; i < n; i += 3, j += 4) {
uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8) |
(unsigned char)U[i + 2];
Buffer[j + 0] = Table[(x >> 18) & 63];
Buffer[j + 1] = Table[(x >> 12) & 63];
Buffer[j + 2] = Table[(x >> 6) & 63];
Buffer[j + 3] = Table[x & 63];
}
if (i + 1 == U.size()) {
uint32_t x = ((unsigned char)U[i] << 16);
Buffer[j + 0] = Table[(x >> 18) & 63];
Buffer[j + 1] = Table[(x >> 12) & 63];
Buffer[j + 2] = '=';
Buffer[j + 3] = '=';
} else if (i + 2 == U.size()) {
uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8);
Buffer[j + 0] = Table[(x >> 18) & 63];
Buffer[j + 1] = Table[(x >> 12) & 63];
Buffer[j + 2] = Table[(x >> 6) & 63];
Buffer[j + 3] = '=';
}
return Buffer;
}
static std::mutex SymbolizeMutex;
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
if (!EF->__sanitizer_symbolize_pc || !l.owns_lock())
return "<can not symbolize>";
char PcDescr[1024] = {};
EF->__sanitizer_symbolize_pc(reinterpret_cast<void *>(PC), SymbolizedFMT,
PcDescr, sizeof(PcDescr));
PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case.
return PcDescr;
}
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) {
if (EF->__sanitizer_symbolize_pc)
Printf("%s", DescribePC(SymbolizedFMT, PC).c_str());
else
Printf(FallbackFMT, PC);
}
void PrintStackTrace() {
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
if (EF->__sanitizer_print_stack_trace && l.owns_lock())
EF->__sanitizer_print_stack_trace();
}
void PrintMemoryProfile() {
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
if (EF->__sanitizer_print_memory_profile && l.owns_lock())
EF->__sanitizer_print_memory_profile(95, 8);
}
unsigned NumberOfCpuCores() {
unsigned N = std::thread::hardware_concurrency();
if (!N) {
Printf(
"WARNING: std::thread::hardware_concurrency not well defined for "
"your platform. Assuming CPU count of 1.\n");
N = 1;
}
return N;
}
size_t SimpleFastHash(const uint8_t *Data, size_t Size) {
size_t Res = 0;
for (size_t i = 0; i < Size; i++)
Res = Res * 11 + Data[i];
return Res;
}
} // namespace fuzzer

View File

@ -0,0 +1,117 @@
//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Util functions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_UTIL_H
#define LLVM_FUZZER_UTIL_H
#include "FuzzerBuiltins.h"
#include "FuzzerBuiltinsMsvc.h"
#include "FuzzerCommand.h"
#include "FuzzerDefs.h"
namespace fuzzer {
void PrintHexArray(const Unit &U, const char *PrintAfter = "");
void PrintHexArray(const uint8_t *Data, size_t Size,
const char *PrintAfter = "");
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = "");
void PrintASCII(const Unit &U, const char *PrintAfter = "");
// Changes U to contain only ASCII (isprint+isspace) characters.
// Returns true iff U has been changed.
bool ToASCII(uint8_t *Data, size_t Size);
bool IsASCII(const Unit &U);
bool IsASCII(const uint8_t *Data, size_t Size);
std::string Base64(const Unit &U);
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC);
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC);
void PrintStackTrace();
void PrintMemoryProfile();
unsigned NumberOfCpuCores();
// Platform specific functions.
void SetSignalHandler(const FuzzingOptions& Options);
void SleepSeconds(int Seconds);
unsigned long GetPid();
size_t GetPeakRSSMb();
int ExecuteCommand(const Command &Cmd);
bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput);
// Fuchsia does not have popen/pclose.
FILE *OpenProcessPipe(const char *Command, const char *Mode);
int CloseProcessPipe(FILE *F);
const void *SearchMemory(const void *haystack, size_t haystacklen,
const void *needle, size_t needlelen);
std::string CloneArgsWithoutX(const Vector<std::string> &Args,
const char *X1, const char *X2);
inline std::string CloneArgsWithoutX(const Vector<std::string> &Args,
const char *X) {
return CloneArgsWithoutX(Args, X, X);
}
inline std::pair<std::string, std::string> SplitBefore(std::string X,
std::string S) {
auto Pos = S.find(X);
if (Pos == std::string::npos)
return std::make_pair(S, "");
return std::make_pair(S.substr(0, Pos), S.substr(Pos));
}
void DiscardOutput(int Fd);
std::string DisassembleCmd(const std::string &FileName);
std::string SearchRegexCmd(const std::string &Regex);
size_t SimpleFastHash(const uint8_t *Data, size_t Size);
inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; }
inline size_t PageSize() { return 4096; }
inline uint8_t *RoundUpByPage(uint8_t *P) {
uintptr_t X = reinterpret_cast<uintptr_t>(P);
size_t Mask = PageSize() - 1;
X = (X + Mask) & ~Mask;
return reinterpret_cast<uint8_t *>(X);
}
inline uint8_t *RoundDownByPage(uint8_t *P) {
uintptr_t X = reinterpret_cast<uintptr_t>(P);
size_t Mask = PageSize() - 1;
X = X & ~Mask;
return reinterpret_cast<uint8_t *>(X);
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
template <typename T> T HostToLE(T X) { return X; }
#else
template <typename T> T HostToLE(T X) { return Bswap(X); }
#endif
} // namespace fuzzer
#endif // LLVM_FUZZER_UTIL_H

View File

@ -0,0 +1,205 @@
//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils for Darwin.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_APPLE
#include "FuzzerCommand.h"
#include "FuzzerIO.h"
#include <mutex>
#include <signal.h>
#include <spawn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
// There is no header for this on macOS so declare here
extern "C" char **environ;
namespace fuzzer {
static std::mutex SignalMutex;
// Global variables used to keep track of how signal handling should be
// restored. They should **not** be accessed without holding `SignalMutex`.
static int ActiveThreadCount = 0;
static struct sigaction OldSigIntAction;
static struct sigaction OldSigQuitAction;
static sigset_t OldBlockedSignalsSet;
// This is a reimplementation of Libc's `system()`. On Darwin the Libc
// implementation contains a mutex which prevents it from being used
// concurrently. This implementation **can** be used concurrently. It sets the
// signal handlers when the first thread enters and restores them when the last
// thread finishes execution of the function and ensures this is not racey by
// using a mutex.
int ExecuteCommand(const Command &Cmd) {
std::string CmdLine = Cmd.toString();
posix_spawnattr_t SpawnAttributes;
if (posix_spawnattr_init(&SpawnAttributes)) return -1;
// Block and ignore signals of the current process when the first thread
// enters.
{
std::lock_guard<std::mutex> Lock(SignalMutex);
if (ActiveThreadCount == 0) {
static struct sigaction IgnoreSignalAction;
sigset_t BlockedSignalsSet;
memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
IgnoreSignalAction.sa_handler = SIG_IGN;
if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
Printf("Failed to ignore SIGINT\n");
(void)posix_spawnattr_destroy(&SpawnAttributes);
return -1;
}
if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
Printf("Failed to ignore SIGQUIT\n");
// Try our best to restore the signal handlers.
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
(void)posix_spawnattr_destroy(&SpawnAttributes);
return -1;
}
(void)sigemptyset(&BlockedSignalsSet);
(void)sigaddset(&BlockedSignalsSet, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
-1) {
Printf("Failed to block SIGCHLD\n");
// Try our best to restore the signal handlers.
(void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
(void)posix_spawnattr_destroy(&SpawnAttributes);
return -1;
}
}
++ActiveThreadCount;
}
// NOTE: Do not introduce any new `return` statements past this
// point. It is important that `ActiveThreadCount` always be decremented
// when leaving this function.
// Make sure the child process uses the default handlers for the
// following signals rather than inheriting what the parent has.
sigset_t DefaultSigSet;
(void)sigemptyset(&DefaultSigSet);
(void)sigaddset(&DefaultSigSet, SIGQUIT);
(void)sigaddset(&DefaultSigSet, SIGINT);
(void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
// Make sure the child process doesn't block SIGCHLD
(void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
(void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
pid_t Pid;
char ** Environ = environ; // Read from global
const char *CommandCStr = CmdLine.c_str();
char *const Argv[] = {strdup("sh"), strdup("-c"), strdup(CommandCStr), NULL};
int ErrorCode = 0, ProcessStatus = 0;
// FIXME: We probably shouldn't hardcode the shell path.
ErrorCode =
posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, Argv, Environ);
(void)posix_spawnattr_destroy(&SpawnAttributes);
if (!ErrorCode) {
pid_t SavedPid = Pid;
do {
// Repeat until call completes uninterrupted.
Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
} while (Pid == -1 && errno == EINTR);
if (Pid == -1) {
// Fail for some other reason.
ProcessStatus = -1;
}
} else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
// Fork failure.
ProcessStatus = -1;
} else {
// Shell execution failure.
ProcessStatus = W_EXITCODE(127, 0);
}
for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
free(Argv[i]);
// Restore the signal handlers of the current process when the last thread
// using this function finishes.
{
std::lock_guard<std::mutex> Lock(SignalMutex);
--ActiveThreadCount;
if (ActiveThreadCount == 0) {
bool FailedRestore = false;
if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
Printf("Failed to restore SIGINT handling\n");
FailedRestore = true;
}
if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
Printf("Failed to restore SIGQUIT handling\n");
FailedRestore = true;
}
if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
Printf("Failed to unblock SIGCHLD\n");
FailedRestore = true;
}
if (FailedRestore) ProcessStatus = -1;
}
}
return ProcessStatus;
}
void DiscardOutput(int Fd) {
FILE *Temp = fopen("/dev/null", "w");
if (!Temp) return;
dup2(fileno(Temp), Fd);
fclose(Temp);
}
} // namespace fuzzer
#endif // LIBFUZZER_APPLE

View File

@ -0,0 +1,658 @@
//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils implementation using Fuchsia/Zircon APIs.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_FUCHSIA
#include "FuzzerInternal.h"
#include "FuzzerUtil.h"
#include <cassert>
#include <cerrno>
#include <cinttypes>
#include <cstdint>
#include <fcntl.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/spawn.h>
#include <string>
#include <sys/select.h>
#include <thread>
#include <unistd.h>
#include <zircon/errors.h>
#include <zircon/process.h>
#include <zircon/sanitizer.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/debug.h>
#include <zircon/syscalls/exception.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <vector>
namespace fuzzer {
// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written
// around, the general approach is to spin up dedicated threads to watch for
// each requested condition (alarm, interrupt, crash). Of these, the crash
// handler is the most involved, as it requires resuming the crashed thread in
// order to invoke the sanitizers to get the needed state.
// Forward declaration of assembly trampoline needed to resume crashed threads.
// This appears to have external linkage to C++, which is why it's not in the
// anonymous namespace. The assembly definition inside MakeTrampoline()
// actually defines the symbol with internal linkage only.
void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");
namespace {
// Helper function to handle Zircon syscall failures.
void ExitOnErr(zx_status_t Status, const char *Syscall) {
if (Status != ZX_OK) {
Printf("libFuzzer: %s failed: %s\n", Syscall,
_zx_status_get_string(Status));
exit(1);
}
}
void AlarmHandler(int Seconds) {
while (true) {
SleepSeconds(Seconds);
Fuzzer::StaticAlarmCallback();
}
}
void InterruptHandler() {
fd_set readfds;
// Ctrl-C sends ETX in Zircon.
do {
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, nullptr);
} while (!FD_ISSET(STDIN_FILENO, &readfds) || getchar() != 0x03);
Fuzzer::StaticInterruptCallback();
}
// CFAOffset is used to reference the stack pointer before entering the
// trampoline (Stack Pointer + CFAOffset = prev Stack Pointer). Before jumping
// to the trampoline we copy all the registers onto the stack. We need to make
// sure that the new stack has enough space to store all the registers.
//
// The trampoline holds CFI information regarding the registers stored in the
// stack, which is then used by the unwinder to restore them.
#if defined(__x86_64__)
// In x86_64 the crashing function might also be using the red zone (128 bytes
// on top of their rsp).
constexpr size_t CFAOffset = 128 + sizeof(zx_thread_state_general_regs_t);
#elif defined(__aarch64__)
// In aarch64 we need to always have the stack pointer aligned to 16 bytes, so
// we make sure that we are keeping that same alignment.
constexpr size_t CFAOffset =
(sizeof(zx_thread_state_general_regs_t) + 15) & -(uintptr_t)16;
#endif
// For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback
// without POSIX signal handlers. To achieve this, we use an assembly
// function to add the necessary CFI unwinding information and a C function to
// bridge from that back into C++.
// FIXME: This works as a short-term solution, but this code really shouldn't
// be architecture dependent. A better long term solution is to implement
// remote unwinding and expose the necessary APIs through sanitizer_common
// and/or ASAN to allow the exception handling thread to gather the crash
// state directly.
//
// Alternatively, Fuchsia may in future actually implement basic signal
// handling for the machine trap signals.
#if defined(__x86_64__)
#define FOREACH_REGISTER(OP_REG, OP_NUM) \
OP_REG(rax) \
OP_REG(rbx) \
OP_REG(rcx) \
OP_REG(rdx) \
OP_REG(rsi) \
OP_REG(rdi) \
OP_REG(rbp) \
OP_REG(rsp) \
OP_REG(r8) \
OP_REG(r9) \
OP_REG(r10) \
OP_REG(r11) \
OP_REG(r12) \
OP_REG(r13) \
OP_REG(r14) \
OP_REG(r15) \
OP_REG(rip)
#elif defined(__aarch64__)
#define FOREACH_REGISTER(OP_REG, OP_NUM) \
OP_NUM(0) \
OP_NUM(1) \
OP_NUM(2) \
OP_NUM(3) \
OP_NUM(4) \
OP_NUM(5) \
OP_NUM(6) \
OP_NUM(7) \
OP_NUM(8) \
OP_NUM(9) \
OP_NUM(10) \
OP_NUM(11) \
OP_NUM(12) \
OP_NUM(13) \
OP_NUM(14) \
OP_NUM(15) \
OP_NUM(16) \
OP_NUM(17) \
OP_NUM(18) \
OP_NUM(19) \
OP_NUM(20) \
OP_NUM(21) \
OP_NUM(22) \
OP_NUM(23) \
OP_NUM(24) \
OP_NUM(25) \
OP_NUM(26) \
OP_NUM(27) \
OP_NUM(28) \
OP_NUM(29) \
OP_REG(sp)
#else
#error "Unsupported architecture for fuzzing on Fuchsia"
#endif
// Produces a CFI directive for the named or numbered register.
// The value used refers to an assembler immediate operand with the same name
// as the register (see ASM_OPERAND_REG).
#define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n"
#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num)
// Produces an assembler immediate operand for the named or numbered register.
// This operand contains the offset of the register relative to the CFA.
#define ASM_OPERAND_REG(reg) \
[reg] "i"(offsetof(zx_thread_state_general_regs_t, reg) - CFAOffset),
#define ASM_OPERAND_NUM(num) \
[x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num]) - CFAOffset),
// Trampoline to bridge from the assembly below to the static C++ crash
// callback.
__attribute__((noreturn)) static void StaticCrashHandler() {
Fuzzer::StaticCrashSignalCallback();
for (;;) {
_Exit(1);
}
}
// Creates the trampoline with the necessary CFI information to unwind through
// to the crashing call stack:
// * Defining the CFA so that it points to the stack pointer at the point
// of crash.
// * Storing all registers at the point of crash in the stack and refer to them
// via CFI information (relative to the CFA).
// * Setting the return column so the unwinder knows how to continue unwinding.
// * (x86_64) making sure rsp is aligned before calling StaticCrashHandler.
// * Calling StaticCrashHandler that will trigger the unwinder.
//
// The __attribute__((used)) is necessary because the function
// is never called; it's just a container around the assembly to allow it to
// use operands for compile-time computed constants.
__attribute__((used)) void MakeTrampoline() {
__asm__(".cfi_endproc\n"
".pushsection .text.CrashTrampolineAsm\n"
".type CrashTrampolineAsm,STT_FUNC\n"
"CrashTrampolineAsm:\n"
".cfi_startproc simple\n"
".cfi_signal_frame\n"
#if defined(__x86_64__)
".cfi_return_column rip\n"
".cfi_def_cfa rsp, %c[CFAOffset]\n"
FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
"mov %%rsp, %%rbp\n"
".cfi_def_cfa_register rbp\n"
"andq $-16, %%rsp\n"
"call %c[StaticCrashHandler]\n"
"ud2\n"
#elif defined(__aarch64__)
".cfi_return_column 33\n"
".cfi_def_cfa sp, %c[CFAOffset]\n"
FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
".cfi_offset 33, %c[pc]\n"
".cfi_offset 30, %c[lr]\n"
"bl %c[StaticCrashHandler]\n"
"brk 1\n"
#else
#error "Unsupported architecture for fuzzing on Fuchsia"
#endif
".cfi_endproc\n"
".size CrashTrampolineAsm, . - CrashTrampolineAsm\n"
".popsection\n"
".cfi_startproc\n"
: // No outputs
: FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM)
#if defined(__aarch64__)
ASM_OPERAND_REG(pc)
ASM_OPERAND_REG(lr)
#endif
[StaticCrashHandler] "i" (StaticCrashHandler),
[CFAOffset] "i" (CFAOffset));
}
void CrashHandler(zx_handle_t *Event) {
// This structure is used to ensure we close handles to objects we create in
// this handler.
struct ScopedHandle {
~ScopedHandle() {
_zx_handle_close(Handle);
}
zx_handle_t Handle = ZX_HANDLE_INVALID;
};
// Create the exception channel. We need to claim to be a "debugger" so the
// kernel will allow us to modify and resume dying threads (see below). Once
// the channel is set, we can signal the main thread to continue and wait
// for the exception to arrive.
ScopedHandle Channel;
zx_handle_t Self = _zx_process_self();
ExitOnErr(_zx_task_create_exception_channel(
Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle),
"_zx_task_create_exception_channel");
ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0),
"_zx_object_signal");
// This thread lives as long as the process in order to keep handling
// crashes. In practice, the first crashed thread to reach the end of the
// StaticCrashHandler will end the process.
while (true) {
ExitOnErr(_zx_object_wait_one(Channel.Handle, ZX_CHANNEL_READABLE,
ZX_TIME_INFINITE, nullptr),
"_zx_object_wait_one");
zx_exception_info_t ExceptionInfo;
ScopedHandle Exception;
ExitOnErr(
_zx_channel_read(Channel.Handle, 0, &ExceptionInfo, &Exception.Handle,
sizeof(ExceptionInfo), 1, nullptr, nullptr),
"_zx_channel_read");
// Ignore informational synthetic exceptions.
if (ZX_EXCP_THREAD_STARTING == ExceptionInfo.type ||
ZX_EXCP_THREAD_EXITING == ExceptionInfo.type ||
ZX_EXCP_PROCESS_STARTING == ExceptionInfo.type) {
continue;
}
// At this point, we want to get the state of the crashing thread, but
// libFuzzer and the sanitizers assume this will happen from that same
// thread via a POSIX signal handler. "Resurrecting" the thread in the
// middle of the appropriate callback is as simple as forcibly setting the
// instruction pointer/program counter, provided we NEVER EVER return from
// that function (since otherwise our stack will not be valid).
ScopedHandle Thread;
ExitOnErr(_zx_exception_get_thread(Exception.Handle, &Thread.Handle),
"_zx_exception_get_thread");
zx_thread_state_general_regs_t GeneralRegisters;
ExitOnErr(
_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
&GeneralRegisters, sizeof(GeneralRegisters)),
"_zx_thread_read_state");
// To unwind properly, we need to push the crashing thread's register state
// onto the stack and jump into a trampoline with CFI instructions on how
// to restore it.
#if defined(__x86_64__)
uintptr_t StackPtr = GeneralRegisters.rsp - CFAOffset;
__unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
sizeof(GeneralRegisters));
GeneralRegisters.rsp = StackPtr;
GeneralRegisters.rip = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
#elif defined(__aarch64__)
uintptr_t StackPtr = GeneralRegisters.sp - CFAOffset;
__unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
sizeof(GeneralRegisters));
GeneralRegisters.sp = StackPtr;
GeneralRegisters.pc = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
#else
#error "Unsupported architecture for fuzzing on Fuchsia"
#endif
// Now force the crashing thread's state.
ExitOnErr(
_zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
&GeneralRegisters, sizeof(GeneralRegisters)),
"_zx_thread_write_state");
// Set the exception to HANDLED so it resumes the thread on close.
uint32_t ExceptionState = ZX_EXCEPTION_STATE_HANDLED;
ExitOnErr(_zx_object_set_property(Exception.Handle, ZX_PROP_EXCEPTION_STATE,
&ExceptionState, sizeof(ExceptionState)),
"zx_object_set_property");
}
}
} // namespace
// Platform specific functions.
void SetSignalHandler(const FuzzingOptions &Options) {
// Make sure information from libFuzzer and the sanitizers are easy to
// reassemble. `__sanitizer_log_write` has the added benefit of ensuring the
// DSO map is always available for the symbolizer.
// A uint64_t fits in 20 chars, so 64 is plenty.
char Buf[64];
memset(Buf, 0, sizeof(Buf));
snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid());
if (EF->__sanitizer_log_write) __sanitizer_log_write(Buf, sizeof(Buf));
Printf("%s", Buf);
// Set up alarm handler if needed.
if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) {
std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1);
T.detach();
}
// Set up interrupt handler if needed.
if (Options.HandleInt || Options.HandleTerm) {
std::thread T(InterruptHandler);
T.detach();
}
// Early exit if no crash handler needed.
if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll &&
!Options.HandleFpe && !Options.HandleAbrt)
return;
// Set up the crash handler and wait until it is ready before proceeding.
zx_handle_t Event;
ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create");
std::thread T(CrashHandler, &Event);
zx_status_t Status =
_zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr);
_zx_handle_close(Event);
ExitOnErr(Status, "_zx_object_wait_one");
T.detach();
}
void SleepSeconds(int Seconds) {
_zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds)));
}
unsigned long GetPid() {
zx_status_t rc;
zx_info_handle_basic_t Info;
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
sizeof(Info), NULL, NULL)) != ZX_OK) {
Printf("libFuzzer: unable to get info about self: %s\n",
_zx_status_get_string(rc));
exit(1);
}
return Info.koid;
}
size_t GetPeakRSSMb() {
zx_status_t rc;
zx_info_task_stats_t Info;
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info,
sizeof(Info), NULL, NULL)) != ZX_OK) {
Printf("libFuzzer: unable to get info about self: %s\n",
_zx_status_get_string(rc));
exit(1);
}
return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20;
}
template <typename Fn>
class RunOnDestruction {
public:
explicit RunOnDestruction(Fn fn) : fn_(fn) {
}
~RunOnDestruction() {
fn_();
}
private:
Fn fn_;
};
template <typename Fn>
RunOnDestruction<Fn> at_scope_exit(Fn fn) {
return RunOnDestruction<Fn>(fn);
}
static fdio_spawn_action_t clone_fd_action(int localFd, int targetFd) {
return {
.action = FDIO_SPAWN_ACTION_CLONE_FD,
.fd =
{
.local_fd = localFd,
.target_fd = targetFd,
},
};
}
int ExecuteCommand(const Command &Cmd) {
zx_status_t rc;
// Convert arguments to C array
auto Args = Cmd.getArguments();
size_t Argc = Args.size();
assert(Argc != 0);
std::unique_ptr<const char *[]> Argv(new const char *[Argc + 1]);
for (size_t i = 0; i < Argc; ++i)
Argv[i] = Args[i].c_str();
Argv[Argc] = nullptr;
// Determine output. On Fuchsia, the fuzzer is typically run as a component
// that lacks a mutable working directory. Fortunately, when this is the case
// a mutable output directory must be specified using "-artifact_prefix=...",
// so write the log file(s) there.
// However, we don't want to apply this logic for absolute paths.
int FdOut = STDOUT_FILENO;
bool discardStdout = false;
bool discardStderr = false;
if (Cmd.hasOutputFile()) {
std::string Path = Cmd.getOutputFile();
if (Path == getDevNull()) {
// On Fuchsia, there's no "/dev/null" like-file, so we
// just don't copy the FDs into the spawned process.
discardStdout = true;
} else {
bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/';
if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix"))
Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path;
FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
if (FdOut == -1) {
Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(),
strerror(errno));
return ZX_ERR_IO;
}
}
}
auto CloseFdOut = at_scope_exit([FdOut]() {
if (FdOut != STDOUT_FILENO) close(FdOut);
});
// Determine stderr
int FdErr = STDERR_FILENO;
if (Cmd.isOutAndErrCombined()) {
FdErr = FdOut;
if (discardStdout) discardStderr = true;
}
// Clone the file descriptors into the new process
std::vector<fdio_spawn_action_t> SpawnActions;
SpawnActions.push_back(clone_fd_action(STDIN_FILENO, STDIN_FILENO));
if (!discardStdout)
SpawnActions.push_back(clone_fd_action(FdOut, STDOUT_FILENO));
if (!discardStderr)
SpawnActions.push_back(clone_fd_action(FdErr, STDERR_FILENO));
// Start the process.
char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
zx_handle_t ProcessHandle = ZX_HANDLE_INVALID;
rc = fdio_spawn_etc(ZX_HANDLE_INVALID,
FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO), Argv[0],
Argv.get(), nullptr, SpawnActions.size(),
SpawnActions.data(), &ProcessHandle, ErrorMsg);
if (rc != ZX_OK) {
Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg,
_zx_status_get_string(rc));
return rc;
}
auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); });
// Now join the process and return the exit status.
if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,
ZX_TIME_INFINITE, nullptr)) != ZX_OK) {
Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],
_zx_status_get_string(rc));
return rc;
}
zx_info_process_t Info;
if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info,
sizeof(Info), nullptr, nullptr)) != ZX_OK) {
Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0],
_zx_status_get_string(rc));
return rc;
}
return Info.return_code;
}
bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) {
auto LogFilePath = TempPath("SimPopenOut", ".txt");
Command Cmd(BaseCmd);
Cmd.setOutputFile(LogFilePath);
int Ret = ExecuteCommand(Cmd);
*CmdOutput = FileToString(LogFilePath);
RemoveFile(LogFilePath);
return Ret == 0;
}
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
size_t PattLen) {
return memmem(Data, DataLen, Patt, PattLen);
}
// In fuchsia, accessing /dev/null is not supported. There's nothing
// similar to a file that discards everything that is written to it.
// The way of doing something similar in fuchsia is by using
// fdio_null_create and binding that to a file descriptor.
void DiscardOutput(int Fd) {
fdio_t *fdio_null = fdio_null_create();
if (fdio_null == nullptr) return;
int nullfd = fdio_bind_to_fd(fdio_null, -1, 0);
if (nullfd < 0) return;
dup2(nullfd, Fd);
}
} // namespace fuzzer
#endif // LIBFUZZER_FUCHSIA

View File

@ -0,0 +1,43 @@
//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils for Linux.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \
LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN
#include "FuzzerCommand.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
namespace fuzzer {
int ExecuteCommand(const Command &Cmd) {
std::string CmdLine = Cmd.toString();
int exit_code = system(CmdLine.c_str());
if (WIFEXITED(exit_code)) return WEXITSTATUS(exit_code);
return exit_code;
}
void DiscardOutput(int Fd) {
FILE *Temp = fopen("/dev/null", "w");
if (!Temp) return;
dup2(fileno(Temp), Fd);
fclose(Temp);
}
} // namespace fuzzer
#endif

View File

@ -0,0 +1,239 @@
//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils implementation using Posix API.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_POSIX
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include "FuzzerTracePC.h"
#include <cassert>
#include <chrono>
#include <cstring>
#include <errno.h>
#include <iomanip>
#include <signal.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <thread>
#include <unistd.h>
namespace fuzzer {
static void AlarmHandler(int, siginfo_t *, void *) {
Fuzzer::StaticAlarmCallback();
}
static void (*upstream_segv_handler)(int, siginfo_t *, void *);
static void SegvHandler(int sig, siginfo_t *si, void *ucontext) {
assert(si->si_signo == SIGSEGV);
if (upstream_segv_handler) return upstream_segv_handler(sig, si, ucontext);
Fuzzer::StaticCrashSignalCallback();
}
static void CrashHandler(int, siginfo_t *, void *) {
Fuzzer::StaticCrashSignalCallback();
}
static void InterruptHandler(int, siginfo_t *, void *) {
Fuzzer::StaticInterruptCallback();
}
static void GracefulExitHandler(int, siginfo_t *, void *) {
Fuzzer::StaticGracefulExitCallback();
}
static void FileSizeExceedHandler(int, siginfo_t *, void *) {
Fuzzer::StaticFileSizeExceedCallback();
}
static void SetSigaction(int signum,
void (*callback)(int, siginfo_t *, void *)) {
struct sigaction sigact = {};
if (sigaction(signum, nullptr, &sigact)) {
Printf("libFuzzer: sigaction failed with %d\n", errno);
exit(1);
}
if (sigact.sa_flags & SA_SIGINFO) {
if (sigact.sa_sigaction) {
if (signum != SIGSEGV) return;
upstream_segv_handler = sigact.sa_sigaction;
}
} else {
if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN &&
sigact.sa_handler != SIG_ERR)
return;
}
sigact = {};
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = callback;
if (sigaction(signum, &sigact, 0)) {
Printf("libFuzzer: sigaction failed with %d\n", errno);
exit(1);
}
}
// Return true on success, false otherwise.
bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) {
FILE *Pipe = popen(Cmd.toString().c_str(), "r");
if (!Pipe) return false;
if (CmdOutput) {
char TmpBuffer[128];
while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe))
CmdOutput->append(TmpBuffer);
}
return pclose(Pipe) == 0;
}
void SetTimer(int Seconds) {
struct itimerval T {
{Seconds, 0}, {
Seconds, 0
}
};
if (setitimer(ITIMER_REAL, &T, nullptr)) {
Printf("libFuzzer: setitimer failed with %d\n", errno);
exit(1);
}
SetSigaction(SIGALRM, AlarmHandler);
}
void SetSignalHandler(const FuzzingOptions &Options) {
// setitimer is not implemented in emscripten.
if (Options.HandleAlrm && Options.UnitTimeoutSec > 0 && !LIBFUZZER_EMSCRIPTEN)
SetTimer(Options.UnitTimeoutSec / 2 + 1);
if (Options.HandleInt) SetSigaction(SIGINT, InterruptHandler);
if (Options.HandleTerm) SetSigaction(SIGTERM, InterruptHandler);
if (Options.HandleSegv) SetSigaction(SIGSEGV, SegvHandler);
if (Options.HandleBus) SetSigaction(SIGBUS, CrashHandler);
if (Options.HandleAbrt) SetSigaction(SIGABRT, CrashHandler);
if (Options.HandleIll) SetSigaction(SIGILL, CrashHandler);
if (Options.HandleFpe) SetSigaction(SIGFPE, CrashHandler);
if (Options.HandleXfsz) SetSigaction(SIGXFSZ, FileSizeExceedHandler);
if (Options.HandleUsr1) SetSigaction(SIGUSR1, GracefulExitHandler);
if (Options.HandleUsr2) SetSigaction(SIGUSR2, GracefulExitHandler);
}
void SleepSeconds(int Seconds) {
sleep(Seconds); // Use C API to avoid coverage from instrumented libc++.
}
unsigned long GetPid() {
return (unsigned long)getpid();
}
size_t GetPeakRSSMb() {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage)) return 0;
if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD ||
LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) {
// ru_maxrss is in KiB
return usage.ru_maxrss >> 10;
} else if (LIBFUZZER_APPLE) {
// ru_maxrss is in bytes
return usage.ru_maxrss >> 20;
}
assert(0 && "GetPeakRSSMb() is not implemented for your platform");
return 0;
}
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
return popen(Command, Mode);
}
int CloseProcessPipe(FILE *F) {
return pclose(F);
}
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
size_t PattLen) {
return memmem(Data, DataLen, Patt, PattLen);
}
std::string DisassembleCmd(const std::string &FileName) {
return "objdump -d " + FileName;
}
std::string SearchRegexCmd(const std::string &Regex) {
return "grep '" + Regex + "'";
}
} // namespace fuzzer
#endif // LIBFUZZER_POSIX

View File

@ -0,0 +1,279 @@
//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils implementation for Windows.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_WINDOWS
#include "FuzzerCommand.h"
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include <cassert>
#include <chrono>
#include <cstring>
#include <errno.h>
#include <io.h>
#include <iomanip>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <windows.h>
// This must be included after windows.h.
#include <psapi.h>
namespace fuzzer {
static const FuzzingOptions *HandlerOpt = nullptr;
static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_STACK_OVERFLOW:
if (HandlerOpt->HandleSegv) Fuzzer::StaticCrashSignalCallback();
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_IN_PAGE_ERROR:
if (HandlerOpt->HandleBus) Fuzzer::StaticCrashSignalCallback();
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_PRIV_INSTRUCTION:
if (HandlerOpt->HandleIll) Fuzzer::StaticCrashSignalCallback();
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_STACK_CHECK:
case EXCEPTION_FLT_UNDERFLOW:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_OVERFLOW:
if (HandlerOpt->HandleFpe) Fuzzer::StaticCrashSignalCallback();
break;
// TODO: handle (Options.HandleXfsz)
}
return EXCEPTION_CONTINUE_SEARCH;
}
BOOL WINAPI CtrlHandler(DWORD dwCtrlType) {
switch (dwCtrlType) {
case CTRL_C_EVENT:
if (HandlerOpt->HandleInt) Fuzzer::StaticInterruptCallback();
return TRUE;
case CTRL_BREAK_EVENT:
if (HandlerOpt->HandleTerm) Fuzzer::StaticInterruptCallback();
return TRUE;
}
return FALSE;
}
void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
Fuzzer::StaticAlarmCallback();
}
class TimerQ {
HANDLE TimerQueue;
public:
TimerQ() : TimerQueue(NULL) {
}
~TimerQ() {
if (TimerQueue) DeleteTimerQueueEx(TimerQueue, NULL);
}
void SetTimer(int Seconds) {
if (!TimerQueue) {
TimerQueue = CreateTimerQueue();
if (!TimerQueue) {
Printf("libFuzzer: CreateTimerQueue failed.\n");
exit(1);
}
}
HANDLE Timer;
if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL,
Seconds * 1000, Seconds * 1000, 0)) {
Printf("libFuzzer: CreateTimerQueueTimer failed.\n");
exit(1);
}
}
};
static TimerQ Timer;
static void CrashHandler(int) {
Fuzzer::StaticCrashSignalCallback();
}
void SetSignalHandler(const FuzzingOptions &Options) {
HandlerOpt = &Options;
if (Options.HandleAlrm && Options.UnitTimeoutSec > 0)
Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1);
if (Options.HandleInt || Options.HandleTerm)
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
DWORD LastError = GetLastError();
Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n",
LastError);
exit(1);
}
if (Options.HandleSegv || Options.HandleBus || Options.HandleIll ||
Options.HandleFpe)
SetUnhandledExceptionFilter(ExceptionHandler);
if (Options.HandleAbrt)
if (SIG_ERR == signal(SIGABRT, CrashHandler)) {
Printf("libFuzzer: signal failed with %d\n", errno);
exit(1);
}
}
void SleepSeconds(int Seconds) {
Sleep(Seconds * 1000);
}
unsigned long GetPid() {
return GetCurrentProcessId();
}
size_t GetPeakRSSMb() {
PROCESS_MEMORY_COUNTERS info;
if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info))) return 0;
return info.PeakWorkingSetSize >> 20;
}
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
return _popen(Command, Mode);
}
int CloseProcessPipe(FILE *F) {
return _pclose(F);
}
int ExecuteCommand(const Command &Cmd) {
std::string CmdLine = Cmd.toString();
return system(CmdLine.c_str());
}
bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) {
FILE *Pipe = _popen(Cmd.toString().c_str(), "r");
if (!Pipe) return false;
if (CmdOutput) {
char TmpBuffer[128];
while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe))
CmdOutput->append(TmpBuffer);
}
return _pclose(Pipe) == 0;
}
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
size_t PattLen) {
// TODO: make this implementation more efficient.
const char *Cdata = (const char *)Data;
const char *Cpatt = (const char *)Patt;
if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
return NULL;
if (PattLen == 1) return memchr(Data, *Cpatt, DataLen);
const char *End = Cdata + DataLen - PattLen + 1;
for (const char *It = Cdata; It < End; ++It)
if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0) return It;
return NULL;
}
std::string DisassembleCmd(const std::string &FileName) {
Vector<std::string> command_vector;
command_vector.push_back("dumpbin /summary > nul");
if (ExecuteCommand(Command(command_vector)) == 0)
return "dumpbin /disasm " + FileName;
Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
exit(1);
}
std::string SearchRegexCmd(const std::string &Regex) {
return "findstr /r \"" + Regex + "\"";
}
void DiscardOutput(int Fd) {
FILE *Temp = fopen("nul", "w");
if (!Temp) return;
_dup2(_fileno(Temp), Fd);
fclose(Temp);
}
} // namespace fuzzer
#endif // LIBFUZZER_WINDOWS

View File

@ -0,0 +1,73 @@
//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// ValueBitMap.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H
#define LLVM_FUZZER_VALUE_BIT_MAP_H
#include "FuzzerPlatform.h"
#include <cstdint>
namespace fuzzer {
// A bit map containing kMapSizeInWords bits.
struct ValueBitMap {
static const size_t kMapSizeInBits = 1 << 16;
static const size_t kMapPrimeMod = 65371; // Largest Prime < kMapSizeInBits;
static const size_t kBitsInWord = (sizeof(uintptr_t) * 8);
static const size_t kMapSizeInWords = kMapSizeInBits / kBitsInWord;
public:
// Clears all bits.
void Reset() { memset(Map, 0, sizeof(Map)); }
// Computes a hash function of Value and sets the corresponding bit.
// Returns true if the bit was changed from 0 to 1.
ATTRIBUTE_NO_SANITIZE_ALL
inline bool AddValue(uintptr_t Value) {
uintptr_t Idx = Value % kMapSizeInBits;
uintptr_t WordIdx = Idx / kBitsInWord;
uintptr_t BitIdx = Idx % kBitsInWord;
uintptr_t Old = Map[WordIdx];
uintptr_t New = Old | (1ULL << BitIdx);
Map[WordIdx] = New;
return New != Old;
}
ATTRIBUTE_NO_SANITIZE_ALL
inline bool AddValueModPrime(uintptr_t Value) {
return AddValue(Value % kMapPrimeMod);
}
inline bool Get(uintptr_t Idx) {
assert(Idx < kMapSizeInBits);
uintptr_t WordIdx = Idx / kBitsInWord;
uintptr_t BitIdx = Idx % kBitsInWord;
return Map[WordIdx] & (1ULL << BitIdx);
}
size_t SizeInBits() const { return kMapSizeInBits; }
template <class Callback>
ATTRIBUTE_NO_SANITIZE_ALL
void ForEach(Callback CB) const {
for (size_t i = 0; i < kMapSizeInWords; i++)
if (uintptr_t M = Map[i])
for (size_t j = 0; j < sizeof(M) * 8; j++)
if (M & ((uintptr_t)1 << j))
CB(i * sizeof(M) * 8 + j);
}
private:
ATTRIBUTE_ALIGNED(512) uintptr_t Map[kMapSizeInWords];
};
} // namespace fuzzer
#endif // LLVM_FUZZER_VALUE_BIT_MAP_H

View File

@ -0,0 +1,86 @@
CFLAGS = -g -O3 -funroll-loops -fPIC -fpermissive -std=c++11
#CFLAGS = -g -O0 -fPIC -fpermissive -std=c++11
CXX ?= clang++
ifdef INTROSPECTION
$(info Compiling with introspection documentation)
CFLAGS += -DINTROSPECTION=1
endif
all: libfuzzer-mutator.so
FuzzerCrossOver.o: FuzzerCrossOver.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerDataFlowTrace.o: FuzzerDataFlowTrace.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerDriver.o: FuzzerDriver.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerExtFunctionsDlsym.o: FuzzerExtFunctionsDlsym.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerExtFunctionsWeak.o: FuzzerExtFunctionsWeak.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerExtFunctionsWindows.o: FuzzerExtFunctionsWindows.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerExtraCounters.o: FuzzerExtraCounters.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerFork.o: FuzzerFork.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerIO.o: FuzzerIO.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerIOPosix.o: FuzzerIOPosix.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerIOWindows.o: FuzzerIOWindows.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerLoop.o: FuzzerLoop.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerMerge.o: FuzzerMerge.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerMutate.o: FuzzerMutate.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerSHA1.o: FuzzerSHA1.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerTracePC.o: FuzzerTracePC.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerUtil.o: FuzzerUtil.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerUtilDarwin.o: FuzzerUtilDarwin.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerUtilFuchsia.o: FuzzerUtilFuchsia.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerUtilLinux.o: FuzzerUtilLinux.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerUtilPosix.o: FuzzerUtilPosix.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
FuzzerUtilWindows.o: FuzzerUtilWindows.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
libfuzzer.o: libfuzzer.cpp
$(CXX) $(CFLAGS) -I../../include -I. -c $^
libfuzzer-mutator.so: FuzzerCrossOver.o FuzzerDataFlowTrace.o FuzzerDriver.o FuzzerExtFunctionsDlsym.o FuzzerExtFunctionsWeak.o FuzzerExtFunctionsWindows.o FuzzerExtraCounters.o FuzzerFork.o FuzzerIO.o FuzzerIOPosix.o FuzzerIOWindows.o FuzzerLoop.o FuzzerMerge.o FuzzerMutate.o FuzzerSHA1.o FuzzerTracePC.o FuzzerUtil.o FuzzerUtilDarwin.o FuzzerUtilFuchsia.o FuzzerUtilLinux.o FuzzerUtilPosix.o FuzzerUtilWindows.o libfuzzer.o
$(CXX) $(CFLAGS) -I../../include -I. -shared -o libfuzzer-mutator.so *.o
clean:
rm -f *.o *~ *.so core

View File

@ -0,0 +1,24 @@
# custum mutator: libfuzzer LLVMFuzzerMutate()
This uses the libfuzzer LLVMFuzzerMutate() function in llvm 12.
just type `make` to build
```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/libfuzzer/libfuzzer-mutator.so afl-fuzz ...```
Note that this is currently a simple implementation and it is missing two features:
* Splicing ("Crossover")
* Dictionary support
To update the source, all that is needed is that FuzzerDriver.cpp has to receive
```
#include "libfuzzer.inc"
```
before the closing namespace bracket.
It is also libfuzzer.inc where the configuration of the libfuzzer mutations
are done.
> Original repository: https://github.com/llvm/llvm-project
> Path: compiler-rt/lib/fuzzer/*.{h|cpp}
> Source commit: df3e903655e2499968fc7af64fb5fa52b2ee79bb

View File

@ -0,0 +1,160 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
//#include "config.h"
//#include "debug.h"
#include "afl-fuzz.h"
#ifdef INTROSPECTION
const char *introspection_ptr;
#endif
afl_state_t *afl_struct;
extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
extern "C" int LLVMFuzzerRunDriver(int *argc, char ***argv,
int (*UserCb)(const uint8_t *Data,
size_t Size));
extern "C" void LLVMFuzzerMyInit(int (*UserCb)(const uint8_t *Data,
size_t Size),
unsigned int Seed);
typedef struct my_mutator {
afl_state_t *afl;
u8 * mutator_buf;
unsigned int seed;
unsigned int extras_cnt, a_extras_cnt;
} my_mutator_t;
extern "C" int dummy(const uint8_t *Data, size_t Size) {
(void)(Data);
(void)(Size);
fprintf(stderr, "dummy() called\n");
return 0;
}
extern "C" my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
my_mutator_t *data = (my_mutator_t *)calloc(1, sizeof(my_mutator_t));
if (!data) {
perror("afl_custom_init alloc");
return NULL;
}
if ((data->mutator_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
free(data);
perror("mutator_buf alloc");
return NULL;
}
data->afl = afl;
data->seed = seed;
afl_struct = afl;
/*
char **argv;
argv = (char**)malloc(sizeof(size_t) * 2);
argv[0] = (char*)"foo";
argv[1] = NULL;
int eins = 1;
LLVMFuzzerRunDriver(&eins, &argv, dummy);
*/
LLVMFuzzerMyInit(dummy, seed);
return data;
}
/* When a new queue entry is added we check if there are new dictionary
entries to add to honggfuzz structure */
#if 0
extern "C" void afl_custom_queue_new_entry(my_mutator_t * data,
const uint8_t *filename_new_queue,
const uint8_t *filename_orig_queue) {
while (data->extras_cnt < afl_struct->extras_cnt) {
/*
memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val,
afl_struct->extras[data->extras_cnt].data,
afl_struct->extras[data->extras_cnt].len);
run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len =
afl_struct->extras[data->extras_cnt].len;
run.global->mutate.dictionaryCnt++;
*/
data->extras_cnt++;
}
while (data->a_extras_cnt < afl_struct->a_extras_cnt) {
/*
memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val,
afl_struct->a_extras[data->a_extras_cnt].data,
afl_struct->a_extras[data->a_extras_cnt].len);
run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len =
afl_struct->a_extras[data->a_extras_cnt].len;
run.global->mutate.dictionaryCnt++;
data->a_extras_cnt++;
*/
}
}
#endif
/* 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 */
extern "C" 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) {
memcpy(data->mutator_buf, buf, buf_size);
size_t ret = LLVMFuzzerMutate(data->mutator_buf, buf_size, max_size);
/* return size of mutated data */
*out_buf = data->mutator_buf;
return ret;
}
#ifdef INTROSPECTION
extern "C" const char* afl_custom_introspection(my_mutator_t *data) {
return introspection_ptr;
}
#endif
/**
* Deinitialize everything
*
* @param data The data ptr from afl_custom_init
*/
extern "C" void afl_custom_deinit(my_mutator_t *data) {
free(data->mutator_buf);
free(data);
}

View File

@ -0,0 +1,36 @@
extern "C" ATTRIBUTE_INTERFACE void
LLVMFuzzerMyInit(int (*Callback)(const uint8_t *Data, size_t Size), unsigned int Seed) {
Random Rand(Seed);
FuzzingOptions Options;
Options.Verbosity = 3;
Options.MaxLen = 1024000;
Options.LenControl = true;
Options.DoCrossOver = false;
Options.MutateDepth = 6;
Options.UseCounters = false;
Options.UseMemmem = false;
Options.UseCmp = false;
Options.UseValueProfile = false;
Options.Shrink = false;
Options.ReduceInputs = false;
Options.PreferSmall = false;
Options.ReloadIntervalSec = 0;
Options.OnlyASCII = false;
Options.DetectLeaks = false;
Options.PurgeAllocatorIntervalSec = 0;
Options.TraceMalloc = false;
Options.RssLimitMb = 100;
Options.MallocLimitMb = 100;
Options.MaxNumberOfRuns = 0;
Options.ReportSlowUnits = false;
Options.Entropic = false;
struct EntropicOptions Entropic;
Entropic.Enabled = Options.Entropic;
EF = new ExternalFunctions();
auto *MD = new MutationDispatcher(Rand, Options);
auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic);
auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
}

View File

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

View File

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

View File

@ -33,6 +33,7 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) {
free(data);
perror("mutator_buf alloc");
return NULL;

View File

@ -0,0 +1,14 @@
ifdef DEBUG
CFLAGS += -DDEBUG
endif
all: symcc-mutator.so
CFLAGS += -O3 -funroll-loops
symcc-mutator.so: symcc.c
$(CC) $(CFLAGS) $(CPPFLAGS) -g -I../../include -shared -fPIC -o symcc-mutator.so symcc.c
clean:
rm -f symcc-mutator.so *.o *~ core

View File

@ -0,0 +1,15 @@
# custum mutator: symcc
This uses the excellent symcc to find new paths into the target.
To use this custom mutator follow the steps in the symcc repository
[https://github.com/eurecom-s3/symcc/](https://github.com/eurecom-s3/symcc/)
on how to build symcc and how to instrument a target binary (the same target
that you are fuzzing).
The target program compiled with symcc has to be pointed to with the
`SYMCC_TARGET` environment variable.
just type `make` to build this custom mutator.
```SYMCC_TARGET=/prg/to/symcc/compiled/target AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/symcc/symcc-mutator.so afl-fuzz ...```

View File

@ -0,0 +1,332 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "config.h"
#include "debug.h"
#include "afl-fuzz.h"
#include "common.h"
afl_state_t *afl_struct;
#ifdef DEBUG
#define DBG(x...) fprintf(stderr, x)
#else
#define DBG(x...) \
{}
#endif
typedef struct my_mutator {
afl_state_t *afl;
u8 * mutator_buf;
u8 * out_dir;
u8 * tmp_dir;
u8 * target;
uint32_t seed;
} my_mutator_t;
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
if (getenv("AFL_CUSTOM_MUTATOR_ONLY"))
FATAL("the symcc module cannot be used with AFL_CUSTOM_MUTATOR_ONLY.");
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) {
free(data);
perror("mutator_buf alloc");
return NULL;
}
if (!(data->target = getenv("SYMCC_TARGET")))
FATAL(
"SYMCC_TARGET not defined, this should point to the full path of the "
"symcc compiled binary.");
if (!(data->out_dir = getenv("SYMCC_OUTPUT_DIR"))) {
data->out_dir = alloc_printf("%s/symcc", afl->out_dir);
}
data->tmp_dir = alloc_printf("%s/tmp", data->out_dir);
setenv("SYMCC_OUTPUT_DIR", data->tmp_dir, 1);
int pid = fork();
if (pid == -1) return NULL;
if (pid) pid = waitpid(pid, NULL, 0);
if (pid == 0) {
char *args[4];
args[0] = "/bin/rm";
args[1] = "-rf";
args[2] = data->out_dir;
args[3] = NULL;
execvp(args[0], args);
DBG("exec:FAIL\n");
exit(-1);
}
data->afl = afl;
data->seed = seed;
afl_struct = afl;
if (mkdir(data->out_dir, 0755))
PFATAL("Could not create directory %s", data->out_dir);
if (mkdir(data->tmp_dir, 0755))
PFATAL("Could not create directory %s", data->tmp_dir);
DBG("out_dir=%s, target=%s\n", data->out_dir, data->target);
return data;
}
/* When a new queue entry is added we run this input with the symcc
instrumented binary */
void afl_custom_queue_new_entry(my_mutator_t * data,
const uint8_t *filename_new_queue,
const uint8_t *filename_orig_queue) {
int pipefd[2];
struct stat st;
ACTF("Queueing to symcc: %s", filename_new_queue);
u8 *fn = alloc_printf("%s", filename_new_queue);
if (!(stat(fn, &st) == 0 && S_ISREG(st.st_mode) && st.st_size)) {
ck_free(fn);
PFATAL("Couldn't find enqueued file: %s", fn);
}
if (afl_struct->fsrv.use_stdin) {
if (pipe(pipefd) == -1) {
ck_free(fn);
PFATAL("Couldn't create a pipe for interacting with symcc child process");
}
}
int pid = fork();
if (pid == -1) return;
if (pid) {
if (afl_struct->fsrv.use_stdin) {
close(pipefd[0]);
int fd = open(fn, O_RDONLY);
if (fd >= 0) {
ssize_t r = read(fd, data->mutator_buf, MAX_FILE);
DBG("fn=%s, fd=%d, size=%ld\n", fn, fd, r);
ck_free(fn);
close(fd);
if (r <= 0) {
close(pipefd[1]);
return;
}
if (r > fcntl(pipefd[1], F_GETPIPE_SZ))
fcntl(pipefd[1], F_SETPIPE_SZ, MAX_FILE);
ck_write(pipefd[1], data->mutator_buf, r, filename_new_queue);
} else {
ck_free(fn);
close(pipefd[1]);
PFATAL(
"Something happened to the enqueued file before sending its "
"contents to symcc binary");
}
close(pipefd[1]);
}
pid = waitpid(pid, NULL, 0);
// At this point we need to transfer files to output dir, since their names
// collide and symcc will just overwrite them
struct dirent **nl;
int32_t items = scandir(data->tmp_dir, &nl, NULL, NULL);
u8 * origin_name = basename(filename_new_queue);
int32_t i;
if (items > 0) {
for (i = 0; i < (u32)items; ++i) {
struct stat st;
u8 *source_name = alloc_printf("%s/%s", data->tmp_dir, nl[i]->d_name);
DBG("test=%s\n", fn);
if (stat(source_name, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
u8 *destination_name =
alloc_printf("%s/%s.%s", data->out_dir, origin_name, nl[i]->d_name);
rename(source_name, destination_name);
ck_free(destination_name);
DBG("found=%s\n", source_name);
}
ck_free(source_name);
free(nl[i]);
}
free(nl);
}
}
if (pid == 0) {
if (afl_struct->fsrv.use_stdin) {
unsetenv("SYMCC_INPUT_FILE");
close(pipefd[1]);
dup2(pipefd[0], 0);
} else {
setenv("SYMCC_INPUT_FILE", afl_struct->fsrv.out_file, 1);
}
DBG("exec=%s\n", data->target);
close(1);
close(2);
dup2(afl_struct->fsrv.dev_null_fd, 1);
dup2(afl_struct->fsrv.dev_null_fd, 2);
execvp(data->target, afl_struct->argv);
DBG("exec=FAIL\n");
exit(-1);
}
}
uint32_t afl_custom_fuzz_count(my_mutator_t *data, const u8 *buf,
size_t buf_size) {
uint32_t count = 0, i;
struct dirent **nl;
int32_t items = scandir(data->out_dir, &nl, NULL, NULL);
if (items > 0) {
for (i = 0; i < (u32)items; ++i) {
struct stat st;
u8 * fn = alloc_printf("%s/%s", data->out_dir, nl[i]->d_name);
DBG("test=%s\n", fn);
if (stat(fn, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
DBG("found=%s\n", fn);
count++;
}
ck_free(fn);
free(nl[i]);
}
free(nl);
}
DBG("dir=%s, count=%u\n", data->out_dir, count);
return count;
}
/* here we actually just read the files generated from symcc */
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) {
struct dirent **nl;
int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL);
ssize_t size = 0;
if (items <= 0) return 0;
for (i = 0; i < (u32)items; ++i) {
struct stat st;
u8 * fn = alloc_printf("%s/%s", data->out_dir, nl[i]->d_name);
if (done == 0) {
if (stat(fn, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
int fd = open(fn, O_RDONLY);
if (fd >= 0) {
size = read(fd, data->mutator_buf, max_size);
*out_buf = data->mutator_buf;
close(fd);
done = 1;
}
}
unlink(fn);
}
ck_free(fn);
free(nl[i]);
}
free(nl);
DBG("FUZZ size=%lu\n", size);
return (uint32_t)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);
}

View File

@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char **argv) {
if (argc < 2) {
printf("Need a file argument\n");
return 1;
}
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
printf("Couldn't open file\n");
return 1;
}
uint32_t value = 0;
read(fd, &value, sizeof(value));
close(fd);
value = value ^ 0xffffffff;
if (value == 0x11223344) printf("Value one\n");
if (value == 0x44332211) printf("Value two\n");
if (value != 0x0) printf("Not zero\n");
return 0;
}

View File

@ -0,0 +1,28 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char input_buffer[16];
uint32_t comparisonValue;
size_t bytesRead;
bytesRead = read(STDIN_FILENO, input_buffer, sizeof(input_buffer));
if (bytesRead < 0) exit(-1);
comparisonValue = *(uint32_t *)input_buffer;
comparisonValue = comparisonValue ^ 0xff112233;
if (comparisonValue == 0x66554493) {
printf("First value\n");
} else {
if (comparisonValue == 0x84444415) printf("Second value\n");
}
return 0;
}

View File

@ -1,6 +1,6 @@
# AFL dictionaries
(See [../docs/README.md](../docs/README.md) for the general instruction manual.)
(See [../README.md](../README.md) for the general instruction manual.)
This subdirectory contains a set of dictionaries that can be used in
conjunction with the -x option to allow the fuzzer to effortlessly explore the

View File

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

View File

@ -9,6 +9,145 @@ 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 ++3.00c (release)
- llvm_mode/ and gcc_plugin/ moved to instrumentation/
- examples/ renamed to utils/
- moved libdislocator, libtokencap and qdbi_mode to utils/
- all compilers combined to afl-cc which emulates the previous ones
- afl-llvm/gcc-rt.o merged into afl-compiler-rt.o
- afl-fuzz
- not specifying -M or -S will now auto-set "-S default"
- deterministic fuzzing is now disabled by default and can be enabled with
-D. It is still enabled by default for -M.
- a new seed selection was implemented that uses weighted randoms based on
a schedule performance score, which is much better that the previous
walk the whole queue approach. Select the old mode with -Z (auto enabled
with -M)
- Marcel Boehme submitted a patch that improves all AFFast schedules :)
- the default schedule is now FAST
- memory limits are now disabled by default, set them with -m if required
- rpc.statsd support, for stats and charts, by Edznux, thanks a lot!
- reading testcases from -i now descends into subdirectories
- allow the -x command line option up to 4 times
- loaded extras now have a duplication protection
- If test cases are too large we do a partial read on the maximum
supported size
- longer seeds with the same trace information will now be ignored
for fuzzing but still be used for splicing
- crashing seeds are now not prohibiting a run anymore but are
skipped - they are used for splicing, though
- update MOpt for expanded havoc modes
- setting the env var AFL_NO_AUTODICT will not load an LTO autodictionary
- added NO_SPLICING compile option and makefile define
- added INTROSPECTION make target that writes all mutations to
out/NAME/introspection.txt
- print special compile time options used in help output
- when using -c cmplog, one of the childs was not killed, fixed
- somewhere we broke -n dumb fuzzing, fixed
- added afl_custom_describe to the custom mutator API to allow for easy
mutation reproduction on crashing inputs
- instrumentation
- We received an enhanced gcc_plugin module from AdaCore, thank you
very much!!
- not overriding -Ox or -fno-unroll-loops anymore
- we now have our own trace-pc-guard implementation. It is the same as
-fsanitize-coverage=trace-pc-guard from llvm 12, but: it is a) inline
and b) works from llvm 10.0.1 + onwards :)
- new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz
-x dictionary of string comparisons found during compilation
- LTO autodict now also collects interesting cmp comparisons,
std::string compare + find + ==, bcmp
- fix crash in dict2file for integers > 64 bit
- custom mutators
- added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/
- added a new custom mutator: libfuzzer that integrates libfuzzer mutations
- Our afl++ Grammar-Mutator is now better integrated into custom_mutators/
- added INTROSPECTION support for custom modules
- python fuzz function was not optional, fixed
- some python mutator speed improvements
- afl-cmin/afl-cmin.bash now search first in PATH and last in AFL_PATH
- unicornafl synced with upstream version 1.02 (fixes, better rust bindings)
- renamed AFL_DEBUG_CHILD_OUTPUT to AFL_DEBUG_CHILD
- added AFL_CRASH_EXITCODE env variable to treat a child exitcode as crash
### 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 +487,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
View 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 [utils/socket_fuzzing/](../utils/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)

Some files were not shown because too many files have changed in this diff Show More