diff options
87 files changed, 3559 insertions, 2663 deletions
diff --git a/.travis.yml b/.travis.yml index 96cdc86c..e23a84de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ sudo: required branches: only: - - master + - stable - dev matrix: @@ -54,6 +54,6 @@ script: - 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 diff --git a/GNUmakefile b/GNUmakefile index e0d89274..3c5e10ed 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -97,8 +97,14 @@ ifneq "$(shell uname -m)" "x86_64" endif endif -CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) -override CFLAGS += -Wall -g -Wno-pointer-sign \ +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)\" @@ -198,6 +204,7 @@ else endif ifneq "$(filter Linux GNU%,$(shell uname))" "" + override CFLAGS += -D_FORTIFY_SOURCE=2 LDFLAGS += -ldl -lrt endif @@ -270,7 +277,7 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as test_build all_done man: $(MANPAGES) tests: source-only - @cd test ; ./test.sh + @cd test ; ./test-all.sh @rm -f test/errors performance-tests: performance-test @@ -304,6 +311,7 @@ help: @echo "==========================================" @echo STATIC - compile AFL++ static @echo ASAN_BUILD - compiles with memory sanitizer for debug purposes + @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror @echo PROFILING - compile afl-fuzz with profiling information @echo AFL_NO_X86 - if compiling on non-intel/amd platforms @echo "==========================================" @@ -551,9 +559,9 @@ source-only: all -$(MAKE) -C gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap - #$(MAKE) -C examples/afl_network_proxy - #$(MAKE) -C examples/socket_fuzzing - #$(MAKE) -C examples/argv_fuzzing + @#$(MAKE) -C examples/afl_network_proxy + @#$(MAKE) -C examples/socket_fuzzing + @#$(MAKE) -C examples/argv_fuzzing %.8: % @echo .TH $* 8 $(BUILD_DATE) "afl++" > $@ @@ -589,6 +597,8 @@ install: all $(MANPAGES) if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi + if [ -f libAFLDriver.a ]; then install -m 644 libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi + if [ -f libAFLQemuDriver.a ]; then install -m 644 libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++ set -e; if [ -f afl-clang-fast ] ; then ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi diff --git a/README.md b/README.md index bd2784ae..6e5d9c1f 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@  - Release Version: [2.66c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [2.67c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 2.66d + Github Version: 2.67d Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) @@ -19,7 +19,7 @@ Originally developed by Michał "lcamtuf" Zalewski. - afl++ is a superiour fork to Google's afl - more speed, more and better + afl++ is a superior fork to Google's afl - more speed, more and better mutations, more and better instrumentation, custom module support, etc. ## Contents @@ -39,19 +39,19 @@ with laf-intel and redqueen, unicorn mode, gcc plugin, full *BSD, Solaris and Android support and much, much, much more. - | Feature/Instrumentation | afl-gcc | llvm_mode | gcc_plugin | qemu_mode | unicorn_mode | - | ----------------------- |:-------:|:---------:|:----------:|:----------------:|:------------:| - | NeverZero | x | x(1) | (2) | x | x | - | Persistent mode | | x | x | x86[_64]/arm[64] | x | - | LAF-Intel / CompCov | | x | | x86[_64]/arm[64] | x86[_64]/arm | - | CmpLog | | x | | x86[_64]/arm[64] | | - | Instrument file list | | x | x | (x)(3) | | - | Non-colliding coverage | | x(4) | | (x)(5) | | - | InsTrim | | x | | | | - | Ngram prev_loc coverage | | x(6) | | | | - | Context coverage | | x | | | | - | Auto dictionary | | x(7) | | | | - | Snapshot LKM support | | x | | (x)(5) | | + | Feature/Instrumentation | afl-gcc | llvm_mode | gcc_plugin | qemu_mode | unicorn_mode | + | -------------------------|:-------:|:---------:|:----------:|:----------------:|:------------:| + | NeverZero | x86[_64]| x(1) | (2) | x | x | + | Persistent Mode | | x | x | x86[_64]/arm[64] | x | + | LAF-Intel / CompCov | | x | | x86[_64]/arm[64] | x86[_64]/arm | + | CmpLog | | x | | x86[_64]/arm[64] | | + | Selective Instrumentation| | x | x | (x)(3) | | + | Non-Colliding Coverage | | x(4) | | (x)(5) | | + | InsTrim | | x | | | | + | Ngram prev_loc Coverage | | x(6) | | | | + | Context Coverage | | x | | | | + | Auto Dictionary | | x(7) | | | | + | Snapshot LKM Support | | x | | (x)(5) | | 1. default for LLVM >= 9.0, env var for older version due an efficiency bug in llvm <= 8 2. GCC creates non-performant code, hence it is disabled in gcc_plugin @@ -64,21 +64,20 @@ Among others, the following features and patches have been integrated: * NeverZero patch for afl-gcc, llvm_mode, qemu_mode and unicorn_mode which prevents a wrapping map value to zero, increases coverage - * Persistent mode and deferred forkserver for qemu_mode + * Persistent mode, deferred forkserver and in-memory fuzzing for qemu_mode * Unicorn mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk) * The new CmpLog instrumentation for LLVM and QEMU inspired by [Redqueen](https://www.syssec.ruhr-uni-bochum.de/media/emma/veroeffentlichungen/2018/12/17/NDSS19-Redqueen.pdf) * Win32 PE binary-only fuzzing with QEMU and Wine * AFLfast's power schedules by Marcel Böhme: [https://github.com/mboehme/aflfast](https://github.com/mboehme/aflfast) * The MOpt mutator: [https://github.com/puppet-meteor/MOpt-AFL](https://github.com/puppet-meteor/MOpt-AFL) * LLVM mode Ngram coverage by Adrian Herrera [https://github.com/adrianherrera/afl-ngram-pass](https://github.com/adrianherrera/afl-ngram-pass) - * InsTrim, an effective CFG llvm_mode instrumentation implementation for large targets: [https://github.com/csienslab/instrim](https://github.com/csienslab/instrim) - * C. Holler's afl-fuzz Python mutator module and llvm_mode instrument file support: [https://github.com/choller/afl](https://github.com/choller/afl) + * InsTrim, a CFG llvm_mode instrumentation implementation: [https://github.com/csienslab/instrim](https://github.com/csienslab/instrim) + * C. Holler's afl-fuzz Python mutator module: [https://github.com/choller/afl](https://github.com/choller/afl) * Custom mutator by a library (instead of Python) by kyakdan * LAF-Intel/CompCov support for llvm_mode, qemu_mode and unicorn_mode (with enhanced capabilities) - * Radamsa and hongfuzz mutators (as custom mutators). + * Radamsa and honggfuzz mutators (as custom mutators). * QBDI mode to fuzz android native libraries via Quarkslab's [QBDI](https://github.com/QBDI/QBDI) framework - - A more thorough list is available in the [PATCHES](docs/PATCHES.md) file. + * Frida and ptrace mode to fuzz binary-only libraries, etc. So all in all this is the best-of afl that is out there :-) @@ -96,7 +95,7 @@ The following branches exist: * [stable/trunk](https://github.com/AFLplusplus/AFLplusplus/) : stable state of afl++ - it is synced from dev from time to - time when we are satisfied with it's stability + time when we are satisfied with its stability * [dev](https://github.com/AFLplusplus/AFLplusplus/tree/dev) : development state of afl++ - bleeding edge and you might catch a checkout which does not compile or has a bug. *We only accept PRs in dev!!* * (any other) : experimental branches to work on specific features or testing @@ -179,6 +178,7 @@ These build options exist: * STATIC - compile AFL++ static * ASAN_BUILD - compiles with memory sanitizer for debug purposes +* DEBUG - no optimization, -ggdb3, all warnings and -Werror * PROFILING - compile with profiling information (gprof) * NO_PYTHON - disable python support * AFL_NO_X86 - if compiling on non-intel/amd platforms @@ -195,6 +195,7 @@ Here are some good writeups to show how to effectively use AFL++: * [https://securitylab.github.com/research/fuzzing-challenges-solutions-1](https://securitylab.github.com/research/fuzzing-challenges-solutions-1) * [https://securitylab.github.com/research/fuzzing-software-2](https://securitylab.github.com/research/fuzzing-software-2) * [https://securitylab.github.com/research/fuzzing-sockets-FTP](https://securitylab.github.com/research/fuzzing-sockets-FTP) + * [https://securitylab.github.com/research/fuzzing-sockets-FreeRDP](https://securitylab.github.com/research/fuzzing-sockets-FreeRDP) If you are interested in fuzzing structured data (where you define what the structure is), these links have you covered: @@ -209,7 +210,7 @@ If you find other good ones, please send them to us :-) The following describes how to fuzz with a target if source code is available. If you have a binary-only target please skip to [#Instrumenting binary-only apps](#Instrumenting binary-only apps) -Fuzzing source code is a three step process. +Fuzzing source code is a three-step process. 1. compile the target with a special compiler that prepares the target to be fuzzed efficiently. This step is called "instrumenting a target". @@ -246,7 +247,7 @@ anything below 9 is not recommended. +--------------------------------+ | if you want to instrument only | -> use afl-gcc-fast and afl-gcc-fast++ | parts of the target | see [gcc_plugin/README.md](gcc_plugin/README.md) and - +--------------------------------+ [gcc_plugin/README.instrument_file.md](gcc_plugin/README.instrument_file.md) + +--------------------------------+ [gcc_plugin/README.instrument_list.md](gcc_plugin/README.instrument_list.md) | | if not, or if you do not have a gcc with plugin support | @@ -272,15 +273,17 @@ afl-clang-lto: To use this set the following environment variable before compiling the target: `export AFL_LLVM_LAF_ALL=1` You can read more about this in [llvm/README.laf-intel.md](llvm/README.laf-intel.md) - * A different technique (and usually a bit better than laf-intel) is to + * A different technique (and usually a better than laf-intel) is to instrument the target so that any compare values in the target are sent to - afl++ which then tries to put this value into the fuzzing data at different + afl++ which then tries to put these values into the fuzzing data at different locations. This technique is very fast and good - if the target does not transform input data before comparison. Therefore this technique is called `input to state` or `redqueen`. If you want to use this technique, then you have to compile the target twice, once specifically with/for this mode, and pass this binary to afl-fuzz via the `-c` parameter. + Not that you can compile also just a cmplog binary and use that for both + however there will a performance penality. You can read more about this in [llvm_mode/README.cmplog.md](llvm_mode/README.cmplog.md) If you use afl-clang-fast, afl-clang-lto or afl-gcc-fast you have the option to @@ -290,24 +293,33 @@ selectively only instrument parts of the target that you are interested in: create a file with all the filenames of the source code that should be instrumented. For afl-clang-lto and afl-gcc-fast - or afl-clang-fast if either the clang - version is < 7 or the CLASSIC instrumentation is used - just put one - filename per line, no directory information necessary, and set - `export AFL_LLVM_INSTRUMENT_FILE=yourfile.txt` - see [llvm_mode/README.instrument_file.md](llvm_mode/README.instrument_file.md) + version is below 7 or the CLASSIC instrumentation is used - just put one + filename or function per line (no directory information necessary for + filenames9, and either set `export AFL_LLVM_ALLOWLIST=allowlist.txt` **or** + `export AFL_LLVM_DENYLIST=denylist.txt` - depending on if you want per + default to instrument unless noted (DENYLIST) or not perform instrumentation + unless requested (ALLOWLIST). + **NOTE:** In optimization functions might be inlined and then not match! + see [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) For afl-clang-fast > 6.0 or if PCGUARD instrumentation is used then use the llvm sancov allow-list feature: [http://clang.llvm.org/docs/SanitizerCoverage.html](http://clang.llvm.org/docs/SanitizerCoverage.html) + The llvm sancov format works with the allowlist/denylist feature of afl++ + however afl++ is more flexible in the format. There are many more options and modes available however these are most of the time less effective. See: * [llvm_mode/README.ctx.md](llvm_mode/README.ctx.md) * [llvm_mode/README.ngram.md](llvm_mode/README.ngram.md) * [llvm_mode/README.instrim.md](llvm_mode/README.instrim.md) + +afl++ employs never zero counting in its bitmap. You can read more about this +here: * [llvm_mode/README.neverzero.md](llvm_mode/README.neverzero.md) #### c) Modify the target -If the target has features that makes fuzzing more difficult, e.g. -checksums, HMAC etc. then modify the source code so that this is +If the target has features that make fuzzing more difficult, e.g. +checksums, HMAC, etc. then modify the source code so that this is removed. This can even be done for productional source code be eliminating these checks within this specific defines: @@ -382,6 +394,7 @@ As you fuzz the target with mutated input, having as diverse inputs for the target as possible improves the efficiency a lot. #### a) Collect inputs + Try to gather valid inputs for the target from wherever you can. E.g. if it is the PNG picture format try to find as many png files as possible, e.g. from reported bugs, test suites, random downloads from the internet, unit test @@ -430,23 +443,24 @@ to be used in fuzzing! :-) ### 3. Fuzzing the target In this final step we fuzz the target. -There are not that many useful options to run the target - unless you want to -use many CPU cores/threads for the fuzzing, which will make the fuzzing much +There are not that many important options to run the target - unless you want +to use many CPU cores/threads for the fuzzing, which will make the fuzzing much more useful. If you just use one CPU for fuzzing, then you are fuzzing just for fun and not seriously :-) Pro tip: load the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) -before the start of afl-fuzz as this improves performance by a x2 speed increase! +before the start of afl-fuzz as this improves performance by a x2 speed increase +(less if you use a persistent mode harness)! #### a) Running afl-fuzz Before to do even a test run of afl-fuzz execute `sudo afl-system-config` (on the host if you execute afl-fuzz in a docker container). This reconfigures the system for optimal speed - which afl-fuzz checks and bails otherwise. -Set `export AFL_SKIP_CPUFREQ=1` for afl-fuzz to skip this check if you cannot run -afl-system-config with root privileges on the host for whatever reason. +Set `export AFL_SKIP_CPUFREQ=1` for afl-fuzz to skip this check if you cannot +run afl-system-config with root privileges on the host for whatever reason. If you have an input corpus from step 2 then specify this directory with the `-i` option. Otherwise create a new directory and create a file with any content @@ -468,7 +482,7 @@ of memory. By default this is 50MB for a process. If this is too little for the target (which you can usually see by afl-fuzz bailing with the message that it could not connect to the forkserver), then you can increase this with the `-m` option, the value is in MB. To disable any memory limits -(beware!) set `-m 0` - which is usually required for ASAN compiled targets. +(beware!) set `-m none` - which is usually required for ASAN compiled targets. Adding a dictionary is helpful. See the [dictionaries/](dictionaries/) if something is already included for your data format, and tell afl-fuzz to load @@ -496,8 +510,8 @@ fuzz your target. On the same machine - due to the design of how afl++ works - there is a maximum number of CPU cores/threads that are useful, use more and the overall performance -degrades instead. This value depends on the target and the limit is between 48 -and 96 cores/threads per machine. +degrades instead. This value depends on the target, and the limit is between 32 +and 64 cores/threads per machine. There should be one main fuzzer (`-M main` option) and as many secondary fuzzers (eg `-S variant1`) as you have cores that you use. @@ -529,7 +543,7 @@ Examples are: A long list can be found at [https://github.com/Microsvuln/Awesome-AFL](https://github.com/Microsvuln/Awesome-AFL) -However you can also sync afl++ with honggfuzz, libfuzzer, entropic, etc. +However you can also sync afl++ with honggfuzz, libfuzzer with -entropic, etc. Just show the main fuzzer (-M) with the `-F` option where the queue directory of a different fuzzer is, e.g. `-F /src/target/honggfuzz`. @@ -547,11 +561,25 @@ To have only the summary use the `-s` switch e.g.: `afl-whatsup -s output/` #### d) Checking the coverage of the fuzzing The `paths found` value is a bad indicator how good the coverage is. -It is better to check out the exact lines of code that have been reached - + +A better indicator - if you use default llvm instrumentation with at least +version 9 - is to use `afl-showmap` with the collect coverage option `-C` on +the output directory: +``` +$ afl-showmap -C -i out -o /dev/null -- ./target -params @@ +... +[*] Using SHARED MEMORY FUZZING feature. +[*] Target map size: 9960 +[+] Processed 7849 input files. +[+] Captured 4331 tuples (highest value 255, total values 67130596) in '/dev/nul +l'. +[+] A coverage of 4331 edges were achieved out of 9960 existing (43.48%) with 7849 input files. +``` +It is even better to check out the exact lines of code that have been reached - and which have not been found so far. An "easy" helper script for this is [https://github.com/vanhauser-thc/afl-cov](https://github.com/vanhauser-thc/afl-cov), -just follow the README of that seperate project. +just follow the README of that separate project. If you see that an important area or a feature has not been covered so far then try to find an input that is able to reach that and start a new secondary in @@ -560,6 +588,11 @@ then terminate it. The main node will pick it up and make it available to the other secondary nodes over time. Set `export AFL_NO_AFFINITY=1` if you have no free core. +Note that you in nearly all cases you can never reach full coverage. A lot of +functionality is usually behind options that were not activated or fuzz e.g. +if you fuzz a library to convert image formats and your target is the png to +tiff API then you will not touch any of the other library APIs and features. + #### e) How long to fuzz a target? This is a difficult question. @@ -568,6 +601,10 @@ then you can expect that your fuzzing won't be fruitful anymore. However often this just means that you should switch out secondaries for others, e.g. custom mutator modules, sync to very different fuzzers, etc. +Keep the queue/ directory (for future fuzzings of the same or similar targets) +and use them to seed other good fuzzers like libfuzzer with the -entropic +switch or honggfuzz. + #### f) Improve the speed! * Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase) @@ -624,7 +661,7 @@ on-the-fly instrumentation of black-box binaries. ### QEMU -For linux programs and it's libraries this is accomplished with a version of +For linux programs and its libraries this is accomplished with a version of QEMU running in the lesser-known "user space emulation" mode. QEMU is a project separate from AFL, but you can conveniently build the feature by doing: @@ -647,7 +684,7 @@ For non-Linux binaries you can use afl++'s unicorn mode which can emulate anything you want - for the price of speed and the user writing scripts. See [unicorn_mode](unicorn_mode/README.md). -It can be easily build by: +It can be easily built by: ```shell cd unicorn_mode ./build_unicorn_support.sh diff --git a/TODO.md b/TODO.md index 999cb9d3..e74fa1d5 100644 --- a/TODO.md +++ b/TODO.md @@ -2,9 +2,9 @@ ## Roadmap 2.67+ - - expand on AFL_LLVM_INSTRUMENT_FILE to also support sancov allowlist format - AFL_MAP_SIZE for qemu_mode and unicorn_mode - CPU affinity for many cores? There seems to be an issue > 96 cores + - afl-plot to support multiple plot_data ## Further down the road diff --git a/custom_mutators/honggfuzz/Makefile b/custom_mutators/honggfuzz/Makefile index 2f46d0e7..1d46f163 100644 --- a/custom_mutators/honggfuzz/Makefile +++ b/custom_mutators/honggfuzz/Makefile @@ -7,6 +7,8 @@ honggfuzz.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c $(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz.so honggfuzz.c mangle.c ../../src/afl-performance.c update: + @# seriously? --unlink is a dud option? sigh ... + rm -f mangle.c mangle.h honggfuzz.h wget --unlink https://github.com/google/honggfuzz/raw/master/mangle.c wget --unlink https://github.com/google/honggfuzz/raw/master/mangle.h wget --unlink https://github.com/google/honggfuzz/raw/master/honggfuzz.h diff --git a/custom_mutators/honggfuzz/honggfuzz.h b/custom_mutators/honggfuzz/honggfuzz.h index 4e045272..9d07fdf4 100644 --- a/custom_mutators/honggfuzz/honggfuzz.h +++ b/custom_mutators/honggfuzz/honggfuzz.h @@ -38,18 +38,17 @@ #include "libhfcommon/util.h" -#define PROG_NAME "honggfuzz" -#define PROG_VERSION "2.2" +#define PROG_NAME "honggfuzz" +#define PROG_VERSION "2.3" -/* Name of the template which will be replaced with the proper name of the file - */ +/* 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 */ +#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" @@ -63,12 +62,11 @@ /* 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 - */ +/* 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_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) @@ -89,7 +87,7 @@ #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 */ +#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 @@ -105,356 +103,283 @@ 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 */ +/* 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 */ +/* 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 */ +/* 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, - + _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; - + 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, - + _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, - + 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; - + 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[]; - + 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]; - + 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]; - + 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; - + 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, - + _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; + 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; + 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 - diff --git a/custom_mutators/honggfuzz/mangle.c b/custom_mutators/honggfuzz/mangle.c index 05e0dcfa..c2988319 100644 --- a/custom_mutators/honggfuzz/mangle.c +++ b/custom_mutators/honggfuzz/mangle.c @@ -51,7 +51,7 @@ static inline size_t mangle_LenLeft(run_t *run, size_t off) { } -/* Get a random value between <1:max> with x^2 distribution */ +/* Get a random value <1:max>, but prefer smaller ones - up to 4KiB */ static inline size_t mangle_getLen(size_t max) { if (max > _HF_INPUT_MAX_SIZE) { @@ -64,27 +64,25 @@ static inline size_t mangle_getLen(size_t max) { if (max == 0) { LOG_F("max == 0"); } if (max == 1) { return 1; } - const uint64_t max2 = (uint64_t)max * max; - const uint64_t max3 = (uint64_t)max * max * max; - const uint64_t rnd = util_rndGet(1, max2 - 1); + /* Give 50% chance the the uniform distribution */ + switch (util_rndGet(0, 9)) { - uint64_t ret = rnd * rnd; - ret /= max3; - ret += 1; - - if (ret < 1) { - - LOG_F("ret (%" PRIu64 ") < 1, max:%zu, rnd:%" PRIu64, ret, max, rnd); - - } - - if (ret > max) { - - LOG_F("ret (%" PRIu64 ") > max (%zu), rnd:%" PRIu64, ret, max, rnd); + case 0: + return (size_t)util_rndGet(1, HF_MIN(16, max)); + case 1: + return (size_t)util_rndGet(1, HF_MIN(64, max)); + case 2: + return (size_t)util_rndGet(1, HF_MIN(256, max)); + case 3: + return (size_t)util_rndGet(1, HF_MIN(1024, max)); + case 4: + return (size_t)util_rndGet(1, HF_MIN(4096, max)); + default: + break; } - return (size_t)ret; + return (size_t)util_rndGet(1, max); } diff --git a/custom_mutators/honggfuzz/mangle.h b/custom_mutators/honggfuzz/mangle.h index 1b6a4943..f8f3988c 100644 --- a/custom_mutators/honggfuzz/mangle.h +++ b/custom_mutators/honggfuzz/mangle.h @@ -26,7 +26,6 @@ #include "honggfuzz.h" -extern void mangle_mangleContent(run_t *run, int speed_factor); +extern void mangle_mangleContent(run_t* run, int speed_factor); #endif - diff --git a/custom_mutators/radamsa/custom_mutator_helpers.h b/custom_mutators/radamsa/custom_mutator_helpers.h index 0848321f..e23c0b6a 100644 --- a/custom_mutators/radamsa/custom_mutator_helpers.h +++ b/custom_mutators/radamsa/custom_mutator_helpers.h @@ -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; diff --git a/docs/Changelog.md b/docs/Changelog.md index ae7377f2..efb5ed0b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,9 +9,16 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to <afl-users+subscribe@googlegroups.com>. -### Version ++2.66d (devel) +### Version ++2.67d (develop) + - Further llvm 12 support (fast moving target like afl++ :-) ) + - Fix for auto dictionary not to throw out a -x dictionary + + +### 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 @@ -19,12 +26,27 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. - 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 + - fixed a bug in redqueen for strings and made deterministic with -s + - Compiletime autodictionary fixes - llvm_mode: - - now supports llvm 12! + - 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) - - LTO: autodictionary mode is a default + - 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 @@ -33,6 +55,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. - 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/) diff --git a/docs/FAQ.md b/docs/FAQ.md index c15cd484..93a87a72 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -2,14 +2,39 @@ ## Contents - 1. [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) - 2. [What is an edge?](#what-is-an-edge) - 3. [Why is my stability below 100%?](#why-is-my-stability-below-100) - 4. [How can I improve the stability value](#how-can-i-improve-the-stability-value) + * [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-to-fuzz-a-network-service) + * [How do I fuzz a GUI program?](#how-to-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 maintance of AFL, however +it is only accepting PR from the community and is not developing enhancements +anymore. + +In the second quarter of 2019, 1 1/2 years after no further development of +AFL had happened and it became clear there would be none coming, afl++ +was born, where initially first community patches were collected and applied +for bugs 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 feature, 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) @@ -20,6 +45,43 @@ If you find an interesting or important question missing, submit it via 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 "out of the box". + +Using network has a slow-down of x10-20 on the fuzzing speed, does not scale, +and finally usually it is more than one initial data packet but a back-and-forth +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 network would be. +See [examples/socket_fuzzing/](../examples/socket_fuzzing/) + +There is an outdated afl++ branch that implements networking if you are +desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) - +however a better option is AFLnet ([https://github.com/aflnet/aflnet](https://github.com/aflnet/aflnet)) +which allows you to define network state with different type of data packets. + +## How do I fuzz a GUI program? + +If the GUI program can read the fuzz data from a file (via the command line, +a fixed location or via an environment variable) without needing any user +interaction then then yes. + +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. @@ -62,15 +124,15 @@ code example above): ``` Every line between two blocks is an `edge`. -## Why is my stability below 100 +## 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 from shared memory, +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 -will result in the edge information being different accross runs. +the result in the edge information 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 @@ -83,9 +145,25 @@ improve the stability. ## How can I improve the stability value +For fuzzing a 100% stable target that covers all edges is the best. +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 +ignore you have a full loss on that edge. + +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 it is a function that has nothing to do with the input data is the +source, e.g. checking jitter, or is a hash map function etc. then it should +not be instrumented. + +To be able to make this decision the following process will allow you to +identify the functions with variable edges so you can make this decision. + Four steps are required to do this and requires quite some knowledge of coding and/or disassembly and it is only effectively possible with -afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! +afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. 1. First step: Identify which edge ID numbers are unstable @@ -96,9 +174,9 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! 2. Second step: Find the responsible function. a) For LTO instrumented binaries this can be documented during compile - time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/afile`. - This file will have one assigned edge ID and the corresponding function - per line. + 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 @@ -112,15 +190,29 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! on start, check to which memory address the edge ID value is written and set a write breakpoint to that address (`watch 0x.....`). - 3. Third step: create a text file with the filenames + c) in all other instrumentation types this is not possible. So just + recompile with the 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. + remove from instrumentation, or just specify the functions you want to + skip instrumenting. Note that optimization might inline functions! - Simply follow this document on how to do this: [llvm_mode/README.instrument_file.md](llvm_mode/README.instrument_file.md) + 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 deny 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) diff --git a/docs/env_variables.md b/docs/env_variables.md index 811c5658..f0ae0b6c 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -202,14 +202,15 @@ Then there are a few specific features that are only available in llvm_mode: See llvm_mode/README.laf-intel.md for more information. -### INSTRUMENT_FILE +### INSTRUMENT LIST (selectively instrument files and functions) This feature allows selectively instrumentation of the source - - Setting AFL_LLVM_INSTRUMENT_FILE with a filename will only instrument those - files that match the names listed in this file. + - Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or + function will only instrument (or skip) those files that match the names + listed in the specified file. - See llvm_mode/README.instrument_file.md for more information. + See llvm_mode/README.instrument_list.md for more information. ### NOT_ZERO @@ -241,7 +242,7 @@ Then there are a few specific features that are only available in the gcc_plugin - Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those files that match the names listed in this file (one filename per line). - See gcc_plugin/README.instrument_file.md for more information. + See gcc_plugin/README.instrument_list.md for more information. ## 3) Settings for afl-fuzz diff --git a/docs/perf_tips.md b/docs/perf_tips.md index 7a690b77..731dc238 100644 --- a/docs/perf_tips.md +++ b/docs/perf_tips.md @@ -67,7 +67,7 @@ to get to the important parts in the code. If you are only interested in specific parts of the code being fuzzed, you can instrument_files the files that are actually relevant. This improves the speed and -accuracy of afl. See llvm_mode/README.instrument_file.md +accuracy of afl. See llvm_mode/README.instrument_list.md Also use the InsTrim mode on larger binaries, this improves performance and coverage a lot. diff --git a/dynamic_list.txt b/dynamic_list.txt new file mode 100644 index 00000000..3c0b054f --- /dev/null +++ b/dynamic_list.txt @@ -0,0 +1,10 @@ +{ + "__afl_area_ptr"; + "__afl_manual_init"; + "__afl_persistent_loop"; + "__afl_auto_init"; + "__afl_area_initial"; + "__afl_prev_loc"; + "__sanitizer_cov_trace_pc_guard"; + "__sanitizer_cov_trace_pc_guard_init"; +}; diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c index ab7874fd..c70fd47d 100644 --- a/examples/afl_network_proxy/afl-network-server.c +++ b/examples/afl_network_proxy/afl-network-server.c @@ -73,9 +73,8 @@ static u8 *in_file, /* Minimizer input test case */ static u8 *in_data; /* Input data for trimming */ static u8 *buf2; -static s32 in_len; -static u32 map_size = MAP_SIZE; -static size_t buf2_len; +static s32 in_len; +static u32 map_size = MAP_SIZE; static volatile u8 stop_soon; /* Ctrl-C pressed? */ @@ -272,7 +271,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { setenv("QEMU_SET_ENV", buf, 1); - ck_free(buf); + afl_free(buf); } else { @@ -343,7 +342,7 @@ static void usage(u8 *argv0) { } -int recv_testcase(int s, void **buf, size_t *max_len) { +int recv_testcase(int s, void **buf) { u32 size; s32 ret; @@ -358,7 +357,8 @@ int recv_testcase(int s, void **buf, size_t *max_len) { if ((size & 0xff000000) != 0xff000000) { - *buf = ck_maybe_grow(buf, max_len, size); + *buf = afl_realloc((void **)&buf, size); + if (unlikely(!buf)) { PFATAL("Alloc"); } received = 0; // fprintf(stderr, "unCOMPRESS (%u)\n", size); while (received < size && @@ -370,7 +370,8 @@ int recv_testcase(int s, void **buf, size_t *max_len) { #ifdef USE_DEFLATE u32 clen; size -= 0xff000000; - *buf = ck_maybe_grow(buf, max_len, size); + *buf = afl_realloc((void **)&buf, size); + if (unlikely(!buf)) { PFATAL("Alloc"); } received = 0; while (received < 4 && (ret = recv(s, &clen + received, 4 - received, 0)) > 0) @@ -379,15 +380,15 @@ int recv_testcase(int s, void **buf, size_t *max_len) { // fprintf(stderr, "received clen information of %d\n", clen); if (clen < 1) FATAL("did not receive valid compressed len information: %u", clen); - buf2 = ck_maybe_grow((void **)&buf2, &buf2_len, clen); + buf2 = afl_realloc((void **)&buf2, clen); + if (unlikely(!buf2)) { PFATAL("Alloc"); } received = 0; while (received < clen && (ret = recv(s, buf2 + received, clen - received, 0)) > 0) received += ret; if (received != clen) FATAL("did not receive compressed information"); if (libdeflate_deflate_decompress(decompressor, buf2, clen, (char *)*buf, - *max_len, - &received) != LIBDEFLATE_SUCCESS) + size, &received) != LIBDEFLATE_SUCCESS) FATAL("decompression failed"); // fprintf(stderr, "DECOMPRESS (%u->%u):\n", clen, received); // for (u32 i = 0; i < clen; i++) fprintf(stderr, "%02x", buf2[i]); @@ -413,7 +414,6 @@ int recv_testcase(int s, void **buf, size_t *max_len) { int main(int argc, char **argv_orig, char **envp) { s32 opt, s, sock, on = 1, port = -1; - size_t max_len = 0; u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; char **use_argv; struct sockaddr_in6 serveraddr, clientaddr; @@ -568,7 +568,8 @@ int main(int argc, char **argv_orig, char **envp) { sharedmem_t shm = {0}; fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); - in_data = ck_maybe_grow((void **)&in_data, &max_len, 65536); + in_data = afl_realloc((void **)&in_data, 65536); + if (unlikely(!in_data)) { PFATAL("Alloc"); } atexit(at_exit_handler); setup_signal_handlers(); @@ -639,7 +640,8 @@ int main(int argc, char **argv_orig, char **envp) { #ifdef USE_DEFLATE compressor = libdeflate_alloc_compressor(1); decompressor = libdeflate_alloc_decompressor(); - buf2 = ck_maybe_grow((void **)&buf2, &buf2_len, map_size + 16); + buf2 = afl_realloc((void **)&buf2, map_size + 16); + if (unlikely(!buf2)) { PFATAL("alloc"); } lenptr = (u32 *)(buf2 + 4); fprintf(stderr, "Compiled with compression support\n"); #endif @@ -664,7 +666,7 @@ int main(int argc, char **argv_orig, char **envp) { #endif - while ((in_len = recv_testcase(s, (void **)&in_data, &max_len)) > 0) { + while ((in_len = recv_testcase(s, (void **)&in_data)) > 0) { // fprintf(stderr, "received %u\n", in_len); (void)run_target(fsrv, use_argv, in_data, in_len, 1); @@ -697,9 +699,9 @@ int main(int argc, char **argv_orig, char **envp) { afl_shm_deinit(&shm); afl_fsrv_deinit(fsrv); if (fsrv->target_path) { ck_free(fsrv->target_path); } - if (in_data) { ck_free(in_data); } + afl_free(in_data); #if USE_DEFLATE - if (buf2) { ck_free(buf2); } + afl_free(buf2); libdeflate_free_compressor(compressor); libdeflate_free_decompressor(decompressor); #endif diff --git a/examples/aflpp_driver/GNUmakefile b/examples/aflpp_driver/GNUmakefile index b118a8b5..57e74be7 100644 --- a/examples/aflpp_driver/GNUmakefile +++ b/examples/aflpp_driver/GNUmakefile @@ -16,6 +16,7 @@ aflpp_driver.o: aflpp_driver.c libAFLDriver.a: aflpp_driver.o ar ru libAFLDriver.a aflpp_driver.o + cp -vf libAFLDriver.a ../../ debug: $(LLVM_BINDIR)clang -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c @@ -29,6 +30,7 @@ aflpp_qemu_driver.o: aflpp_qemu_driver.c libAFLQemuDriver.a: aflpp_qemu_driver.o ar ru libAFLQemuDriver.a aflpp_qemu_driver.o + cp -vf libAFLQemuDriver.a ../../ aflpp_qemu_driver_hook.so: aflpp_qemu_driver_hook.o $(LLVM_BINDIR)clang -shared aflpp_qemu_driver_hook.o -o aflpp_qemu_driver_hook.so diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 86c7a69f..ff5446e9 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -56,13 +56,21 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <sys/mman.h> #include "config.h" +#include "cmplog.h" #ifdef _DEBUG #include "hash.h" #endif +#ifndef MAP_FIXED_NOREPLACE + #define MAP_FIXED_NOREPLACE 0x100000 +#endif + +#define MAX_DUMMY_SIZE 256000 + // Platform detection. Copied from FuzzerInternal.h #ifdef __linux__ #define LIBFUZZER_LINUX 1 @@ -242,22 +250,26 @@ int main(int argc, char **argv) { printf( "======================= INFO =========================\n" - "This binary is built for AFL-fuzz.\n" + "This binary is built for afl++.\n" "To run the target function on individual input(s) execute this:\n" - " %s < INPUT_FILE\n" - "or\n" " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" "To fuzz with afl-fuzz execute this:\n" - " afl-fuzz [afl-flags] %s [-N]\n" - "afl-fuzz will run N iterations before " - "re-spawning the process (default: 1000)\n" + " afl-fuzz [afl-flags] -- %s [-N]\n" + "afl-fuzz will run N iterations before re-spawning the process (default: " + "1000)\n" "======================================================\n", - argv[0], argv[0], argv[0]); + argv[0], argv[0]); output_file = stderr; maybe_duplicate_stderr(); maybe_close_fd_mask(); - if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); + if (LLVMFuzzerInitialize) { + + fprintf(stderr, "Running LLVMFuzzerInitialize ...\n"); + LLVMFuzzerInitialize(&argc, &argv); + fprintf(stderr, "continue...\n"); + + } // Do any other expensive one-time initialization here. @@ -272,13 +284,9 @@ int main(int argc, char **argv) { printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); else if (argc > 1) { - // if (!getenv("AFL_DRIVER_DONT_DEFER")) { - __afl_sharedmem_fuzzing = 0; __afl_manual_init(); - // } return ExecuteFilesOnyByOne(argc, argv); - exit(0); } diff --git a/examples/aflpp_driver/aflpp_driver_test.c b/examples/aflpp_driver/aflpp_driver_test.c index e4567bbf..ddc3effb 100644 --- a/examples/aflpp_driver/aflpp_driver_test.c +++ b/examples/aflpp_driver/aflpp_driver_test.c @@ -4,6 +4,16 @@ #include "hash.h" +void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { + + if (Data[0] == 'F') + if (Data[1] == 'A') + if (Data[2] == '$') + if (Data[3] == '$') + if (Data[4] == '$') abort(); + +} + int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { fprintf(stderr, "FUNC crc: %016llx len: %lu\n", @@ -13,11 +23,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (Size < 5) return 0; - if (Data[0] == 'F') - if (Data[1] == 'A') - if (Data[2] == '$') - if (Data[3] == '$') - if (Data[4] == '$') abort(); + crashme(Data, Size); return 0; diff --git a/examples/custom_mutators/custom_mutator_helpers.h b/examples/custom_mutators/custom_mutator_helpers.h index 0848321f..ad5acb08 100644 --- a/examples/custom_mutators/custom_mutator_helpers.h +++ b/examples/custom_mutators/custom_mutator_helpers.h @@ -324,8 +324,8 @@ static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { } /* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void swap_bufs(void **buf1, size_t *size1, void **buf2, - size_t *size2) { +static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, + size_t *size2) { void * scratch_buf = *buf1; size_t scratch_size = *size1; diff --git a/gcc_plugin/GNUmakefile b/gcc_plugin/GNUmakefile index 4a4f0dcd..f10a6c1d 100644 --- a/gcc_plugin/GNUmakefile +++ b/gcc_plugin/GNUmakefile @@ -163,7 +163,7 @@ install: all install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH) install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH) install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md - install -m 644 -T README.instrument_file.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md + install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md clean: rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 diff --git a/gcc_plugin/Makefile b/gcc_plugin/Makefile index f720112f..c088b61c 100644 --- a/gcc_plugin/Makefile +++ b/gcc_plugin/Makefile @@ -152,7 +152,7 @@ install: all install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH) install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH) install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md - install -m 644 -T README.instrument_file.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md + install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md clean: rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 diff --git a/gcc_plugin/README.instrument_file.md b/gcc_plugin/README.instrument_list.md index d0eaf6ff..d0eaf6ff 100644 --- a/gcc_plugin/README.instrument_file.md +++ b/gcc_plugin/README.instrument_list.md diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index b82ddb4a..c04ba396 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -126,6 +126,9 @@ #define STAGE_BUF_SIZE (64) /* usable size for stage name buf in afl_state */ +// Little helper to access the ptr to afl->##name_buf - for use in afl_realloc. +#define AFL_BUF_PARAM(name) ((void **)&afl->name##_buf) + extern s8 interesting_8[INTERESTING_8_LEN]; extern s16 interesting_16[INTERESTING_8_LEN + INTERESTING_16_LEN]; extern s32 @@ -172,6 +175,14 @@ struct extra_data { }; +struct auto_extra_data { + + u8 data[MAX_AUTO_EXTRA]; /* Dictionary token data */ + u32 len; /* Dictionary token length */ + u32 hit_cnt; /* Use count in the corpus */ + +}; + /* Fuzzing stages */ enum { @@ -564,15 +575,15 @@ typedef struct afl_state { // growing buf struct queue_entry **queue_buf; - size_t queue_size; struct queue_entry **top_rated; /* Top entries for bitmap bytes */ struct extra_data *extras; /* Extra tokens to fuzz with */ u32 extras_cnt; /* Total number of tokens read */ - struct extra_data *a_extras; /* Automatically selected extras */ - u32 a_extras_cnt; /* Total number of tokens available */ + struct auto_extra_data + a_extras[MAX_AUTO_EXTRAS]; /* Automatically selected extras */ + u32 a_extras_cnt; /* Total number of tokens available */ /* afl_postprocess API - Now supported via custom mutators */ @@ -608,8 +619,6 @@ typedef struct afl_state { u32 document_counter; #endif - void *maybe_add_auto; - /* statistics file */ double last_bitmap_cvg, last_stability, last_eps; @@ -626,24 +635,18 @@ typedef struct afl_state { /*needed for afl_fuzz_one */ // TODO: see which we can reuse - u8 * out_buf; - size_t out_size; + u8 *out_buf; - u8 * out_scratch_buf; - size_t out_scratch_size; + u8 *out_scratch_buf; - u8 * eff_buf; - size_t eff_size; + u8 *eff_buf; - u8 * in_buf; - size_t in_size; + u8 *in_buf; - u8 * in_scratch_buf; - size_t in_scratch_size; + u8 *in_scratch_buf; - u8 * ex_buf; - size_t ex_size; - u32 custom_mutators_count; + u8 *ex_buf; + u32 custom_mutators_count; list_t custom_mutator_list; @@ -656,9 +659,9 @@ typedef struct afl_state { struct custom_mutator { const char *name; + char * name_short; void * dh; u8 * post_process_buf; - size_t post_process_size; u8 stacked_custom_prob, stacked_custom; void *data; /* custom mutator data ptr */ @@ -910,7 +913,8 @@ u8 has_new_bits(afl_state_t *, u8 *); void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32); void load_extras(afl_state_t *, u8 *); -void maybe_add_auto(void *, u8 *, u32); +void add_extra(afl_state_t *afl, u8 *mem, u32 len); +void maybe_add_auto(afl_state_t *, u8 *, u32); void save_auto(afl_state_t *); void load_auto(afl_state_t *); void destroy_extras(afl_state_t *); @@ -986,6 +990,8 @@ uint64_t rand_next(afl_state_t *afl); static inline u32 rand_below(afl_state_t *afl, u32 limit) { + if (limit <= 1) return 0; + /* The boundary not being necessarily a power of 2, we need to ensure the result uniformity. */ if (unlikely(!afl->rand_cnt--) && likely(!afl->fixed_seed)) { @@ -1001,6 +1007,32 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) { } +/* we prefer lower range values here */ +/* this is only called with normal havoc, not MOpt, to have an equalizer for + expand havoc mode */ +static inline u32 rand_below_datalen(afl_state_t *afl, u32 limit) { + + if (limit <= 1) return 0; + + switch (rand_below(afl, 3)) { + + case 2: + return (rand_below(afl, limit) % (1 + rand_below(afl, limit - 1))) % + (1 + rand_below(afl, limit - 1)); + break; + case 1: + return rand_below(afl, limit) % (1 + rand_below(afl, limit - 1)); + break; + case 0: + return rand_below(afl, limit); + break; + + } + + return 1; // cannot be reached + +} + static inline s64 rand_get_seed(afl_state_t *afl) { if (unlikely(afl->fixed_seed)) { return afl->init_seed; } diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 832b2de4..90701d18 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -30,12 +30,13 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stddef.h> #include "config.h" #include "types.h" #include "debug.h" -/* Initial size used for ck_maybe_grow */ +/* Initial size used for afl_realloc */ #define INITIAL_GROWTH_SIZE (64) // Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators @@ -76,10 +77,6 @@ \ } while (0) - /* Allocator increments for ck_realloc_block(). */ - - #define ALLOC_BLK_INC 256 - /* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized requests. */ @@ -149,15 +146,6 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) { } -/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up - repeated small reallocs without complicating the user code). */ - -static inline void *DFL_ck_realloc_block(void *orig, u32 size) { - - return DFL_ck_realloc(orig, size); - -} - /* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ static inline u8 *DFL_ck_strdup(u8 *str) { @@ -177,53 +165,13 @@ static inline u8 *DFL_ck_strdup(u8 *str) { } -/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized - or NULL inputs. */ - -static inline void *DFL_ck_memdup(void *mem, u32 size) { - - void *ret; - - if (!mem || !size) { return NULL; } - - ALLOC_CHECK_SIZE(size); - ret = malloc(size); - ALLOC_CHECK_RESULT(ret, size); - - return memcpy(ret, mem, size); - -} - -/* Create a buffer with a block of text, appending a NUL terminator at the end. - Returns NULL for zero-sized or NULL inputs. */ - -static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { - - u8 *ret; - - if (!mem || !size) { return NULL; } - - ALLOC_CHECK_SIZE(size); - ret = (u8 *)malloc(size + 1); - ALLOC_CHECK_RESULT(ret, size); - - memcpy(ret, mem, size); - ret[size] = 0; - - return ret; - -} - /* In non-debug mode, we just do straightforward aliasing of the above functions to user-visible names such as ck_alloc(). */ #define ck_alloc DFL_ck_alloc #define ck_alloc_nozero DFL_ck_alloc_nozero #define ck_realloc DFL_ck_realloc - #define ck_realloc_block DFL_ck_realloc_block #define ck_strdup DFL_ck_strdup - #define ck_memdup DFL_ck_memdup - #define ck_memdup_str DFL_ck_memdup_str #define ck_free DFL_ck_free #define alloc_report() @@ -278,10 +226,6 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { #define ALLOC_OFF_HEAD 8 #define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) - /* Allocator increments for ck_realloc_block(). */ - - #define ALLOC_BLK_INC 256 - /* Sanity-checking macros for pointers. */ #define CHECK_PTR(_p) \ @@ -441,29 +385,6 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) { } -/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up - repeated small reallocs without complicating the user code). */ - -static inline void *DFL_ck_realloc_block(void *orig, u32 size) { - - #ifndef DEBUG_BUILD - - if (orig) { - - CHECK_PTR(orig); - - if (ALLOC_S(orig) >= size) return orig; - - size += ALLOC_BLK_INC; - - } - - #endif /* !DEBUG_BUILD */ - - return DFL_ck_realloc(orig, size); - -} - /* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ static inline u8 *DFL_ck_strdup(u8 *str) { @@ -489,55 +410,6 @@ static inline u8 *DFL_ck_strdup(u8 *str) { } -/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized - or NULL inputs. */ - -static inline void *DFL_ck_memdup(void *mem, u32 size) { - - void *ret; - - if (!mem || !size) return NULL; - - ALLOC_CHECK_SIZE(size); - ret = malloc(size + ALLOC_OFF_TOTAL); - ALLOC_CHECK_RESULT(ret, size); - - ret += ALLOC_OFF_HEAD; - - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; - - return memcpy(ret, mem, size); - -} - -/* Create a buffer with a block of text, appending a NUL terminator at the end. - Returns NULL for zero-sized or NULL inputs. */ - -static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { - - u8 *ret; - - if (!mem || !size) return NULL; - - ALLOC_CHECK_SIZE(size); - ret = malloc(size + ALLOC_OFF_TOTAL + 1); - ALLOC_CHECK_RESULT(ret, size); - - ret += ALLOC_OFF_HEAD; - - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; - - memcpy(ret, mem, size); - ret[size] = 0; - - return ret; - -} - #ifndef DEBUG_BUILD /* In non-debug mode, we just do straightforward aliasing of the above @@ -546,10 +418,7 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { #define ck_alloc DFL_ck_alloc #define ck_alloc_nozero DFL_ck_alloc_nozero #define ck_realloc DFL_ck_realloc - #define ck_realloc_block DFL_ck_realloc_block #define ck_strdup DFL_ck_strdup - #define ck_memdup DFL_ck_memdup - #define ck_memdup_str DFL_ck_memdup_str #define ck_free DFL_ck_free #define alloc_report() @@ -618,8 +487,8 @@ static inline void TRK_alloc_buf(void *ptr, const char *file, const char *func, /* No space available - allocate more. */ - TRK[bucket] = DFL_ck_realloc_block( - TRK[bucket], (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); + TRK[bucket] = DFL_ck_realloc(TRK[bucket], + (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); TRK[bucket][i].ptr = ptr; TRK[bucket][i].file = (char *)file; @@ -694,16 +563,6 @@ static inline void *TRK_ck_realloc(void *orig, u32 size, const char *file, } -static inline void *TRK_ck_realloc_block(void *orig, u32 size, const char *file, - const char *func, u32 line) { - - void *ret = DFL_ck_realloc_block(orig, size); - TRK_free_buf(orig, file, func, line); - TRK_alloc_buf(ret, file, func, line); - return ret; - -} - static inline void *TRK_ck_strdup(u8 *str, const char *file, const char *func, u32 line) { @@ -713,24 +572,6 @@ static inline void *TRK_ck_strdup(u8 *str, const char *file, const char *func, } -static inline void *TRK_ck_memdup(void *mem, u32 size, const char *file, - const char *func, u32 line) { - - void *ret = DFL_ck_memdup(mem, size); - TRK_alloc_buf(ret, file, func, line); - return ret; - -} - -static inline void *TRK_ck_memdup_str(void *mem, u32 size, const char *file, - const char *func, u32 line) { - - void *ret = DFL_ck_memdup_str(mem, size); - TRK_alloc_buf(ret, file, func, line); - return ret; - -} - static inline void TRK_ck_free(void *ptr, const char *file, const char *func, u32 line) { @@ -749,17 +590,8 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func, #define ck_realloc(_p1, _p2) \ TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) - #define ck_realloc_block(_p1, _p2) \ - TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) - #define ck_strdup(_p1) TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__) - #define ck_memdup(_p1, _p2) \ - TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) - - #define ck_memdup_str(_p1, _p2) \ - TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) - #define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__) #endif /* ^!DEBUG_BUILD */ @@ -771,11 +603,14 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func, */ static inline size_t next_pow2(size_t in) { - if (in == 0 || in > (size_t)-1) { + // Commented this out as this behavior doesn't change, according to unittests + // if (in == 0 || in > (size_t)-1) { - return 0; /* avoid undefined behaviour under-/overflow */ - - } + // + // return 0; /* avoid undefined behaviour under-/overflow + // */ + // + // } size_t out = in - 1; out |= out >> 1; @@ -787,32 +622,32 @@ static inline size_t next_pow2(size_t in) { } -/* This function makes sure *size is > size_needed after call. - It will realloc *buf otherwise. - *size will grow exponentially as per: - https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ - Will return NULL and free *buf if size_needed is <1 or realloc failed. - @return For convenience, this function returns *buf. - */ -static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { +/* AFL alloc buffer, the struct is here so we don't need to do fancy ptr + * arithmetics */ +struct afl_alloc_buf { - /* No need to realloc */ - if (likely(size_needed && *size >= size_needed)) { return *buf; } + /* The complete allocated size, including the header of len + * AFL_ALLOC_SIZE_OFFSET */ + size_t complete_size; + /* ptr to the first element of the actual buffer */ + u8 buf[0]; - /* No initial size was set */ - if (size_needed < INITIAL_GROWTH_SIZE) { size_needed = INITIAL_GROWTH_SIZE; } +}; - /* grow exponentially */ - size_t next_size = next_pow2(size_needed); +#define AFL_ALLOC_SIZE_OFFSET (offsetof(struct afl_alloc_buf, buf)) - /* handle overflow and zero size_needed */ - if (!next_size) { next_size = size_needed; } +/* Returs the container element to this ptr */ +static inline struct afl_alloc_buf *afl_alloc_bufptr(void *buf) { - /* alloc */ - *buf = realloc(*buf, next_size); - *size = *buf ? next_size : 0; + return (struct afl_alloc_buf *)((u8 *)buf - AFL_ALLOC_SIZE_OFFSET); - return *buf; +} + +/* Gets the maximum size of the buf contents (ptr->complete_size - + * AFL_ALLOC_SIZE_OFFSET) */ +static inline size_t afl_alloc_bufsize(void *buf) { + + return afl_alloc_bufptr(buf)->complete_size - AFL_ALLOC_SIZE_OFFSET; } @@ -820,45 +655,71 @@ static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { It will realloc *buf otherwise. *size will grow exponentially as per: https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ - Will FATAL if size_needed is <1. + Will return NULL and free *buf if size_needed is <1 or realloc failed. @return For convenience, this function returns *buf. */ -static inline void *ck_maybe_grow(void **buf, size_t *size, - size_t size_needed) { +static inline void *afl_realloc(void **buf, size_t size_needed) { - /* Oops. found a bug? */ - if (unlikely(size_needed < 1)) { FATAL("cannot grow to non-positive size"); } + struct afl_alloc_buf *new_buf = NULL; + + size_t current_size = 0; + size_t next_size = 0; + + if (likely(*buf)) { + + /* the size is always stored at buf - 1*size_t */ + new_buf = afl_alloc_bufptr(*buf); + current_size = new_buf->complete_size; + + } + + size_needed += AFL_ALLOC_SIZE_OFFSET; /* No need to realloc */ - if (likely(*size >= size_needed)) { return *buf; } + if (likely(current_size >= size_needed)) { return *buf; } /* No initial size was set */ - if (size_needed < INITIAL_GROWTH_SIZE) { size_needed = INITIAL_GROWTH_SIZE; } + if (size_needed < INITIAL_GROWTH_SIZE) { + + next_size = INITIAL_GROWTH_SIZE; + + } else { + + /* grow exponentially */ + next_size = next_pow2(size_needed); - /* grow exponentially */ - size_t next_size = next_pow2(size_needed); + /* handle overflow: fall back to the original size_needed */ + if (unlikely(!next_size)) { next_size = size_needed; } - /* handle overflow */ - if (!next_size) { next_size = size_needed; } + } /* alloc */ - *buf = ck_realloc(*buf, next_size); - *size = next_size; + new_buf = realloc(new_buf, next_size); + if (unlikely(!new_buf)) { + + *buf = NULL; + return NULL; + + } + new_buf->complete_size = next_size; + *buf = (void *)(new_buf->buf); return *buf; } +static inline void afl_free(void *buf) { + + if (buf) { free(afl_alloc_bufptr(buf)); } + +} + /* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void swap_bufs(void **buf1, size_t *size1, void **buf2, - size_t *size2) { +static inline void afl_swap_bufs(void **buf1, void **buf2) { - void * scratch_buf = *buf1; - size_t scratch_size = *size1; + void *scratch_buf = *buf1; *buf1 = *buf2; - *size1 = *size2; *buf2 = scratch_buf; - *size2 = scratch_size; } diff --git a/include/config.h b/include/config.h index 344a368f..6c73bced 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++2.66d" +#define VERSION "++2.67d" /****************************************************** * * @@ -70,21 +70,21 @@ #ifndef __NetBSD__ #ifndef WORD_SIZE_64 - #define MEM_LIMIT 25 - #else #define MEM_LIMIT 50 + #else + #define MEM_LIMIT 75 #endif /* ^!WORD_SIZE_64 */ #else /* NetBSD's kernel needs more space for stack, see discussion for issue \ #165 */ - #define MEM_LIMIT 200 + #define MEM_LIMIT 250 #endif /* Default memory limit when running in QEMU mode (MB): */ -#define MEM_LIMIT_QEMU 200 +#define MEM_LIMIT_QEMU 250 /* Default memory limit when running in Unicorn mode (MB): */ -#define MEM_LIMIT_UNICORN 200 +#define MEM_LIMIT_UNICORN 250 /* Number of calibration cycles per every new test case (and for test cases that show variable behavior): */ diff --git a/include/debug.h b/include/debug.h index d1bd971b..f9ebce58 100644 --- a/include/debug.h +++ b/include/debug.h @@ -28,11 +28,6 @@ #include "types.h" #include "config.h" -/* __FUNCTION__ is non-iso */ -#ifdef __func__ - #define __FUNCTION__ __func__ -#endif - /******************* * Terminal colors * *******************/ @@ -223,43 +218,43 @@ /* Die with a verbose non-OS fatal error message. */ -#define FATAL(x...) \ - do { \ - \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ - SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __FUNCTION__, \ - __FILE__, __LINE__); \ - exit(1); \ - \ +#define FATAL(x...) \ + do { \ + \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] PROGRAM ABORT : " cRST x); \ + SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ + __FILE__, __LINE__); \ + exit(1); \ + \ } while (0) /* Die by calling abort() to provide a core dump. */ -#define ABORT(x...) \ - do { \ - \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ - SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __FUNCTION__, \ - __FILE__, __LINE__); \ - abort(); \ - \ +#define ABORT(x...) \ + do { \ + \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] PROGRAM ABORT : " cRST x); \ + SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ + __FILE__, __LINE__); \ + abort(); \ + \ } while (0) /* Die while also including the output of perror(). */ -#define PFATAL(x...) \ - do { \ - \ - fflush(stdout); \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] SYSTEM ERROR : " cRST x); \ - SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __FUNCTION__, \ - __FILE__, __LINE__); \ - SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ - exit(1); \ - \ +#define PFATAL(x...) \ + do { \ + \ + fflush(stdout); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] SYSTEM ERROR : " cRST x); \ + SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ + __FILE__, __LINE__); \ + SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ + exit(1); \ + \ } while (0) /* Die with FATAL() or PFATAL() depending on the value of res (used to @@ -281,7 +276,7 @@ #define ck_write(fd, buf, len, fn) \ do { \ \ - u32 _len = (len); \ + s32 _len = (s32)(len); \ s32 _res = write(fd, buf, _len); \ if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \ \ @@ -290,7 +285,7 @@ #define ck_read(fd, buf, len, fn) \ do { \ \ - u32 _len = (len); \ + s32 _len = (s32)(len); \ s32 _res = read(fd, buf, _len); \ if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \ \ diff --git a/include/envs.h b/include/envs.h index 7153ed47..96ae91ba 100644 --- a/include/envs.h +++ b/include/envs.h @@ -62,6 +62,9 @@ static char *afl_environment_variables[] = { "AFL_REAL_LD", "AFL_LD_PRELOAD", "AFL_LD_VERBOSE", + "AFL_LLVM_ALLOWLIST", + "AFL_LLVM_DENYLIST", + "AFL_LLVM_BLOCKLIST", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CTX", diff --git a/include/forkserver.h b/include/forkserver.h index 717493db..d824c1c9 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -89,9 +89,9 @@ typedef struct afl_forkserver { /* Function to kick off the forkserver child */ void (*init_child_func)(struct afl_forkserver *fsrv, char **argv); - u8 *function_opt; /* for autodictionary: afl ptr */ + u8 *afl_ptr; /* for autodictionary: afl ptr */ - void (*function_ptr)(void *afl_tmp, u8 *mem, u32 len); + void (*add_extra_func)(void *afl_ptr, u8 *mem, u32 len); } afl_forkserver_t; diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile index a030b910..fb4e8537 100644 --- a/llvm_mode/GNUmakefile +++ b/llvm_mode/GNUmakefile @@ -206,6 +206,9 @@ 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 @@ -218,7 +221,9 @@ CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ../include/ \ -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ - -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ + -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 override CFLAGS += $(CFLAGS_SAFE) @@ -236,7 +241,7 @@ endif ifneq "$(LLVM_CONFIG)" "" CLANG_CFL += -I$(shell dirname $(LLVM_CONFIG))/../include endif -CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) +CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS) @@ -387,20 +392,20 @@ endif $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o document: - $(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o - @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -m32 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -m64 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + $(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o + @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CLANG_CFL) -O3 -Wno-unused-result -m32 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CLANG_CFL) -O3 -Wno-unused-result -m64 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi ../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps - $(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -fPIC -c $< -o $@ + $(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) -O3 -Wno-unused-result -fPIC -c $< -o $@ ../afl-llvm-rt-32.o: afl-llvm-rt.o.c | test_deps @printf "[*] Building 32-bit variant of the runtime (-m32)... " - @$(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi ../afl-llvm-rt-64.o: afl-llvm-rt.o.c | test_deps @printf "[*] Building 64-bit variant of the runtime (-m64)... " - @$(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi test_build: $(PROGS) @echo "[*] Testing the CC wrapper and instrumentation output..." @@ -427,6 +432,7 @@ install: all if [ -f ../split-compares-pass.so ]; then set -e; install -m 755 ../split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi if [ -f ../split-switches-pass.so ]; then set -e; install -m 755 ../split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi if [ -f ../cmplog-instructions-pass.so ]; then set -e; install -m 755 ../cmplog-*-pass.so $${DESTDIR}$(HELPER_PATH); fi + set -e; install -m 644 ../dynamic_list.txt $${DESTDIR}$(HELPER_PATH) set -e; if [ -f ../afl-clang-fast ] ; then ln -sf ../afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ../afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf ../afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ../afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi install -m 644 README.*.md $${DESTDIR}$(DOC_PATH)/ install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.llvm_mode.md diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 4d8c4719..6b2aaf23 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -94,7 +94,7 @@ struct InsTrim : public ModulePass { } -#if LLVM_VERSION_MAJOR >= 4 || \ +#if LLVM_VERSION_MAJOR > 4 || \ (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) #define AFL_HAVE_VECTOR_INTRINSICS 1 #endif @@ -144,7 +144,7 @@ struct InsTrim : public ModulePass { #ifdef AFL_HAVE_VECTOR_INTRINSICS unsigned int ngram_size = 0; /* Decide previous location vector size (must be a power of two) */ - VectorType *PrevLocTy; + VectorType *PrevLocTy = NULL; if (ngram_size_str) if (sscanf(ngram_size_str, "%u", &ngram_size) != 1 || ngram_size < 2 || @@ -181,10 +181,16 @@ struct InsTrim : public ModulePass { #ifdef AFL_HAVE_VECTOR_INTRINSICS // IntegerType *Int64Ty = IntegerType::getInt64Ty(C); - uint64_t PrevLocVecSize = PowerOf2Ceil(PrevLocSize); + int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); IntegerType *IntLocTy = IntegerType::getIntNTy(C, sizeof(PREV_LOC_T) * CHAR_BIT); - if (ngram_size) PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize); + if (ngram_size) + PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize + #if LLVM_VERSION_MAJOR >= 12 + , + false + #endif + ); #endif /* Get globals for the SHM region and the previous location. Note that @@ -194,7 +200,7 @@ struct InsTrim : public ModulePass { new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc; - GlobalVariable *AFLContext; + GlobalVariable *AFLContext = NULL; LoadInst * PrevCtx = NULL; // for CTX sensitive coverage if (ctx_str) @@ -256,6 +262,8 @@ struct InsTrim : public ModulePass { u64 total_rs = 0; u64 total_hs = 0; + scanForDangerousFunctions(&M); + for (Function &F : M) { if (debug) { diff --git a/llvm_mode/README.instrument_file.md b/llvm_mode/README.instrument_file.md deleted file mode 100644 index 46e45ba2..00000000 --- a/llvm_mode/README.instrument_file.md +++ /dev/null @@ -1,81 +0,0 @@ -# Using afl++ with partial instrumentation - - This file describes how you can selectively instrument only the source files - that are interesting to you using the LLVM instrumentation provided by - afl++ - - Originally developed by Christian Holler (:decoder) <choller@mozilla.com>. - -## 1) Description and purpose - -When building and testing complex programs where only a part of the program is -the fuzzing target, it often helps to only instrument the necessary parts of -the program, leaving the rest uninstrumented. This helps to focus the fuzzer -on the important parts of the program, avoiding undesired noise and -disturbance by uninteresting code being exercised. - -For this purpose, I have added a "partial instrumentation" support to the LLVM -mode of AFLFuzz that allows you to specify on a source file level which files -should be compiled with or without instrumentation. - -Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: -https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation - -## 2) Building the LLVM module - -The new code is part of the existing afl++ LLVM module in the llvm_mode/ -subdirectory. There is nothing specifically to do :) - - -## 3) How to use the partial instrumentation mode - -In order to build with partial instrumentation, you need to build with -afl-clang-fast and afl-clang-fast++ respectively. The only required change is -that you need to set the environment variable AFL_LLVM_INSTRUMENT_FILE when calling -the compiler. - -The environment variable must point to a file containing all the filenames -that should be instrumented. For matching, the filename that is being compiled -must end in the filename entry contained in this the instrument file list (to avoid breaking -the matching when absolute paths are used during compilation). - -For example if your source tree looks like this: - -``` -project/ -project/feature_a/a1.cpp -project/feature_a/a2.cpp -project/feature_b/b1.cpp -project/feature_b/b2.cpp -``` - -and you only want to test feature_a, then create a the instrument file list file containing: - -``` -feature_a/a1.cpp -feature_a/a2.cpp -``` - -However if the instrument file list file contains only this, it works as well: - -``` -a1.cpp -a2.cpp -``` - -but it might lead to files being unwantedly instrumented if the same filename -exists somewhere else in the project directories. - -The created the instrument file list file is then set to AFL_LLVM_INSTRUMENT_FILE when you compile -your program. For each file that didn't match the the instrument file list, the compiler will -issue a warning at the end stating that no blocks were instrumented. If you -didn't intend to instrument that file, then you can safely ignore that warning. - -For old LLVM versions this feature might require to be compiled with debug -information (-g), however at least from llvm version 6.0 onwards this is not -required anymore (and might hurt performance and crash detection, so better not -use -g). - -## 4) UNIX-style filename pattern matching -You can add UNIX-style pattern matching in the the instrument file list entries. See `man -fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.instrument_list.md b/llvm_mode/README.instrument_list.md new file mode 100644 index 00000000..1fc06414 --- /dev/null +++ b/llvm_mode/README.instrument_list.md @@ -0,0 +1,86 @@ +# Using afl++ with partial instrumentation + + This file describes how you can selectively instrument only the source files + or functions that are interesting to you using the LLVM instrumentation + provided by afl++ + +## 1) Description and purpose + +When building and testing complex programs where only a part of the program is +the fuzzing target, it often helps to only instrument the necessary parts of +the program, leaving the rest uninstrumented. This helps to focus the fuzzer +on the important parts of the program, avoiding undesired noise and +disturbance by uninteresting code being exercised. + +For this purpose, a "partial instrumentation" support en par with llvm sancov +is provided by afl++ that allows you to specify on a source file and function +level which function should be compiled with or without instrumentation. + +Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: +https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation + +The llvm sancov list format is fully supported by afl++, however afl++ has +more flexibility. + +## 2) Building the LLVM module + +The new code is part of the existing afl++ LLVM module in the llvm_mode/ +subdirectory. There is nothing specifically to do :) + +## 3) How to use the partial instrumentation mode + +In order to build with partial instrumentation, you need to build with +afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. +The only required change is that you need to set either the environment variable +AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename. + +That file then contains the filenames or functions that should be instrumented +(AFL_LLVM_ALLOWLIST) or should specifically NOT be instrumented (AFL_LLVM_DENYLIST). + +For matching, the function/filename that is being compiled must end in the +function/filename entry contained in this instrument file list (to avoid +breaking the matching when absolute paths are used during compilation). + +**NOTE:** In builds with optimization enabled functions might be inlined and would not match! + +For example if your source tree looks like this: +``` +project/ +project/feature_a/a1.cpp +project/feature_a/a2.cpp +project/feature_b/b1.cpp +project/feature_b/b2.cpp +``` + +and you only want to test feature_a, then create a instrument file list file containing: +``` +feature_a/a1.cpp +feature_a/a2.cpp +``` + +However if the instrument file list file contains only this, it works as well: +``` +a1.cpp +a2.cpp +``` +but it might lead to files being unwantedly instrumented if the same filename +exists somewhere else in the project directories. + +You can also specify function names. Note that for C++ the function names +must be mangled to match! + +afl++ is able to identify if an entry is a filename or a function. +However if you want to be sure (and compliant to the sancov allow/blocklist +format), you can specify source file entries like this: +``` +src: *malloc.c +``` +and function entries like this: +``` +fun: MallocFoo +``` +Note that whitespace is ignored and comments (`# foo`) are supported. + +## 4) UNIX-style pattern matching +You can add UNIX-style pattern matching in the the instrument file list entries. +See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index e521ac82..9046c5a8 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -17,9 +17,6 @@ This version requires a current llvm 11+ compiled from the github master. 5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. -6. If a target uses _init functions or early constructors then additionally - set `AFL_LLVM_MAP_DYNAMIC=1` as your target will crash otherwise! - ## Introduction and problem description A big issue with how afl/afl++ works is that the basic block IDs that are @@ -108,15 +105,12 @@ make install Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc. -Also the instrument file listing (AFL_LLVM_INSTRUMENT_FILE -> [README.instrument_file.md](README.instrument_file.md)) and +Also the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> [README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. -InsTrim (control flow graph instrumentation) is supported and recommended! - (set `AFL_LLVM_INSTRUMENT=CFG`) Example: ``` CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure -export AFL_LLVM_INSTRUMENT=CFG make ``` @@ -131,14 +125,14 @@ on start. This improves coverage statistically by 5-10% :) ## Fixed memory map -To speed up fuzzing, the shared memory map is hard set to a specific address, -by default 0x10000. In most cases this will work without any problems. +To speed up fuzzing, it is possible to set a fixed shared memory map. +Recommened is the value 0x10000. +In most cases this will work without any problems. However if a target uses +early constructors, ifuncs or a deferred forkserver this can crash the target. On unusual operating systems/processors/kernels or weird libraries this might fail so to change the fixed address at compile time set AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address to be dynamic - the original afl way, which is slower). -AFL_LLVM_MAP_DYNAMIC can be set so the shared memory address is dynamic (which -is safer but also slower). ## Document edge IDs @@ -265,15 +259,6 @@ If this succeeeds then there is an issue with afl-clang-lto. Please report at Even some targets where clang-12 fails can be build if the fail is just in `./configure`, see `Solving difficult targets` above. -### Target crashes immediately - -If the target is using early constructors (priority values smaller than 6) -or have their own _init/.init functions and these are instrumented then the -target will likely crash when started. This can be avoided by compiling with -`AFL_LLVM_MAP_DYNAMIC=1` . - -This can e.g. happen with OpenSSL. - ## History This was originally envisioned by hexcoder- in Summer 2019, however we saw no diff --git a/llvm_mode/README.md b/llvm_mode/README.md index 22088dfd..f23d7150 100644 --- a/llvm_mode/README.md +++ b/llvm_mode/README.md @@ -109,7 +109,7 @@ Several options are present to make llvm_mode faster or help it rearrange the code to make afl-fuzz path discovery easier. If you need just to instrument specific parts of the code, you can the instrument file list -which C/C++ files to actually instrument. See [README.instrument_file](README.instrument_file.md) +which C/C++ files to actually instrument. See [README.instrument_list](README.instrument_list.md) For splitting memcmp, strncmp, etc. please see [README.laf-intel](README.laf-intel.md) diff --git a/llvm_mode/README.persistent_mode.md b/llvm_mode/README.persistent_mode.md index 4f0bcb2e..7d2fd93b 100644 --- a/llvm_mode/README.persistent_mode.md +++ b/llvm_mode/README.persistent_mode.md @@ -115,6 +115,33 @@ will keep working normally when compiled with a tool other than afl-clang-fast. Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will *not* generate a deferred-initialization binary) - and you should be all set! +*NOTE:* In the code between `main` and `__AFL_INIT()` should not be any code +run that is instrumented - otherwise a crash might occure. +In case this is useful (e.g. for expensive one time initialization) you can +try to do the following: + +Add after the includes: +``` +extern unsigned char *__afl_area_ptr; +#define MAX_DUMMY_SIZE 256000 + +__attribute__((constructor(1))) void __afl_protect(void) { +#ifdef MAP_FIXED_NOREPLACE + __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if ((uint64_t)__afl_area_ptr == -1) +#endif + __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if ((uint64_t)__afl_area_ptr == -1) + __afl_area_ptr = (unsigned char*) mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); +} + +``` +and just before `__AFL_INIT()`: +``` + munmap(__afl_area_ptr, MAX_DUMMY_SIZE); + __afl_area_ptr = NULL; +``` + ## 4) persistent mode Some libraries provide APIs that are stateless, or whose state can be reset in diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index ef99e3f3..72b81922 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -161,7 +161,8 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { - u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0, + preprocessor_only = 0; u8 have_pic = 0; u8 *name; @@ -229,7 +230,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode) { if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST")) { + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; @@ -244,33 +246,60 @@ static void edit_params(u32 argc, char **argv, char **envp) { // laf if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + } } if (getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/compare-transform-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/compare-transform-pass.so", obj_path); + + } } if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-compares-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-compares-pass.so", obj_path); + + } } @@ -280,24 +309,37 @@ static void edit_params(u32 argc, char **argv, char **envp) { unsetenv("AFL_LD_CALLER"); if (cmplog_mode) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-routines-pass.so", obj_path); + if (lto_mode) { - // reuse split switches from laf - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path); - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-routines-pass.so", obj_path); + + // reuse split switches from laf + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + + } cc_params[cc_par_cnt++] = "-fno-inline"; @@ -310,8 +352,17 @@ static void edit_params(u32 argc, char **argv, char **envp) { else setenv("AFL_LLVM_LTO_AUTODICTIONARY", "1", 1); +#if defined(AFL_CLANG_LDPATH) && LLVM_VERSION_MAJOR >= 12 + u8 *ld_ptr = strrchr(AFL_REAL_LD, '/'); + if (!ld_ptr) ld_ptr = "ld.lld"; + cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_ptr); + cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", AFL_REAL_LD); +#else cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD); +#endif + cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; + /* The current LTO instrim mode is not good, so we disable it if (instrument_mode == INSTRUMENT_CFG) @@ -319,6 +370,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so", obj_path); else */ + cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); cc_params[cc_par_cnt++] = lto_flag; @@ -327,8 +379,14 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { +#if LLVM_VERSION_MAJOR > 4 || \ + (LLVM_VERSION_MAJOR == 4 && \ + (LLVM_VERSION_MINOR > 0 || LLVM_VERSION_PATCH >= 1)) cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default +#else + FATAL("pcguard instrumentation requires llvm 4.0.1+"); +#endif } else { @@ -399,6 +457,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; + if (!strcmp(cur, "-E")) preprocessor_only = 1; + if (!strcmp(cur, "-shared")) shared_linking = 1; + cc_params[cc_par_cnt++] = cur; } @@ -563,6 +624,18 @@ static void edit_params(u32 argc, char **argv, char **envp) { } + if (preprocessor_only) { + + /* In the preprocessor_only case (-E), we are not actually compiling at + all but requesting the compiler to output preprocessed sources only. + We must not add the runtime in this case because the compiler will + simply output its binary content back on stdout, breaking any build + systems that rely on a separate source preprocessing step. */ + cc_params[cc_par_cnt] = NULL; + return; + + } + #ifndef __ANDROID__ switch (bit_mode) { @@ -605,6 +678,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { } + if (!shared_linking) + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); + #endif cc_params[cc_par_cnt] = NULL; @@ -637,9 +714,13 @@ int main(int argc, char **argv, char **envp) { } - if ((getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) && + if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && getenv("AFL_DONT_OPTIMIZE")) - FATAL("AFL_LLVM_INSTRUMENT_FILE and AFL_DONT_OPTIMIZE cannot be combined"); + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || getenv("INSTRIM_LIB")) { @@ -787,15 +868,17 @@ int main(int argc, char **argv, char **envp) { #if LLVM_VERSION_MAJOR <= 6 instrument_mode = INSTRUMENT_AFL; #else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) { + if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { instrument_mode = INSTRUMENT_AFL; WARNF( "switching to classic instrumentation because " - "AFL_LLVM_INSTRUMENT_FILE does not work with PCGUARD. Use " - "-fsanitize-coverage-allowlist=allowlist.txt if you want to use " - "PCGUARD. Requires llvm 12+. See " - "https://clang.llvm.org/docs/" + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt if you want to use " + "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " "SanitizerCoverage.html#partially-disabling-instrumentation"); } else @@ -846,11 +929,14 @@ int main(int argc, char **argv, char **envp) { "together"); if (instrument_mode == INSTRUMENT_PCGUARD && - (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST"))) + (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) FATAL( "Instrumentation type PCGUARD does not support " - "AFL_LLVM_INSTRUMENT_FILE! Use " - "-fsanitize-coverage-allowlist=allowlist.txt instead (requires llvm " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " "12+), see " "https://clang.llvm.org/docs/" "SanitizerCoverage.html#partially-disabling-instrumentation"); @@ -892,8 +978,11 @@ int main(int argc, char **argv, char **envp) { "AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" "AFL_HARDEN: adds code hardening to catch memory bugs\n" "AFL_INST_RATIO: percentage of branches to instrument\n" +#if LLVM_VERSION_MAJOR < 9 "AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" +#else "AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" +#endif "AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" "AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" "AFL_LLVM_LAF_SPLIT_SWITCHES: casc. comp. in 'switch'\n" @@ -935,6 +1024,10 @@ int main(int argc, char **argv, char **envp) { #ifdef AFL_CLANG_FLTO SAYF( "\nafl-clang-lto specific environment variables:\n" + "AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " + "0x10000\n" + "AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " + "functions they are in into this file\n" "AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " "global var\n" "AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " diff --git a/llvm_mode/afl-ld-lto.c b/llvm_mode/afl-ld-lto.c index 1b59bb4a..771e2d0d 100644 --- a/llvm_mode/afl-ld-lto.c +++ b/llvm_mode/afl-ld-lto.c @@ -278,7 +278,7 @@ int main(int argc, char **argv) { if (debug) { - (void)getcwd(thecwd, sizeof(thecwd)); + if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, "."); SAYF(cMGN "[D] " cRST "cd \"%s\";", thecwd); for (i = 0; i < argc; i++) diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 9a884ded..d9e63bd3 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -20,7 +20,10 @@ using namespace llvm; -static std::list<std::string> myInstrumentList; +static std::list<std::string> allowListFiles; +static std::list<std::string> allowListFunctions; +static std::list<std::string> denyListFiles; +static std::list<std::string> denyListFunctions; char *getBBName(const llvm::BasicBlock *BB) { @@ -57,15 +60,18 @@ bool isIgnoreFunction(const llvm::Function *F) { "asan.", "llvm.", "sancov.", - "__ubsan_handle_", + "__ubsan_", "ign.", "__afl_", "_fini", "__libc_csu", "__asan", "__msan", + "__cmplog", + "__sancov", "msan.", "LLVMFuzzer", + "__decide_deferred", "maybe_duplicate_stderr", "discard_output", "close_stdout", @@ -87,41 +93,246 @@ bool isIgnoreFunction(const llvm::Function *F) { void initInstrumentList() { - char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!instrumentListFilename) - instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); + char *allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); - if (instrumentListFilename) { + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST " + "but not both!"); + + if (allowlist) { std::string line; std::ifstream fileStream; - fileStream.open(instrumentListFilename); - if (!fileStream) - report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE"); + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_ALLOWLIST"); getline(fileStream, line); + while (fileStream) { - myInstrumentList.push_back(line); - getline(fileStream, line); + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_ALLOWLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); + + } } + if (debug) + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); + } - if (debug) - SAYF(cMGN "[D] " cRST "loaded instrument list with %zu entries\n", - myInstrumentList.size()); + if (denylist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_DENYLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_DENYLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); + + } + + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } } -bool isInInstrumentList(llvm::Function *F) { +void scanForDangerousFunctions(llvm::Module *M) { - // is this a function with code? If it is external we dont instrument it - // anyway and cant be in the the instrument file list. Or if it is ignored. - if (!F->size() || isIgnoreFunction(F)) return false; + if (!M) return; + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 9) - // if we do not have a the instrument file list return true - if (myInstrumentList.empty()) return true; + for (GlobalIFunc &IF : M->ifuncs()) { + + StringRef ifunc_name = IF.getName(); + Constant *r = IF.getResolver(); + StringRef r_name = cast<Function>(r->getOperand(0))->getName(); + if (!be_quiet) + fprintf(stderr, + "Info: Found an ifunc with name %s that points to resolver " + "function %s, we will not instrument this, putting it into the " + "block list.\n", + ifunc_name.str().c_str(), r_name.str().c_str()); + denyListFunctions.push_back(r_name.str()); + + } + + GlobalVariable *GV = M->getNamedGlobal("llvm.global_ctors"); + if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { + + ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer()); + + if (InitList) { + + for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { + + if (ConstantStruct *CS = + dyn_cast<ConstantStruct>(InitList->getOperand(i))) { + + if (CS->getNumOperands() >= 2) { + + if (CS->getOperand(1)->isNullValue()) + break; // Found a null terminator, stop here. + + ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0)); + int Priority = CI ? CI->getSExtValue() : 0; + + Constant *FP = CS->getOperand(1); + if (ConstantExpr *CE = dyn_cast<ConstantExpr>(FP)) + if (CE->isCast()) FP = CE->getOperand(0); + if (Function *F = dyn_cast<Function>(FP)) { + + if (!F->isDeclaration() && + strncmp(F->getName().str().c_str(), "__afl", 5) != 0) { + + if (!be_quiet) + fprintf(stderr, + "Info: Found constructor function %s with prio " + "%u, we will not instrument this, putting it into a " + "block list.\n", + F->getName().str().c_str(), Priority); + denyListFunctions.push_back(F->getName().str()); + + } + + } + + } + + } + + } + + } + + } + +#endif + +} + +static std::string getSourceName(llvm::Function *F) { // let's try to get the filename for the function auto bb = &F->getEntryBlock(); @@ -135,28 +346,56 @@ bool isInInstrumentList(llvm::Function *F) { DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); + StringRef instFilename = cDILoc->getFilename(); if (instFilename.str().empty()) { /* If the original location is empty, try using the inlined location */ DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { + if (oDILoc) { instFilename = oDILoc->getFilename(); } - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); + } - } + return instFilename.str(); - } + } + +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + StringRef instFilename = cDILoc.getFilename(); /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { + return instFilename.str(); - for (std::list<std::string>::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + } + +#endif + + return std::string(""); + +} + +bool isInInstrumentList(llvm::Function *F) { + + bool return_default = true; + + // is this a function with code? If it is external we don't instrument it + // anyway and it can't be in the instrument file list. Or if it is it is + // ignored. + if (!F->size() || isIgnoreFunction(F)) return false; + + if (!denyListFiles.empty() || !denyListFunctions.empty()) { + + if (!denyListFunctions.empty()) { + + std::string instFunction = F->getName().str(); + + for (std::list<std::string>::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -164,16 +403,16 @@ bool isInInstrumentList(llvm::Function *F) { * specified in the list. We also allow UNIX-style pattern * matching */ - if (instFilename.str().length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the list (%s), instrumenting ... \n", - F->getName().str().c_str(), instFilename.str().c_str()); - return true; + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; } @@ -183,22 +422,60 @@ bool isInInstrumentList(llvm::Function *F) { } + if (!denyListFiles.empty()) { + + std::string source_file = getSourceName(F); + + if (!source_file.empty()) { + + for (std::list<std::string>::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (source_file.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + + return false; + + } + + } + + } + + } else { + + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + + } + + } + } -#else - if (!Loc.isUnknown()) { + // if we do not have a instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { - DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + return_default = false; - unsigned int instLine = cDILoc.getLineNumber(); - StringRef instFilename = cDILoc.getFilename(); + if (!allowListFunctions.empty()) { - (void)instLine; - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { + std::string instFunction = F->getName().str(); - for (std::list<std::string>::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + for (std::list<std::string>::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -206,11 +483,15 @@ bool isInInstrumentList(llvm::Function *F) { * specified in the list. We also allow UNIX-style pattern * matching */ - if (instFilename.str().length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); return true; } @@ -221,23 +502,56 @@ bool isInInstrumentList(llvm::Function *F) { } - } + if (!allowListFiles.empty()) { -#endif - else { + std::string source_file = getSourceName(F); - // we could not find out the location. in this case we say it is not - // in the the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return false; + if (!source_file.empty()) { + + for (std::list<std::string>::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (source_file.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + F->getName().str().c_str(), source_file.c_str()); + return true; + + } + + } + + } + + } else { + + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return false; + + } + + } } - return false; + return return_default; } diff --git a/llvm_mode/afl-llvm-common.h b/llvm_mode/afl-llvm-common.h index 5b96be43..a1561d9c 100644 --- a/llvm_mode/afl-llvm-common.h +++ b/llvm_mode/afl-llvm-common.h @@ -37,6 +37,7 @@ bool isIgnoreFunction(const llvm::Function *F); void initInstrumentList(); bool isInInstrumentList(llvm::Function *F); unsigned long long int calculateCollisions(uint32_t edges); +void scanForDangerousFunctions(llvm::Module *M); #ifndef IS_EXTERN #define IS_EXTERN diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 38c3f202..10482705 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -49,6 +49,7 @@ #include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Pass.h" +#include "llvm/IR/Constants.h" #include "afl-llvm-common.h" @@ -102,6 +103,7 @@ bool AFLLTOPass::runOnModule(Module &M) { std::vector<std::string> dictionary; std::vector<CallInst *> calls; DenseMap<Value *, std::string *> valueMap; + std::vector<BasicBlock *> BlockList; char * ptr; FILE * documentFile = NULL; @@ -135,7 +137,10 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_LTO_AUTODICTIONARY")) autodictionary = 1; - if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; + // we make this the default as the fixed map has problems with + // defered forkserver, early constructors, ifuncs and maybe more + /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ + map_addr = 0; if (getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; @@ -146,7 +151,7 @@ bool AFLLTOPass::runOnModule(Module &M) { map_addr = 0; - } else if (map_addr == 0) { + } else if (getenv("AFL_LLVM_MAP_DYNAMIC")) { FATAL( "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used together"); @@ -196,7 +201,8 @@ bool AFLLTOPass::runOnModule(Module &M) { ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - /* This dumps all inialized global strings - might be useful in the future + // This dumps all inialized global strings - might be useful in the future + /* for (auto G=M.getGlobalList().begin(); G!=M.getGlobalList().end(); G++) { GlobalVariable &GV=*G; @@ -214,14 +220,20 @@ bool AFLLTOPass::runOnModule(Module &M) { */ + scanForDangerousFunctions(&M); + /* Instrument all the things! */ int inst_blocks = 0; for (auto &F : M) { - // fprintf(stderr, "DEBUG: Module %s Function %s\n", - // M.getName().str().c_str(), F.getName().str().c_str()); + /*For debugging + AttributeSet X = F.getAttributes().getFnAttributes(); + fprintf(stderr, "DEBUG: Module %s Function %s attributes %u\n", + M.getName().str().c_str(), F.getName().str().c_str(), + X.getNumAttributes()); + */ if (F.size() < function_minimum_size) continue; if (isIgnoreFunction(&F)) continue; @@ -279,14 +291,14 @@ bool AFLLTOPass::runOnModule(Module &M) { if ((callInst = dyn_cast<CallInst>(&IN))) { - bool isStrcmp = true; - bool isMemcmp = true; - bool isStrncmp = true; - bool isStrcasecmp = true; - bool isStrncasecmp = true; - bool isIntMemcpy = true; - bool addedNull = false; - uint8_t optLen = 0; + bool isStrcmp = true; + bool isMemcmp = true; + bool isStrncmp = true; + bool isStrcasecmp = true; + bool isStrncasecmp = true; + bool isIntMemcpy = true; + bool addedNull = false; + size_t optLen = 0; Function *Callee = callInst->getCalledFunction(); if (!Callee) continue; @@ -299,6 +311,24 @@ bool AFLLTOPass::runOnModule(Module &M) { isStrncasecmp &= !FuncName.compare("strncasecmp"); isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + /* we do something different here, putting this BB and the + successors in a block map */ + if (!FuncName.compare("__afl_persistent_loop")) { + + BlockList.push_back(&BB); + /* + for (succ_iterator SI = succ_begin(&BB), SE = + succ_end(&BB); SI != SE; ++SI) { + + BasicBlock *succ = *SI; + BlockList.push_back(succ); + + } + + */ + + } + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp && !isIntMemcpy) continue; @@ -516,18 +546,27 @@ bool AFLLTOPass::runOnModule(Module &M) { // add null byte if this is a string compare function and a null // was not already added - if (addedNull == false && !isMemcmp) { + if (!isMemcmp) { - thestring.append("\0", 1); // add null byte - optLen++; + if (addedNull == false) { + + thestring.append("\0", 1); // add null byte + optLen++; + + } + + // ensure we do not have garbage + size_t offset = thestring.find('\0', 0); + if (offset + 1 < optLen) optLen = offset + 1; + thestring = thestring.substr(0, optLen); } if (!be_quiet) { std::string outstring; - fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen, - (unsigned int)thestring.length()); + fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, + thestring.length()); for (uint8_t i = 0; i < thestring.length(); i++) { uint8_t c = thestring[i]; @@ -563,17 +602,41 @@ bool AFLLTOPass::runOnModule(Module &M) { for (auto &BB : F) { - uint32_t succ = 0; + if (F.size() == 1) { + + InsBlocks.push_back(&BB); + continue; - if (F.size() == 1) InsBlocks.push_back(&BB); + } + uint32_t succ = 0; for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE; ++SI) if ((*SI)->size() > 0) succ++; - if (succ < 2) // no need to instrument continue; + if (BlockList.size()) { + + int skip = 0; + for (uint32_t k = 0; k < BlockList.size(); k++) { + + if (origBB == BlockList[k]) { + + if (debug) + fprintf(stderr, + "DEBUG: Function %s skipping BB with/after __afl_loop\n", + F.getName().str().c_str()); + skip = 1; + + } + + } + + if (skip) continue; + + } + InsBlocks.push_back(&BB); } @@ -585,7 +648,7 @@ bool AFLLTOPass::runOnModule(Module &M) { do { --i; - BasicBlock * newBB; + BasicBlock * newBB = NULL; BasicBlock * origBB = &(*InsBlocks[i]); std::vector<BasicBlock *> Successors; Instruction * TI = origBB->getTerminator(); diff --git a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc index ab7c0c58..a7331444 100644 --- a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc @@ -59,39 +59,9 @@ class AFLcheckIfInstrument : public ModulePass { static char ID; AFLcheckIfInstrument() : ModulePass(ID) { - int entries = 0; - if (getenv("AFL_DEBUG")) debug = 1; - char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!instrumentListFilename) - instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); - if (instrumentListFilename) { - - std::string line; - std::ifstream fileStream; - fileStream.open(instrumentListFilename); - if (!fileStream) - report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE"); - getline(fileStream, line); - while (fileStream) { - - myInstrumentList.push_back(line); - getline(fileStream, line); - entries++; - - } - - } else - - PFATAL( - "afl-llvm-lto-instrumentlist.so loaded without " - "AFL_LLVM_INSTRUMENT_FILE?!"); - - if (debug) - SAYF(cMGN "[D] " cRST - "loaded the instrument file list %s with %d entries\n", - instrumentListFilename, entries); + initInstrumentList(); } @@ -129,120 +99,28 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { for (auto &F : M) { if (F.size() < 1) continue; - // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - if (isIgnoreFunction(&F)) continue; - - BasicBlock::iterator IP = F.getEntryBlock().getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - if (!myInstrumentList.empty()) { - - bool instrumentFunction = false; - - /* Get the current location using debug information. - * For now, just instrument the block if we are not able - * to determine our location. */ - DebugLoc Loc = IP->getDebugLoc(); - if (Loc) { - - DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); - - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); - - if (instFilename.str().empty()) { - - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { - - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); - - } - - if (instFilename.str().empty()) { - if (!be_quiet) - WARNF( - "Function %s has no source file name information and will " - "not be instrumented.", - F.getName().str().c_str()); - continue; - - } - - } - - //(void)instLine; - - fprintf(stderr, "xxx %s %s\n", F.getName().str().c_str(), - instFilename.str().c_str()); - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in file %s\n", - F.getName().str().c_str(), instFilename.str().c_str()); - - for (std::list<std::string>::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. */ - if (instFilename.str().length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { - - instrumentFunction = true; - break; - - } - - } - - } - - } else { - - if (!be_quiet) - WARNF( - "No debug information found for function %s, recompile with -g " - "-O[1-3]", - F.getName().str().c_str()); - continue; - - } - - /* Either we couldn't figure out our location or the location is - * not the instrument file listed, so we skip instrumentation. - * We do this by renaming the function. */ - if (instrumentFunction == true) { - - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", - F.getName().str().c_str()); - - } else { - - if (debug) - SAYF(cMGN "[D] " cRST - "function %s is NOT in the instrument file list\n", - F.getName().str().c_str()); + // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - auto & Ctx = F.getContext(); - AttributeList Attrs = F.getAttributes(); - AttrBuilder NewAttrs; - NewAttrs.addAttribute("skipinstrument"); - F.setAttributes( - Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); + if (isInInstrumentList(&F)) { - } + if (debug) + SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", + F.getName().str().c_str()); } else { - PFATAL("InstrumentList is empty"); + if (debug) + SAYF(cMGN "[D] " cRST + "function %s is NOT in the instrument file list\n", + F.getName().str().c_str()); + + auto & Ctx = F.getContext(); + AttributeList Attrs = F.getAttributes(); + AttrBuilder NewAttrs; + NewAttrs.addAttribute("skipinstrument"); + F.setAttributes( + Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); } diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 618abe48..0206080f 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -112,7 +112,7 @@ uint64_t PowerOf2Ceil(unsigned in) { #endif /* #if LLVM_VERSION_STRING >= "4.0.1" */ -#if LLVM_VERSION_MAJOR >= 4 || \ +#if LLVM_VERSION_MAJOR > 4 || \ (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) #define AFL_HAVE_VECTOR_INTRINSICS 1 #endif @@ -194,7 +194,7 @@ bool AFLCoverage::runOnModule(Module &M) { #ifdef AFL_HAVE_VECTOR_INTRINSICS /* Decide previous location vector size (must be a power of two) */ - VectorType *PrevLocTy; + VectorType *PrevLocTy = NULL; if (ngram_size_str) if (sscanf(ngram_size_str, "%u", &ngram_size) != 1 || ngram_size < 2 || @@ -225,8 +225,14 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocSize = 1; #ifdef AFL_HAVE_VECTOR_INTRINSICS - uint64_t PrevLocVecSize = PowerOf2Ceil(PrevLocSize); - if (ngram_size) PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize); + int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); + if (ngram_size) + PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize + #if LLVM_VERSION_MAJOR >= 12 + , + false + #endif + ); #endif /* Get globals for the SHM region and the previous location. Note that @@ -236,7 +242,7 @@ bool AFLCoverage::runOnModule(Module &M) { new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc; - GlobalVariable *AFLContext; + GlobalVariable *AFLContext = NULL; if (ctx_str) #ifdef __ANDROID__ @@ -292,11 +298,12 @@ bool AFLCoverage::runOnModule(Module &M) { ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - LoadInst *PrevCtx; // CTX sensitive coverage + LoadInst *PrevCtx = NULL; // CTX sensitive coverage /* Instrument all the things! */ int inst_blocks = 0; + scanForDangerousFunctions(&M); for (auto &F : M) { diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index fcacc675..d00fd26f 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -35,6 +35,8 @@ #include <string.h> #include <assert.h> #include <stdint.h> +#include <stddef.h> +#include <limits.h> #include <errno.h> #include <sys/mman.h> @@ -42,6 +44,8 @@ #include <sys/wait.h> #include <sys/types.h> +#include "llvm/Config/llvm-config.h" + #ifdef __linux__ #include "snapshot-inl.h" #endif @@ -50,8 +54,6 @@ Basically, we need to make sure that the forkserver is initialized after the LLVM-generated runtime initialization pass, not before. */ -#define CONST_PRIO 5 - #ifndef MAP_FIXED_NOREPLACE #ifdef MAP_EXCL #define MAP_FIXED_NOREPLACE MAP_EXCL | MAP_FIXED @@ -60,6 +62,8 @@ #endif #endif +#define CTOR_PRIO 3 + #include <sys/mman.h> #include <fcntl.h> @@ -73,11 +77,7 @@ #define MAP_INITIAL_SIZE MAP_SIZE #endif -#ifdef AFL_REAL_LD -u8 __afl_area_initial[MAP_INITIAL_SIZE]; -#else -u8 __afl_area_initial[MAP_SIZE]; -#endif +u8 __afl_area_initial[MAP_INITIAL_SIZE]; u8 * __afl_area_ptr = __afl_area_initial; u8 * __afl_dictionary; u8 * __afl_fuzz_ptr; @@ -107,6 +107,10 @@ struct cmp_map *__afl_cmp_map; static u8 is_persistent; +/* Are we in sancov mode? */ + +static u8 _is_sancov; + /* Error reporting to forkserver controller */ void send_forkserver_error(int error) { @@ -183,14 +187,17 @@ static void __afl_map_shm_fuzz() { static void __afl_map_shm(void) { + // we we are not running in afl ensure the map exists + if (!__afl_area_ptr) { __afl_area_ptr = __afl_area_initial; } + char *id_str = getenv(SHM_ENV_VAR); if (__afl_final_loc) { if (__afl_final_loc % 8) __afl_final_loc = (((__afl_final_loc + 7) >> 3) << 3); - __afl_map_size = __afl_final_loc; + if (__afl_final_loc > MAP_SIZE) { char *ptr; @@ -200,10 +207,12 @@ static void __afl_map_shm(void) { if (__afl_final_loc > FS_OPT_MAX_MAPSIZE) { - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_final_loc); + if (!getenv("AFL_QUIET")) + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u " + "to be able to run this instrumented program!\n", + __afl_final_loc); + if (id_str) { send_forkserver_error(FS_ERROR_MAP_SIZE); @@ -213,10 +222,11 @@ static void __afl_map_shm(void) { } else { - fprintf(stderr, - "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_final_loc); + if (!getenv("AFL_QUIET")) + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u " + "to be able to run this instrumented program!\n", + __afl_final_loc); } @@ -232,13 +242,25 @@ static void __afl_map_shm(void) { if (getenv("AFL_DEBUG")) fprintf(stderr, - "DEBUG: id_str %s, __afl_map_addr 0x%llx, MAP_SIZE %u, " - "__afl_final_loc %u, max_size_forkserver %u/0x%x\n", - id_str == NULL ? "<null>" : id_str, __afl_map_addr, MAP_SIZE, - __afl_final_loc, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); + "DEBUG: id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " + "__afl_map_addr 0x%llx, MAP_SIZE %u, __afl_final_loc %u, " + "max_size_forkserver %u/0x%x\n", + id_str == NULL ? "<null>" : id_str, __afl_area_ptr, + __afl_area_initial, __afl_map_addr, MAP_SIZE, __afl_final_loc, + FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE); if (id_str) { + if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) { + + if (__afl_map_addr) + munmap((void *)__afl_map_addr, __afl_final_loc); + else + free(__afl_area_ptr); + __afl_area_ptr = __afl_area_initial; + + } + #ifdef USEMMAP const char * shm_file_path = id_str; int shm_fd = -1; @@ -307,11 +329,14 @@ static void __afl_map_shm(void) { __afl_area_ptr[0] = 1; - } else if (__afl_map_addr) { + } else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) && + + __afl_map_addr) { __afl_area_ptr = mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (__afl_area_ptr == MAP_FAILED) { fprintf(stderr, "can not aquire mmap for address %p\n", @@ -320,14 +345,25 @@ static void __afl_map_shm(void) { } + } else if (_is_sancov && __afl_area_ptr != __afl_area_initial) { + + free(__afl_area_ptr); + __afl_area_ptr = NULL; + if (__afl_final_loc > MAP_INITIAL_SIZE) + __afl_area_ptr = malloc(__afl_final_loc); + if (!__afl_area_ptr) __afl_area_ptr = __afl_area_initial; + } id_str = getenv(CMPLOG_SHM_ENV_VAR); - if (getenv("AFL_DEBUG")) + if (getenv("AFL_DEBUG")) { + fprintf(stderr, "DEBUG: cmplog id_str %s\n", id_str == NULL ? "<null>" : id_str); + } + if (id_str) { #ifdef USEMMAP @@ -399,9 +435,12 @@ static void __afl_start_snapshots(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - if (getenv("AFL_DEBUG")) + if (getenv("AFL_DEBUG")) { + fprintf(stderr, "target forkserver recv: %08x\n", was_killed); + } + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { @@ -608,9 +647,12 @@ static void __afl_start_forkserver(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - if (getenv("AFL_DEBUG")) + if (getenv("AFL_DEBUG")) { + fprintf(stderr, "target forkserver recv: %08x\n", was_killed); + } + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { @@ -828,9 +870,22 @@ void __afl_manual_init(void) { static u8 init_done; + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { + + init_done = 1; + is_persistent = 0; + __afl_sharedmem_fuzzing = 0; + if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_initial; + + if (getenv("AFL_DEBUG")) + fprintf(stderr, + "DEBUG: disabled instrumentation because of " + "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); + + } + if (!init_done) { - __afl_map_shm(); __afl_start_forkserver(); init_done = 1; @@ -838,11 +893,11 @@ void __afl_manual_init(void) { } -/* Proper initialization routine. */ +/* Initialization of the forkserver - latest possible */ -__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { +__attribute__((constructor())) void __afl_auto_init(void) { - is_persistent = !!getenv(PERSIST_ENV_VAR); + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; if (getenv(DEFER_ENV_VAR)) return; @@ -850,6 +905,57 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { } +/* Initialization of the shmem - earliest possible because of LTO fixed mem. */ + +__attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { + + is_persistent = !!getenv(PERSIST_ENV_VAR); + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + + __afl_map_shm(); + +} + +/* preset __afl_area_ptr #2 */ + +__attribute__((constructor(1))) void __afl_auto_second(void) { + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + u8 *ptr; + + if (__afl_final_loc) { + + if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) + free(__afl_area_ptr); + + if (__afl_map_addr) + ptr = (u8 *)mmap((void *)__afl_map_addr, __afl_final_loc, + PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + else + ptr = (u8 *)malloc(__afl_final_loc); + + if (ptr && (ssize_t)ptr != -1) __afl_area_ptr = ptr; + + } + +} + +/* preset __afl_area_ptr #1 - at constructor level 0 global variables have + not been set */ + +__attribute__((constructor(0))) void __afl_auto_first(void) { + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + u8 *ptr; + + ptr = (u8 *)malloc(1024000); + + if (ptr && (ssize_t)ptr != -1) __afl_area_ptr = ptr; + +} + /* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. It remains non-operational in the traditional, plugin-backed LLVM mode. For more info about 'trace-pc-guard', see llvm_mode/README.md. @@ -860,7 +966,7 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { // For stability analysis, if you want to know to which function unstable - // edge IDs belong to - uncomment, recompile+install llvm_mode, recompile + // edge IDs belong - uncomment, recompile+install llvm_mode, recompile // the target. libunwind and libbacktrace are better solutions. // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture // the backtrace output @@ -892,8 +998,17 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { */ +#if (LLVM_VERSION_MAJOR < 9) + __afl_area_ptr[*guard]++; +#else + + __afl_area_ptr[*guard] = + __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); + +#endif + } /* Init callback. Populates instrumentation IDs. Note that we're using @@ -905,6 +1020,15 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { u32 inst_ratio = 100; char *x; + _is_sancov = 1; + + if (getenv("AFL_DEBUG")) { + + fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", + start, stop); + + } + if (start == stop || *start) return; x = getenv("AFL_INST_RATIO"); @@ -940,7 +1064,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { - if (!__afl_cmp_map) return; + if (unlikely(!__afl_cmp_map)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -963,7 +1087,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { - if (!__afl_cmp_map) return; + if (unlikely(!__afl_cmp_map)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -984,7 +1108,7 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { - if (!__afl_cmp_map) return; + if (unlikely(!__afl_cmp_map)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1005,7 +1129,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { - if (!__afl_cmp_map) return; + if (unlikely(!__afl_cmp_map)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1056,6 +1180,8 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { + if (unlikely(!__afl_cmp_map)) return; + for (uint64_t i = 0; i < cases[0]; i++) { uintptr_t k = (uintptr_t)__builtin_return_address(0) + i; @@ -1093,7 +1219,7 @@ static int area_is_mapped(void *ptr, size_t len) { void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { - if (!__afl_cmp_map) return; + if (unlikely(!__afl_cmp_map)) return; if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; diff --git a/llvm_mode/cmplog-instructions-pass.cc b/llvm_mode/cmplog-instructions-pass.cc index 7c48d906..d5de3dbb 100644 --- a/llvm_mode/cmplog-instructions-pass.cc +++ b/llvm_mode/cmplog-instructions-pass.cc @@ -284,3 +284,9 @@ static RegisterStandardPasses RegisterCmpLogInstructionsPass( static RegisterStandardPasses RegisterCmpLogInstructionsPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogInstructionsPass); +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterCmpLogInstructionsPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogInstructionsPass); +#endif + diff --git a/llvm_mode/cmplog-routines-pass.cc b/llvm_mode/cmplog-routines-pass.cc index a0f8f64f..c44f38c4 100644 --- a/llvm_mode/cmplog-routines-pass.cc +++ b/llvm_mode/cmplog-routines-pass.cc @@ -204,3 +204,9 @@ static RegisterStandardPasses RegisterCmpLogRoutinesPass( static RegisterStandardPasses RegisterCmpLogRoutinesPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogRoutinesPass); +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterCmpLogRoutinesPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogRoutinesPass); +#endif + diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc index bed3597a..acdd0f3b 100644 --- a/llvm_mode/compare-transform-pass.so.cc +++ b/llvm_mode/compare-transform-pass.so.cc @@ -137,7 +137,6 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, bool isStrcasecmp = processStrcasecmp; bool isStrncasecmp = processStrncasecmp; bool isIntMemcpy = true; - bool indirect = false; Function *Callee = callInst->getCalledFunction(); if (!Callee) continue; @@ -264,8 +263,6 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, } - if ((HasStr1 || HasStr2)) indirect = true; - } if (isIntMemcpy) continue; @@ -278,7 +275,6 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, Str1 = StringRef(*val); HasStr1 = true; - indirect = true; // fprintf(stderr, "loaded1 %s\n", Str1.str().c_str()); } else { @@ -288,7 +284,6 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, Str2 = StringRef(*val); HasStr2 = true; - indirect = true; // fprintf(stderr, "loaded2 %s\n", Str2.str().c_str()); } @@ -585,3 +580,8 @@ static RegisterStandardPasses RegisterCompTransPass( static RegisterStandardPasses RegisterCompTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass); +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterCompTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCompTransPass); +#endif + diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc index 3630bd8c..617b55de 100644 --- a/llvm_mode/split-compares-pass.so.cc +++ b/llvm_mode/split-compares-pass.so.cc @@ -1342,3 +1342,9 @@ static RegisterStandardPasses RegisterSplitComparesPass( static RegisterStandardPasses RegisterSplitComparesTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass); +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterSplitComparesTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerSplitComparesPass); +#endif + diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc index f025df77..a79d4114 100644 --- a/llvm_mode/split-switches-pass.so.cc +++ b/llvm_mode/split-switches-pass.so.cc @@ -439,3 +439,9 @@ static RegisterStandardPasses RegisterSplitSwitchesTransPass( static RegisterStandardPasses RegisterSplitSwitchesTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass); +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterSplitSwitchesTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerSplitSwitchesTransPass); +#endif + diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh index a7bfe20d..30ac158f 100755 --- a/qemu_mode/build_qemu_support.sh +++ b/qemu_mode/build_qemu_support.sh @@ -176,6 +176,7 @@ echo Building for CPU target $CPU_TARGET echo "[*] Applying patches..." patch -p1 <../patches/elfload.diff || exit 1 +patch -p1 <../patches/mips-fpu.diff || exit 1 patch -p1 <../patches/bsd-elfload.diff || exit 1 patch -p1 <../patches/cpu-exec.diff || exit 1 patch -p1 <../patches/syscall.diff || exit 1 diff --git a/qemu_mode/patches/mips-fpu.diff b/qemu_mode/patches/mips-fpu.diff new file mode 100644 index 00000000..646d167e --- /dev/null +++ b/qemu_mode/patches/mips-fpu.diff @@ -0,0 +1,15 @@ +--- a/linux-user/elfload.c 2020-07-13 20:10:37.776374566 -0700 ++++ b/linux-user/elfload.c 2020-07-13 20:11:51.794957015 -0700 +@@ -2667,6 +2667,11 @@ + char *elf_interpreter = NULL; + char *scratch; + ++ memset(&interp_info, 0, sizeof(interp_info)); ++#ifdef TARGET_MIPS ++ interp_info.fp_abi = MIPS_ABI_FP_UNKNOWN; ++#endif ++ + info->start_mmap = (abi_ulong)ELF_START_MMAP; + + load_elf_image(bprm->filename, bprm->fd, info, + diff --git a/src/afl-analyze.c b/src/afl-analyze.c index e6dd0fca..7c1c269a 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -384,7 +384,7 @@ static void show_legend(void) { /* Interpret and report a pattern in the input file. */ -static void dump_hex(u8 *buf, u32 len, u8 *b_data) { +static void dump_hex(u32 len, u8 *b_data) { u32 i; @@ -678,7 +678,7 @@ static void analyze(char **argv) { } - dump_hex(in_data, in_len, b_data); + dump_hex(in_len, b_data); SAYF("\n"); @@ -700,6 +700,7 @@ static void analyze(char **argv) { static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; if (child_pid > 0) { kill(child_pid, SIGKILL); } diff --git a/src/afl-as.c b/src/afl-as.c index f16d6060..0ed47d8c 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -136,7 +136,7 @@ static void edit_params(int argc, char **argv) { as_params[argc] = 0; - for (i = 1; i < argc - 1; i++) { + for (i = 1; (s32)i < argc - 1; i++) { if (!strcmp(argv[i], "--64")) { @@ -407,7 +407,7 @@ static void add_instrumentation(void) { if (line[0] == '\t') { - if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) { + if (line[1] == 'j' && line[2] != 'm' && R(100) < (long)inst_ratio) { fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, R(MAP_SIZE)); @@ -449,7 +449,7 @@ static void add_instrumentation(void) { /* Apple: L<num> / LBB<num> */ if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) && - R(100) < inst_ratio) { + R(100) < (long)inst_ratio) { #else @@ -457,7 +457,7 @@ static void add_instrumentation(void) { if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3))) && - R(100) < inst_ratio) { + R(100) < (long)inst_ratio) { #endif /* __APPLE__ */ @@ -591,7 +591,7 @@ int main(int argc, char **argv) { rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); // in fast systems where pids can repeat in the same seconds we need this - for (i = 1; i < argc; i++) + for (i = 1; (s32)i < argc; i++) for (j = 0; j < strlen(argv[i]); j++) rand_seed += argv[i][j]; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 47493eba..8277116b 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -58,6 +58,8 @@ static list_t fsrv_list = {.element_prealloc_count = 0}; static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) { + if (fsrv->qemu_mode) setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); + execv(fsrv->target_path, argv); } @@ -122,8 +124,8 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { Returns the time passed to read. If the wait times out, returns timeout_ms + 1; Returns 0 if an error occurred (fd closed, signal, ...); */ -static u32 read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, - volatile u8 *stop_soon_p) { +static u32 __attribute__((hot)) +read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) { fd_set readfds; FD_ZERO(&readfds); @@ -145,6 +147,13 @@ restart_select: if (likely(sret > 0)) { restart_read: + if (*stop_soon_p) { + + // Early return - the user wants to quit. + return 0; + + } + len_read = read(fd, (u8 *)buf, 4); if (likely(len_read == 4)) { // for speed we put this first @@ -286,8 +295,8 @@ static void report_error_and_exit(int error) { FATAL( "the fuzzing target reports that hardcoded map address might be the " "reason the mmap of the shared memory failed. Solution: recompile " - "the target with either afl-clang-lto and the environment variable " - "AFL_LLVM_MAP_DYNAMIC set or recompile with afl-clang-fast."); + "the target with either afl-clang-lto and do not set " + "AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast."); break; case FS_ERROR_SHM_OPEN: FATAL("the fuzzing target reports that the shm_open() call failed."); @@ -621,13 +630,19 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) { - if (fsrv->function_ptr == NULL || fsrv->function_opt == NULL) { + if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { // this is not afl-fuzz - we deny and return - if (fsrv->use_shmem_fuzz) + if (fsrv->use_shmem_fuzz) { + status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); - else + + } else { + status = (FS_OPT_ENABLED); + + } + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { FATAL("Writing to forkserver failed."); @@ -640,11 +655,16 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!be_quiet) { ACTF("Using AUTODICT feature."); } - if (fsrv->use_shmem_fuzz) + if (fsrv->use_shmem_fuzz) { + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); - else + + } else { + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); + } + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { FATAL("Writing to forkserver failed."); @@ -663,7 +683,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } - u32 len = status, offset = 0, count = 0; + u32 offset = 0, count = 0; + u32 len = status; u8 *dict = ck_alloc(len); if (dict == NULL) { @@ -691,10 +712,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } offset = 0; - while (offset < status && (u8)dict[offset] + offset < status) { + while (offset < (u32)status && + (u8)dict[offset] + offset < (u32)status) { - fsrv->function_ptr(fsrv->function_opt, dict + offset + 1, - (u8)dict[offset]); + fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, + (u8)dict[offset]); offset += (1 + dict[offset]); count++; @@ -820,8 +842,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, SAYF("\n" cLRD "[-] " cRST "Hmm, looks like the target binary terminated before we could" " complete a handshake with the injected code.\n" - "If the target was compiled with afl-clang-lto then recompiling with" - " AFL_LLVM_MAP_DYNAMIC might solve your problem.\n" + "If the target was compiled with afl-clang-lto and AFL_LLVM_MAP_ADDR" + " then recompiling without this parameter.\n" "Otherwise there is a horrible bug in the fuzzer.\n" "Poke <afl-users@googlegroups.com> for troubleshooting tips.\n"); @@ -852,8 +874,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - the target was compiled with afl-clang-lto and a constructor " "was\n" - " instrumented, recompiling with AFL_LLVM_MAP_DYNAMIC might solve " - "your\n" + " instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve " + "your \n" " problem\n\n" " - Less likely, there is a horrible bug in the fuzzer. If other " diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index aa8d5a18..1b9df624 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -57,7 +57,7 @@ void write_bitmap(afl_state_t *afl) { This function is called after every exec() on a fairly large buffer, so it needs to be fast. We do this in 32-bit and 64-bit flavors. */ -u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) { +u8 __attribute__((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) { #ifdef WORD_SIZE_64 @@ -347,7 +347,7 @@ void init_count_class16(void) { #ifdef WORD_SIZE_64 -void classify_counts(afl_forkserver_t *fsrv) { +void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) { u64 *mem = (u64 *)fsrv->trace_bits; @@ -376,7 +376,7 @@ void classify_counts(afl_forkserver_t *fsrv) { #else -void classify_counts(afl_forkserver_t *fsrv) { +void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) { u32 *mem = (u32 *)fsrv->trace_bits; @@ -534,7 +534,8 @@ static void write_crash_readme(afl_state_t *afl) { save or queue the input test case for further analysis if so. Returns 1 if entry is saved, 0 otherwise. */ -u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { +u8 __attribute__((hot)) +save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c index faf4dcb7..8ffc6e1b 100644 --- a/src/afl-fuzz-cmplog.c +++ b/src/afl-fuzz-cmplog.c @@ -29,10 +29,6 @@ #include "afl-fuzz.h" #include "cmplog.h" -typedef struct cmplog_data { - -} cmplog_data_t; - void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv) { setenv("___AFL_EINS_ZWEI_POLIZEI___", "1", 1); diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 12771cd7..094c30b9 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -25,23 +25,28 @@ #include "afl-fuzz.h" -/* Helper function for load_extras. */ +/* helper function for auto_extras qsort */ +static int compare_auto_extras_len(const void *ae1, const void *ae2) { + + return ((struct auto_extra_data *)ae1)->len - + ((struct auto_extra_data *)ae2)->len; + +} -static int compare_extras_len(const void *p1, const void *p2) { +/* descending order */ - struct extra_data *e1 = (struct extra_data *)p1, - *e2 = (struct extra_data *)p2; +static int compare_auto_extras_use_d(const void *ae1, const void *ae2) { - return e1->len - e2->len; + return ((struct auto_extra_data *)ae2)->hit_cnt - + ((struct auto_extra_data *)ae1)->hit_cnt; } -static int compare_extras_use_d(const void *p1, const void *p2) { +/* Helper function for load_extras. */ - struct extra_data *e1 = (struct extra_data *)p1, - *e2 = (struct extra_data *)p2; +static int compare_extras_len(const void *e1, const void *e2) { - return e2->hit_cnt - e1->hit_cnt; + return ((struct extra_data *)e1)->len - ((struct extra_data *)e2)->len; } @@ -115,7 +120,7 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (*lptr == '@') { ++lptr; - if (atoi(lptr) > dict_level) { continue; } + if (atoi(lptr) > (s32)dict_level) { continue; } while (isdigit(*lptr)) { ++lptr; @@ -147,8 +152,10 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, /* Okay, let's allocate memory and copy data between "...", handling \xNN escaping, \\, and \". */ - afl->extras = ck_realloc_block( - afl->extras, (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = + afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (unlikely(!afl->extras)) { PFATAL("alloc"); } wptr = afl->extras[afl->extras_cnt].data = ck_alloc(rptr - lptr); @@ -220,6 +227,36 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, } +static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, + u8 *dir) { + + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; + + if (!afl->extras_cnt) { FATAL("No usable files in '%s'", dir); } + + qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), + compare_extras_len); + + OKF("Loaded %u extra tokens, size range %s to %s.", afl->extras_cnt, + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len)); + + if (max_len > 32) { + + WARNF("Some tokens are relatively large (%s) - consider trimming.", + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), max_len)); + + } + + if (afl->extras_cnt > MAX_DET_EXTRAS) { + + WARNF("More than %d tokens - will use them probabilistically.", + MAX_DET_EXTRAS); + + } + +} + /* Read extras from the extras directory and sort them by size. */ void load_extras(afl_state_t *afl, u8 *dir) { @@ -249,7 +286,8 @@ void load_extras(afl_state_t *afl, u8 *dir) { if (errno == ENOTDIR) { load_extras_file(afl, dir, &min_len, &max_len, dict_level); - goto check_and_sort; + extras_check_and_sort(afl, min_len, max_len, dir); + return; } @@ -291,8 +329,10 @@ void load_extras(afl_state_t *afl, u8 *dir) { if (min_len > st.st_size) { min_len = st.st_size; } if (max_len < st.st_size) { max_len = st.st_size; } - afl->extras = ck_realloc_block( - afl->extras, (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = + afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (unlikely(!afl->extras)) { PFATAL("alloc"); } afl->extras[afl->extras_cnt].data = ck_alloc(st.st_size); afl->extras[afl->extras_cnt].len = st.st_size; @@ -312,56 +352,71 @@ void load_extras(afl_state_t *afl, u8 *dir) { closedir(d); -check_and_sort: + extras_check_and_sort(afl, min_len, max_len, dir); - if (!afl->extras_cnt) { FATAL("No usable files in '%s'", dir); } +} - qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), - compare_extras_len); +/* Helper function for maybe_add_auto(afl, ) */ - OKF("Loaded %u extra tokens, size range %s to %s.", afl->extras_cnt, - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len), - stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len)); +static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { - if (max_len > 32) { + while (len--) { - WARNF("Some tokens are relatively large (%s) - consider trimming.", - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), max_len)); + if (tolower(*(m1++)) ^ tolower(*(m2++))) { return 1; } } - if (afl->extras_cnt > MAX_DET_EXTRAS) { + return 0; - WARNF("More than %d tokens - will use them probabilistically.", - MAX_DET_EXTRAS); +} - } +/* Adds a new extra / dict entry. */ +void add_extra(afl_state_t *afl, u8 *mem, u32 len) { -} + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; -/* Helper function for maybe_add_auto(afl, ) */ + if (len > MAX_DICT_FILE) { -static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { + FATAL("Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); - while (len--) { + } else if (len > 32) { - if (tolower(*(m1++)) ^ tolower(*(m2++))) { return 1; } + WARNF("Extra '%.*s' is pretty large, consider trimming.", (int)len, mem); } - return 0; + afl->extras = afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (unlikely(!afl->extras)) { PFATAL("alloc"); } + + afl->extras[afl->extras_cnt].data = ck_alloc(len); + afl->extras[afl->extras_cnt].len = len; + + memcpy(afl->extras[afl->extras_cnt].data, mem, len); + + afl->extras_cnt++; + + qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), + compare_extras_len); + + /* We only want to print this once */ + + if (afl->extras_cnt == MAX_DET_EXTRAS + 1) { + + WARNF("More than %d tokens - will use them probabilistically.", + MAX_DET_EXTRAS); + + } } /* Maybe add automatic extra. */ -/* Ugly hack: afl state is transfered as u8* because we import data via - afl-forkserver.c - which is shared with other afl tools that do not - have the afl state struct */ -void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { +void maybe_add_auto(afl_state_t *afl, u8 *mem, u32 len) { - afl_state_t *afl = (afl_state_t *)afl_tmp; - u32 i; + u32 i; /* Allow users to specify that they don't want auto dictionaries. */ @@ -375,7 +430,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { } - if (i == len) { return; } + if (i == len || unlikely(len > MAX_AUTO_EXTRA)) { return; } /* Reject builtin interesting values. */ @@ -402,7 +457,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { while (i--) { - if (*((u32 *)mem) == interesting_32[i] || + if (*((u32 *)mem) == (u32)interesting_32[i] || *((u32 *)mem) == SWAP32(interesting_32[i])) { return; @@ -452,10 +507,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { if (afl->a_extras_cnt < MAX_AUTO_EXTRAS) { - afl->a_extras = ck_realloc_block( - afl->a_extras, (afl->a_extras_cnt + 1) * sizeof(struct extra_data)); - - afl->a_extras[afl->a_extras_cnt].data = ck_memdup(mem, len); + memcpy(afl->a_extras[afl->a_extras_cnt].data, mem, len); afl->a_extras[afl->a_extras_cnt].len = len; ++afl->a_extras_cnt; @@ -463,9 +515,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { i = MAX_AUTO_EXTRAS / 2 + rand_below(afl, (MAX_AUTO_EXTRAS + 1) / 2); - ck_free(afl->a_extras[i].data); - - afl->a_extras[i].data = ck_memdup(mem, len); + memcpy(afl->a_extras[i].data, mem, len); afl->a_extras[i].len = len; afl->a_extras[i].hit_cnt = 0; @@ -475,13 +525,13 @@ sort_a_extras: /* First, sort all auto extras by use count, descending order. */ - qsort(afl->a_extras, afl->a_extras_cnt, sizeof(struct extra_data), - compare_extras_use_d); + qsort(afl->a_extras, afl->a_extras_cnt, sizeof(struct auto_extra_data), + compare_auto_extras_use_d); /* Then, sort the top USE_AUTO_EXTRAS entries by size. */ - qsort(afl->a_extras, MIN(USE_AUTO_EXTRAS, afl->a_extras_cnt), - sizeof(struct extra_data), compare_extras_len); + qsort(afl->a_extras, MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt), + sizeof(struct auto_extra_data), compare_auto_extras_len); } @@ -494,7 +544,7 @@ void save_auto(afl_state_t *afl) { if (!afl->auto_changed) { return; } afl->auto_changed = 0; - for (i = 0; i < MIN(USE_AUTO_EXTRAS, afl->a_extras_cnt); ++i) { + for (i = 0; i < MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt); ++i) { u8 *fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", afl->out_dir, i); @@ -544,7 +594,7 @@ void load_auto(afl_state_t *afl) { if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, tmp, len); + maybe_add_auto(afl, tmp, len); } @@ -579,13 +629,5 @@ void destroy_extras(afl_state_t *afl) { ck_free(afl->extras); - for (i = 0; i < afl->a_extras_cnt; ++i) { - - ck_free(afl->a_extras[i].data); - - } - - ck_free(afl->a_extras); - } diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 2c17ffbb..350a8599 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -110,7 +110,7 @@ void bind_to_free_cpu(afl_state_t *afl) { u8 cpu_used[4096] = {0}; u8 lockfile[PATH_MAX] = ""; - u32 i; + s32 i; if (afl->afl_env.afl_no_affinity) { @@ -509,7 +509,7 @@ void read_foreign_testcases(afl_state_t *afl, int first) { afl->stage_cur = 0; afl->stage_max = 0; - for (i = 0; i < nl_cnt; ++i) { + for (i = 0; i < (u32)nl_cnt; ++i) { struct stat st; @@ -667,7 +667,7 @@ void read_testcases(afl_state_t *afl) { } - for (i = 0; i < nl_cnt; ++i) { + for (i = 0; i < (u32)nl_cnt; ++i) { struct stat st; @@ -762,7 +762,7 @@ void perform_dry_run(afl_state_t *afl) { use_mem = ck_alloc_nozero(q->len); - if (read(fd, use_mem, q->len) != q->len) { + if (read(fd, use_mem, q->len) != (ssize_t)q->len) { FATAL("Short read from '%s'", q->fname); @@ -2147,7 +2147,7 @@ void get_core_count(afl_state_t *afl) { WARNF("System under apparent load, performance may be spotty."); - } else if (cur_runnable + 1 <= afl->cpu_core_count) { + } else if ((s64)cur_runnable + 1 <= (s64)afl->cpu_core_count) { OKF("Try parallel jobs - see %s/parallel_fuzzing.md.", doc_path); @@ -2201,6 +2201,7 @@ void fix_up_sync(afl_state_t *afl) { static void handle_resize(int sig) { + (void)sig; afl_states_clear_screen(); } @@ -2252,6 +2253,7 @@ void check_asan_opts(void) { static void handle_stop_sig(int sig) { + (void)sig; afl_states_stop(); } @@ -2260,6 +2262,7 @@ static void handle_stop_sig(int sig) { static void handle_skipreq(int sig) { + (void)sig; afl_states_request_skip(); } diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index b30106a0..22578df9 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -122,9 +122,8 @@ void destroy_custom_mutators(afl_state_t *afl) { if (el->post_process_buf) { - ck_free(el->post_process_buf); + afl_free(el->post_process_buf); el->post_process_buf = NULL; - el->post_process_size = 0; } @@ -142,6 +141,7 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator)); mutator->name = fn; + mutator->name_short = strrchr(fn, '/') + 1; ACTF("Loading custom mutator library from '%s'...", fn); dh = dlopen(fn, RTLD_NOW); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 1f0bf30e..3bf0c195 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -77,7 +77,7 @@ static int select_algorithm(afl_state_t *afl) { static u32 choose_block_len(afl_state_t *afl, u32 limit) { u32 min_value, max_value; - u32 rlim = MIN(afl->queue_cycle, 3); + u32 rlim = MIN(afl->queue_cycle, (u32)3); if (unlikely(!afl->run_over10m)) { rlim = 1; } @@ -95,7 +95,7 @@ static u32 choose_block_len(afl_state_t *afl, u32 limit) { default: - if (rand_below(afl, 10)) { + if (likely(rand_below(afl, 10))) { min_value = HAVOC_BLK_MEDIUM; max_value = HAVOC_BLK_LARGE; @@ -292,7 +292,7 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { /* See if two-byte insertions over old_val could give us new_val. */ - for (i = 0; i < blen - 1; ++i) { + for (i = 0; (s32)i < blen - 1; ++i) { for (j = 0; j < sizeof(interesting_16) / 2; ++j) { @@ -364,15 +364,15 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { #endif /* !IGNORE_FINDS */ -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - /* Take the current entry from the queue, fuzz it for a while. This function is a tad too long... returns 0 if fuzzed successfully, 1 if skipped or bailed out. */ u8 fuzz_one_original(afl_state_t *afl) { - s32 len, fd, temp_len, i, j; + s32 len, fd, temp_len; + u32 j; + u32 i; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; u64 havoc_queued = 0, orig_hit_cnt, new_hit_cnt = 0, prev_cksum; u32 splice_cycle = 0, perf_score = 100, orig_perf, eff_cnt = 1; @@ -382,9 +382,6 @@ u8 fuzz_one_original(afl_state_t *afl) { u8 a_collect[MAX_AUTO_EXTRA]; u32 a_len = 0; -/* Not pretty, but saves a lot of writing */ -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - #ifdef IGNORE_FINDS /* In IGNORE_FINDS mode, skip any entries that weren't in the @@ -419,7 +416,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (((afl->queue_cur->was_fuzzed > 0 || afl->queue_cur->fuzz_level > 0) || !afl->queue_cur->favored) && - rand_below(afl, 100) < SKIP_TO_NEW_PROB) { + likely(rand_below(afl, 100) < SKIP_TO_NEW_PROB)) { return 1; @@ -436,11 +433,11 @@ u8 fuzz_one_original(afl_state_t *afl) { if (afl->queue_cycle > 1 && (afl->queue_cur->fuzz_level == 0 || afl->queue_cur->was_fuzzed)) { - if (rand_below(afl, 100) < SKIP_NFAV_NEW_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_NEW_PROB)) { return 1; } } else { - if (rand_below(afl, 100) < SKIP_NFAV_OLD_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_OLD_PROB)) { return 1; } } @@ -482,7 +479,8 @@ u8 fuzz_one_original(afl_state_t *afl) { single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } afl->subseq_tmouts = 0; @@ -524,8 +522,8 @@ u8 fuzz_one_original(afl_state_t *afl) { * TRIMMING * ************/ - if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done && - !afl->disable_trim) { + if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && + !afl->disable_trim)) { u8 res = trim_case(afl, afl->queue_cur, in_buf); @@ -560,7 +558,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(perf_score == 0)) { goto abandon_entry; } - if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) { + if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { if (input_to_state_stage(afl, in_buf, out_buf, len, afl->queue_cur->exec_cksum)) { @@ -589,8 +587,9 @@ u8 fuzz_one_original(afl_state_t *afl) { /* Skip deterministic fuzzing if exec path checksum puts this out of scope for this main instance. */ - if (afl->main_node_max && (afl->queue_cur->exec_cksum % afl->main_node_max) != - afl->main_node_id - 1) { + if (unlikely(afl->main_node_max && + (afl->queue_cur->exec_cksum % afl->main_node_max) != + afl->main_node_id - 1)) { goto custom_mutator_stage; @@ -679,7 +678,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -690,7 +689,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -797,7 +796,8 @@ u8 fuzz_one_original(afl_state_t *afl) { /* Initialize effector map for the next step (see comments below). Always flag first and last byte as doing something. */ - eff_map = ck_maybe_grow(BUF_PARAMS(eff), EFF_ALEN(len)); + eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len)); + if (unlikely(!eff_map)) { PFATAL("alloc"); } eff_map[0] = 1; if (EFF_APOS(len - 1) != 0) { @@ -862,7 +862,7 @@ u8 fuzz_one_original(afl_state_t *afl) { whole thing as worth fuzzing, since we wouldn't be saving much time anyway. */ - if (eff_cnt != EFF_ALEN(len) && + if (eff_cnt != (u32)EFF_ALEN(len) && eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { memset(eff_map, 1, EFF_ALEN(len)); @@ -893,7 +893,7 @@ u8 fuzz_one_original(afl_state_t *afl) { orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { /* Let's consult the effector map... */ @@ -931,7 +931,7 @@ u8 fuzz_one_original(afl_state_t *afl) { orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; (s32)i < len - 3; ++i) { /* Let's consult the effector map... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && @@ -977,7 +977,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -1051,7 +1051,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; i < (u32)len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -1161,7 +1161,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; i < (u32)len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -1202,7 +1202,7 @@ skip_bitflip: } - if ((orig & 0xffff) < j && !could_be_bitflip(r2)) { + if ((orig & 0xffff) < (u32)j && !could_be_bitflip(r2)) { afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = orig - j; @@ -1234,7 +1234,7 @@ skip_bitflip: } - if ((SWAP32(orig) & 0xffff) < j && !could_be_bitflip(r4)) { + if ((SWAP32(orig) & 0xffff) < (u32)j && !could_be_bitflip(r4)) { afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); @@ -1276,7 +1276,7 @@ skip_arith: /* Setting 8-bit integers. */ - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -1291,7 +1291,7 @@ skip_arith: afl->stage_cur_byte = i; - for (j = 0; j < sizeof(interesting_8); ++j) { + for (j = 0; j < (u32)sizeof(interesting_8); ++j) { /* Skip if the value could be a product of bitflips or arithmetics. */ @@ -1331,7 +1331,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -1409,7 +1409,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; i++) { + for (i = 0; (s32)i < len - 3; i++) { u32 orig = *(u32 *)(out_buf + i); @@ -1496,7 +1496,7 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; @@ -1554,9 +1554,10 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); + ex_tmp = afl_realloc(AFL_BUF_PARAM(ex), len + MAX_DICT_FILE); + if (unlikely(!ex_tmp)) { PFATAL("alloc"); } - for (i = 0; i <= len; ++i) { + for (i = 0; i <= (u32)len; ++i) { afl->stage_cur_byte = i; @@ -1602,19 +1603,20 @@ skip_user_extras: afl->stage_name = "auto extras (over)"; afl->stage_short = "ext_AO"; afl->stage_cur = 0; - afl->stage_max = MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS) * len; + afl->stage_max = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS) * len; afl->stage_val_type = STAGE_VAL_NONE; orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; afl->stage_cur_byte = i; - for (j = 0; j < MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS); ++j) { + u32 min_extra_len = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); + for (j = 0; j < min_extra_len; ++j) { /* See the comment in the earlier code; extras are sorted by size. */ @@ -1680,6 +1682,8 @@ custom_mutator_stage: has_custom_fuzz = true; + afl->stage_short = el->name_short; + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { @@ -1727,7 +1731,8 @@ custom_mutator_stage: fd = open(target->fname, O_RDONLY); if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len); + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } ck_read(fd, new_buf, target->len, target->fname); close(fd); @@ -1902,7 +1907,8 @@ havoc_stage: temp_len = new_len; if (out_buf != custom_havoc_buf) { - ck_maybe_grow(BUF_PARAMS(out), temp_len); + afl_realloc(AFL_BUF_PARAM(out), temp_len); + if (unlikely(!afl->out_buf)) { PFATAL("alloc"); } memcpy(out_buf, custom_havoc_buf, temp_len); } @@ -2126,7 +2132,7 @@ havoc_stage: u32 clone_from, clone_to, clone_len; u8 *new_buf; - if (actually_clone) { + if (likely(actually_clone)) { clone_len = choose_block_len(afl, temp_len); clone_from = rand_below(afl, temp_len - clone_len + 1); @@ -2141,7 +2147,8 @@ havoc_stage: clone_to = rand_below(afl, temp_len); new_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } /* Head */ @@ -2149,7 +2156,7 @@ havoc_stage: /* Inserted part */ - if (actually_clone) { + if (likely(actually_clone)) { memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); @@ -2166,7 +2173,7 @@ havoc_stage: memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); out_buf = new_buf; new_buf = NULL; temp_len += clone_len; @@ -2189,7 +2196,7 @@ havoc_stage: copy_from = rand_below(afl, temp_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); - if (rand_below(afl, 4)) { + if (likely(rand_below(afl, 4))) { if (copy_from != copy_to) { @@ -2231,7 +2238,7 @@ havoc_stage: u32 extra_len = afl->a_extras[use_extra].len; u32 insert_at; - if (extra_len > temp_len) { break; } + if ((s32)extra_len > temp_len) { break; } insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, @@ -2245,7 +2252,7 @@ havoc_stage: u32 extra_len = afl->extras[use_extra].len; u32 insert_at; - if (extra_len > temp_len) { break; } + if ((s32)extra_len > temp_len) { break; } insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->extras[use_extra].data, @@ -2281,7 +2288,8 @@ havoc_stage: if (temp_len + extra_len >= MAX_FILE) { break; } - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -2337,7 +2345,8 @@ havoc_stage: } u32 new_len = target->len; - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); + u8 *new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), new_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } ck_read(fd, new_buf, new_len, target->fname); @@ -2360,7 +2369,7 @@ havoc_stage: u32 copy_from, copy_to, copy_len; copy_len = choose_block_len(afl, new_len - 1); - if (copy_len > temp_len) copy_len = temp_len; + if ((s32)copy_len > temp_len) copy_len = temp_len; copy_from = rand_below(afl, new_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); @@ -2377,7 +2386,8 @@ havoc_stage: clone_to = rand_below(afl, temp_len); u8 *temp_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); + if (unlikely(!temp_buf)) { PFATAL("alloc"); } /* Head */ @@ -2391,7 +2401,7 @@ havoc_stage: memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); out_buf = temp_buf; temp_len += clone_len; @@ -2412,7 +2422,8 @@ havoc_stage: /* out_buf might have been mangled a bit, so let's restore it to its original size and shape. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } temp_len = len; memcpy(out_buf, in_buf, len); @@ -2507,7 +2518,8 @@ retry_splicing: if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), target->len); + new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } ck_read(fd, new_buf, target->len, target->fname); @@ -2517,7 +2529,7 @@ retry_splicing: the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto retry_splicing; } @@ -2529,10 +2541,11 @@ retry_splicing: len = target->len; memcpy(new_buf, in_buf, split_at); - swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); goto custom_mutator_stage; @@ -2587,7 +2600,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } - s32 len, fd, temp_len, i, j; + s32 len, fd, temp_len; + u32 i; + u32 j; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; u64 havoc_queued = 0, orig_hit_cnt, new_hit_cnt = 0, cur_ms_lv, prev_cksum; u32 splice_cycle = 0, perf_score = 100, orig_perf, eff_cnt = 1; @@ -2671,7 +2686,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } afl->subseq_tmouts = 0; @@ -2746,7 +2762,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) { + if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { if (input_to_state_stage(afl, in_buf, out_buf, len, afl->queue_cur->exec_cksum)) { @@ -2761,9 +2777,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { cur_ms_lv = get_cur_time(); if (!(afl->key_puppet == 0 && - ((cur_ms_lv - afl->last_path_time < afl->limit_time_puppet) || + ((cur_ms_lv - afl->last_path_time < (u32)afl->limit_time_puppet) || (afl->last_crash_time != 0 && - cur_ms_lv - afl->last_crash_time < afl->limit_time_puppet) || + cur_ms_lv - afl->last_crash_time < (u32)afl->limit_time_puppet) || afl->last_path_time == 0))) { afl->key_puppet = 1; @@ -2875,7 +2891,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -2886,7 +2902,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -2993,7 +3009,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { /* Initialize effector map for the next step (see comments below). Always flag first and last byte as doing something. */ - eff_map = ck_maybe_grow(BUF_PARAMS(eff), EFF_ALEN(len)); + eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len)); + if (unlikely(!eff_map)) { PFATAL("alloc"); } eff_map[0] = 1; if (EFF_APOS(len - 1) != 0) { @@ -3058,7 +3075,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { whole thing as worth fuzzing, since we wouldn't be saving much time anyway. */ - if (eff_cnt != EFF_ALEN(len) && + if (eff_cnt != (u32)EFF_ALEN(len) && eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { memset(eff_map, 1, EFF_ALEN(len)); @@ -3089,7 +3106,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { /* Let's consult the effector map... */ @@ -3127,7 +3144,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; (s32)i < len - 3; ++i) { /* Let's consult the effector map... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && @@ -3173,7 +3190,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -3247,7 +3264,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -3357,7 +3374,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; (s32)i < len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -3472,7 +3489,7 @@ skip_arith: /* Setting 8-bit integers. */ - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -3527,7 +3544,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -3605,7 +3622,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; (s32)i < len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -3692,7 +3709,7 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; @@ -3750,9 +3767,10 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); + ex_tmp = afl_realloc(AFL_BUF_PARAM(ex), len + MAX_DICT_FILE); + if (unlikely(!ex_tmp)) { PFATAL("alloc"); } - for (i = 0; i <= len; ++i) { + for (i = 0; i <= (u32)len; ++i) { afl->stage_cur_byte = i; @@ -3798,23 +3816,23 @@ skip_user_extras: afl->stage_name = "auto extras (over)"; afl->stage_short = "ext_AO"; afl->stage_cur = 0; - afl->stage_max = MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS) * len; + afl->stage_max = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS) * len; afl->stage_val_type = STAGE_VAL_NONE; orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; afl->stage_cur_byte = i; - for (j = 0; j < MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS); ++j) { + for (j = 0; j < MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); ++j) { /* See the comment in the earlier code; extras are sorted by size. */ - if (afl->a_extras[j].len > len - i || + if ((s32)(afl->a_extras[j].len) > (s32)(len - i) || !memcmp(afl->a_extras[j].data, out_buf + i, afl->a_extras[j].len) || !memchr(eff_map + EFF_APOS(i), 1, EFF_SPAN_ALEN(i, afl->a_extras[j].len))) { @@ -4188,8 +4206,9 @@ pacemaker_fuzzing: clone_to = rand_below(afl, temp_len); - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), - temp_len + clone_len); + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), + temp_len + clone_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } /* Head */ @@ -4215,7 +4234,7 @@ pacemaker_fuzzing: memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); out_buf = new_buf; temp_len += clone_len; MOpt_globals.cycles_v2[STAGE_Clone75] += 1; @@ -4276,7 +4295,7 @@ pacemaker_fuzzing: u32 use_extra = rand_below(afl, afl->a_extras_cnt); u32 extra_len = afl->a_extras[use_extra].len; - if (extra_len > temp_len) break; + if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, @@ -4289,7 +4308,7 @@ pacemaker_fuzzing: u32 use_extra = rand_below(afl, afl->extras_cnt); u32 extra_len = afl->extras[use_extra].len; - if (extra_len > temp_len) break; + if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->extras[use_extra].data, @@ -4332,7 +4351,8 @@ pacemaker_fuzzing: if (temp_len + extra_len >= MAX_FILE) break; - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -4365,7 +4385,8 @@ pacemaker_fuzzing: /* out_buf might have been mangled a bit, so let's restore it to its original size and shape. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } temp_len = len; memcpy(out_buf, in_buf, len); @@ -4449,7 +4470,8 @@ pacemaker_fuzzing: retry_splicing_puppet: - if (afl->use_splicing && splice_cycle++ < afl->SPLICE_CYCLES_puppet && + if (afl->use_splicing && + splice_cycle++ < (u32)afl->SPLICE_CYCLES_puppet && afl->queued_paths > 1 && afl->queue_cur->len > 1) { struct queue_entry *target; @@ -4509,7 +4531,8 @@ pacemaker_fuzzing: if (fd < 0) { PFATAL("Unable to open '%s'", target->fname); } - new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), target->len); + new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } ck_read(fd, new_buf, target->len, target->fname); @@ -4519,7 +4542,8 @@ pacemaker_fuzzing: the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, (s32)target->len), &f_diff, + &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { @@ -4535,9 +4559,10 @@ pacemaker_fuzzing: len = target->len; memcpy(new_buf, in_buf, split_at); - swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); goto havoc_stage_puppet; @@ -4551,7 +4576,7 @@ pacemaker_fuzzing: abandon_entry: abandon_entry_puppet: - if (splice_cycle >= afl->SPLICE_CYCLES_puppet) { + if ((s64)splice_cycle >= afl->SPLICE_CYCLES_puppet) { afl->SPLICE_CYCLES_puppet = (rand_below( @@ -4870,5 +4895,3 @@ u8 fuzz_one(afl_state_t *afl) { } -#undef BUF_PARAMS - diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 2044c97d..e540f548 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -30,6 +30,9 @@ static void *unsupported(afl_state_t *afl, unsigned int seed) { + (void)afl; + (void)seed; + FATAL("Python Mutator cannot be called twice yet"); return NULL; @@ -37,9 +40,7 @@ static void *unsupported(afl_state_t *afl, unsigned int seed) { /* sorry for this makro... it just fills in `&py_mutator->something_buf, &py_mutator->something_size`. */ - #define BUF_PARAMS(name) \ - (void **)&((py_mutator_t *)py_mutator)->name##_buf, \ - &((py_mutator_t *)py_mutator)->name##_size + #define BUF_PARAMS(name) (void **)&((py_mutator_t *)py_mutator)->name##_buf static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, u8 *add_buf, size_t add_buf_size, size_t max_size) { @@ -94,7 +95,8 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, mutated_size = PyByteArray_Size(py_value); - *out_buf = ck_maybe_grow(BUF_PARAMS(fuzz), mutated_size); + *out_buf = afl_realloc(BUF_PARAMS(fuzz), mutated_size); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size); Py_DECREF(py_value); @@ -111,6 +113,8 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { + (void)afl; + if (!module_name) { return NULL; } py_mutator_t *py = calloc(1, sizeof(py_mutator_t)); @@ -247,6 +251,8 @@ void finalize_py_module(void *py_mutator) { static void init_py(afl_state_t *afl, py_mutator_t *py_mutator, unsigned int seed) { + (void)afl; + PyObject *py_args, *py_value; /* Provide the init function a seed for the Python RNG */ @@ -310,7 +316,6 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, mutator = ck_alloc(sizeof(struct custom_mutator)); mutator->post_process_buf = NULL; - mutator->post_process_size = 0; mutator->name = module_name; ACTF("Loading Python mutator library from '%s'...", module_name); @@ -412,7 +417,11 @@ size_t post_process_py(void *py_mutator, u8 *buf, size_t buf_size, py_out_buf_size = PyByteArray_Size(py_value); - ck_maybe_grow(BUF_PARAMS(post_process), py_out_buf_size); + if (unlikely(!afl_realloc(BUF_PARAMS(post_process), py_out_buf_size))) { + + PFATAL("alloc"); + + } memcpy(py->post_process_buf, PyByteArray_AsString(py_value), py_out_buf_size); @@ -520,7 +529,8 @@ size_t trim_py(void *py_mutator, u8 **out_buf) { if (py_value != NULL) { ret = PyByteArray_Size(py_value); - *out_buf = ck_maybe_grow(BUF_PARAMS(trim), ret); + *out_buf = afl_realloc(BUF_PARAMS(trim), ret); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), ret); Py_DECREF(py_value); @@ -585,7 +595,8 @@ size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size, } else { /* A new buf is needed... */ - *out_buf = ck_maybe_grow(BUF_PARAMS(havoc), mutated_size); + *out_buf = afl_realloc(BUF_PARAMS(havoc), mutated_size); + if (unlikely(!out_buf)) { PFATAL("alloc"); } } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 71874283..0c472845 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -26,8 +26,6 @@ #include <limits.h> #include <ctype.h> -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - /* Mark deterministic checks as done for a particular queue entry. We use the .state file to avoid repeating deterministic fuzzing when resuming aborted scans. */ @@ -112,8 +110,10 @@ static u8 check_if_text(struct queue_entry *q) { u8 buf[MAX_FILE]; s32 fd, len = q->len, offset = 0, ascii = 0, utf8 = 0, comp; + if (len >= MAX_FILE) len = MAX_FILE - 1; if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; if ((comp = read(fd, buf, len)) != len) return 0; + buf[len] = 0; close(fd); while (offset < len) { @@ -246,8 +246,9 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } - struct queue_entry **queue_buf = ck_maybe_grow( - BUF_PARAMS(queue), afl->queued_paths * sizeof(struct queue_entry *)); + struct queue_entry **queue_buf = afl_realloc( + AFL_BUF_PARAM(queue), afl->queued_paths * sizeof(struct queue_entry *)); + if (unlikely(!queue_buf)) { PFATAL("alloc"); } queue_buf[afl->queued_paths - 1] = q; afl->last_path_time = get_cur_time(); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index cb4c78df..1ae6ab54 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -151,7 +151,8 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { /* Discard if the mutations change the paths or if it is too decremental in speed */ if (cksum != exec_cksum || - (stop_us - start_us > 2 * afl->queue_cur->exec_us)) { + ((stop_us - start_us > 2 * afl->queue_cur->exec_us) && + likely(!afl->fixed_seed))) { ranges = add_range(ranges, rng->start, rng->start + s / 2); ranges = add_range(ranges, rng->start + s / 2 + 1, rng->end); @@ -312,8 +313,6 @@ static unsigned long long strntoull(const char *str, size_t sz, char **end, } -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u64 pattern, u64 repl, u64 o_pattern, u32 idx, u8 *orig_buf, u8 *buf, u32 len, u8 do_reverse, @@ -352,12 +351,13 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (use_num && num == pattern) { + if (use_num && (u64)num == pattern) { size_t old_len = endptr - buf_8; size_t num_len = snprintf(NULL, 0, "%lld", num); - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), len + num_len); + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } memcpy(new_buf, buf, idx); snprintf(new_buf + idx, num_len, "%lld", num); @@ -370,7 +370,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, size_t old_len = endptr - buf_8; size_t num_len = snprintf(NULL, 0, "%llu", unum); - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), len + num_len); + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } memcpy(new_buf, buf, idx); snprintf(new_buf + idx, num_len, "%llu", unum); @@ -499,7 +500,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } - maybe_add_auto((u8 *)afl, (u8 *)&v, shape); + maybe_add_auto(afl, (u8 *)&v, shape); u64 rev; switch (shape) { @@ -508,15 +509,15 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { break; case 2: rev = SWAP16((u16)v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; case 4: rev = SWAP32((u32)v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; case 8: rev = SWAP64(v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; } @@ -659,12 +660,12 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } -static u8 rtn_extend_encoding(afl_state_t *afl, struct cmp_header *h, - u8 *pattern, u8 *repl, u8 *o_pattern, u32 idx, - u8 *orig_buf, u8 *buf, u32 len, u8 *status) { +static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, + u8 *o_pattern, u32 idx, u8 *orig_buf, u8 *buf, + u32 len, u8 *status) { u32 i; - u32 its_len = MIN(32, len - idx); + u32 its_len = MIN((u32)32, len - idx); u8 save[32]; memcpy(save, &buf[idx], its_len); @@ -728,7 +729,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { for (idx = 0; idx < len && fails < 8; ++idx) { - if (unlikely(rtn_extend_encoding(afl, h, o->v0, o->v1, orig_o->v0, idx, + if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0, idx, orig_buf, buf, len, &status))) { return 1; @@ -745,7 +746,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } - if (unlikely(rtn_extend_encoding(afl, h, o->v1, o->v0, orig_o->v1, idx, + if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1, idx, orig_buf, buf, len, &status))) { return 1; @@ -771,8 +772,8 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { if (afl->pass_stats[key].total == 0) { - maybe_add_auto((u8 *)afl, o->v0, SHAPE_BYTES(h->shape)); - maybe_add_auto((u8 *)afl, o->v1, SHAPE_BYTES(h->shape)); + maybe_add_auto(afl, o->v0, SHAPE_BYTES(h->shape)); + maybe_add_auto(afl, o->v1, SHAPE_BYTES(h->shape)); } @@ -853,12 +854,13 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, if (afl->shm.cmp_map->headers[k].type == CMP_TYPE_INS) { - afl->stage_max += MIN((u32)afl->shm.cmp_map->headers[k].hits, CMP_MAP_H); + afl->stage_max += + MIN((u32)(afl->shm.cmp_map->headers[k].hits), (u32)CMP_MAP_H); } else { afl->stage_max += - MIN((u32)afl->shm.cmp_map->headers[k].hits, CMP_MAP_RTN_H); + MIN((u32)(afl->shm.cmp_map->headers[k].hits), (u32)CMP_MAP_RTN_H); } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 44d3c522..d71ec339 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -38,8 +38,8 @@ u64 time_spent_working = 0; /* Execute target application, monitoring for timeouts. Return status information. The called program will update afl->fsrv->trace_bits. */ -fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, - u32 timeout) { +fsrv_run_result_t __attribute__((hot)) +fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { #ifdef PROFILING static u64 time_spent_start = 0; @@ -72,7 +72,8 @@ fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, old file is unlinked and a new one is created. Otherwise, afl->fsrv.out_fd is rewound and truncated. */ -void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { +void __attribute__((hot)) +write_to_testcase(afl_state_t *afl, void *mem, u32 len) { #ifdef _AFL_DOCUMENT_MUTATIONS s32 doc_fd; @@ -134,11 +135,9 @@ void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { } -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - /* The same, but with an adjustable gap. Used for trimming. */ -static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, +static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, u32 skip_len) { s32 fd = afl->fsrv.out_fd; @@ -148,7 +147,8 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, This memory is used to carry out the post_processing(if present) after copying the testcase by removing the gaps. This can break though */ - u8 *mem_trimmed = ck_maybe_grow(BUF_PARAMS(out_scratch), len - skip_len + 1); + u8 *mem_trimmed = afl_realloc(AFL_BUF_PARAM(out_scratch), len - skip_len + 1); + if (unlikely(!mem_trimmed)) { PFATAL("alloc"); } ssize_t new_size = len - skip_len; void * new_mem = mem; @@ -287,8 +287,6 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, } -#undef BUF_PARAMS - /* Calibrate a new test case. This is done when processing the input directory to warn about flaky or otherwise problematic test cases early on; and when new paths are discovered to detect variable behavior and so on. */ @@ -733,12 +731,12 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { len_p2 = next_pow2(q->len); - remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES); + remove_len = MAX(len_p2 / TRIM_START_STEPS, (u32)TRIM_MIN_BYTES); /* Continue until the number of steps gets too high or the stepover gets too small. */ - while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) { + while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, (u32)TRIM_MIN_BYTES)) { u32 remove_pos = remove_len; @@ -819,16 +817,27 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { fd = open(q->fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + + u32 written = 0; + while (written < q->len) { + + ssize_t result = write(fd, in_buf, q->len - written); + if (result > 0) written += result; + + } + } else { unlink(q->fname); /* ignore errors */ fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); - } + if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } - if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + ck_write(fd, in_buf, q->len, q->fname); + + } - ck_write(fd, in_buf, q->len, q->fname); close(fd); memcpy(afl->fsrv.trace_bits, afl->clean_trace, afl->fsrv.map_size); @@ -847,7 +856,8 @@ abort_trimming: error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ -u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { +u8 __attribute__((hot)) +common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 fault; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index e2d62bc6..dd0e316c 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -112,8 +112,9 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->fsrv.use_stdin = 1; afl->fsrv.map_size = map_size; - afl->fsrv.function_opt = (u8 *)afl; - afl->fsrv.function_ptr = &maybe_add_auto; + // afl_state_t is not available in forkserver.c + afl->fsrv.afl_ptr = (void *)afl; + afl->fsrv.add_extra_func = (void (*)(void *, u8 *, u32)) & add_extra; afl->cal_cycles = CAL_CYCLES; afl->cal_cycles_long = CAL_CYCLES_LONG; @@ -420,13 +421,13 @@ void afl_state_deinit(afl_state_t *afl) { if (afl->pass_stats) { ck_free(afl->pass_stats); } if (afl->orig_cmp_map) { ck_free(afl->orig_cmp_map); } - if (afl->queue_buf) { free(afl->queue_buf); } - if (afl->out_buf) { free(afl->out_buf); } - if (afl->out_scratch_buf) { free(afl->out_scratch_buf); } - if (afl->eff_buf) { free(afl->eff_buf); } - if (afl->in_buf) { free(afl->in_buf); } - if (afl->in_scratch_buf) { free(afl->in_scratch_buf); } - if (afl->ex_buf) { free(afl->ex_buf); } + afl_free(afl->queue_buf); + afl_free(afl->out_buf); + afl_free(afl->out_scratch_buf); + afl_free(afl->eff_buf); + afl_free(afl->in_buf); + afl_free(afl->in_scratch_buf); + afl_free(afl->ex_buf); ck_free(afl->virgin_bits); ck_free(afl->virgin_tmout); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 7b30b5ea..aeb290bd 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -890,12 +890,12 @@ void show_stats(afl_state_t *afl) { if (afl->cpu_aff >= 0) { SAYF("%s" cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, spacing, - MIN(afl->cpu_aff, 999), cpu_color, MIN(cur_utilization, 999)); + MIN(afl->cpu_aff, 999), cpu_color, MIN(cur_utilization, (u32)999)); } else { SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color, - MIN(cur_utilization, 999)); + MIN(cur_utilization, (u32)999)); } @@ -1081,7 +1081,7 @@ void show_init_stats(afl_state_t *afl) { if (afl->non_instrumented_mode && !(afl->afl_env.afl_hang_tmout)) { - afl->hang_tmout = MIN(EXEC_TIMEOUT, afl->fsrv.exec_tmout * 2 + 100); + afl->hang_tmout = MIN((u32)EXEC_TIMEOUT, afl->fsrv.exec_tmout * 2 + 100); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index da30797c..5dd092f2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -79,7 +79,7 @@ static void at_exit() { /* Display usage hints. */ -static void usage(afl_state_t *afl, u8 *argv0, int more_help) { +static void usage(u8 *argv0, int more_help) { SAYF( "\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" @@ -115,8 +115,8 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { " if using QEMU, just use -c 0.\n\n" "Fuzzing behavior settings:\n" - " -N - do not unlink the fuzzing input file (only for " - "devices etc.!)\n" + " -N - do not unlink the fuzzing input file (for devices " + "etc.)\n" " -d - quick & dirty mode (skips deterministic steps)\n" " -n - fuzz without instrumentation (non-instrumented mode)\n" " -x dict_file - optional fuzzer dictionary (see README.md, its really " @@ -289,9 +289,12 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->cpu_to_bind != -1) FATAL("Multiple -b options not supported"); - if (sscanf(optarg, "%u", &afl->cpu_to_bind) < 0 || optarg[0] == '-') + if (sscanf(optarg, "%d", &afl->cpu_to_bind) < 0) { + FATAL("Bad syntax used for -b"); + } + break; } @@ -677,7 +680,7 @@ int main(int argc, char **argv_orig, char **envp) { u64 limit_time_puppet2 = afl->limit_time_puppet * 60 * 1000; - if (limit_time_puppet2 < afl->limit_time_puppet) { + if ((s32)limit_time_puppet2 < afl->limit_time_puppet) { FATAL("limit_time overflow"); @@ -811,7 +814,7 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !afl->in_dir || !afl->out_dir || show_help) { - usage(afl, argv[0], show_help); + usage(argv[0], show_help); } diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 71e975a1..47c615d8 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -68,9 +68,11 @@ static char *stdin_file; /* stdin file */ static u8 *in_dir = NULL, /* input folder */ *out_file = NULL, *at_file = NULL; /* Substitution string for @@ */ -static u8 *in_data; /* Input data */ +static u8 *in_data, /* Input data */ + *coverage_map; /* Coverage map */ -static u32 total, highest; /* tuple content information */ +static u64 total; /* tuple content information */ +static u32 tcnt, highest; /* tuple content information */ static u32 in_len, /* Input data length */ arg_offset; /* Total number of execs */ @@ -83,7 +85,8 @@ static u8 quiet_mode, /* Hide non-essential messages? */ cmin_mode, /* Generate output in afl-cmin mode? */ binary_mode, /* Write output as a binary map */ keep_cores, /* Allow coredumps? */ - remove_shm = 1; /* remove shmem? */ + remove_shm = 1, /* remove shmem? */ + collect_coverage; /* collect coverage */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_crashed; /* Child crashed? */ @@ -175,6 +178,25 @@ static void at_exit_handler(void) { } +/* Analyze results. */ + +static void analyze_results(afl_forkserver_t *fsrv) { + + u32 i; + for (i = 0; i < map_size; i++) { + + if (fsrv->trace_bits[i]) { + + total += fsrv->trace_bits[i]; + if (fsrv->trace_bits[i] > highest) highest = fsrv->trace_bits[i]; + if (!coverage_map[i]) { coverage_map[i] = 1; } + + } + + } + +} + /* Write results. */ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { @@ -256,8 +278,8 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { /* Execute target application. */ -static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, char **argv, - u8 *mem, u32 len) { +static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, + u32 len) { afl_fsrv_write_to_testcase(fsrv, mem, len); @@ -444,6 +466,7 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; afl_fsrv_killall(); @@ -587,9 +610,14 @@ static void usage(u8 *argv0) { " (Not necessary, here for consistency with other afl-* " "tools)\n\n" "Other settings:\n" - " -i dir - process all files in this directory, -o must be a " + " -i dir - process all files in this directory, must be combined " + "with -o.\n" + " With -C, -o is a file, without -C it must be a " "directory\n" " and each bitmap will be written there individually.\n" + " -C - collect coverage, writes all edges to -o and gives a " + "summary\n" + " Must be combined with -i.\n" " -q - sink program's output and don't show messages\n" " -e - show edge coverage only, ignore hit counts\n" " -r - show real tuple values instead of AFL filter values\n" @@ -623,7 +651,6 @@ int main(int argc, char **argv_orig, char **envp) { s32 opt, i; u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; - u32 tcnt = 0; char **use_argv; char **argv = argv_cpy_dup(argc, argv_orig); @@ -638,10 +665,14 @@ int main(int argc, char **argv_orig, char **envp) { if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; } - while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqZQUWbcrh")) > 0) { + while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrh")) > 0) { switch (opt) { + case 'C': + collect_coverage = 1; + break; + case 'i': if (in_dir) { FATAL("Multiple -i options not supported"); } in_dir = optarg; @@ -819,6 +850,13 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !out_file) { usage(argv[0]); } + if (in_dir) { + + if (!out_file && !collect_coverage) + FATAL("for -i you need to specify either -C and/or -o"); + + } + if (fsrv->qemu_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } if (unicorn_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } @@ -909,7 +947,7 @@ int main(int argc, char **argv_orig, char **envp) { if (in_dir) { - DIR * dir_in, *dir_out; + DIR * dir_in, *dir_out = NULL; struct dirent *dir_ent; int done = 0; u8 infile[PATH_MAX], outfile[PATH_MAX]; @@ -923,20 +961,43 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->dev_null_fd = open("/dev/null", O_RDWR); if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } + // if a queue subdirectory exists switch to that + u8 *dn = alloc_printf("%s/queue", in_dir); + if ((dir_in = opendir(dn)) != NULL) { + + closedir(dir_in); + in_dir = dn; + + } else + + ck_free(dn); + if (!be_quiet) ACTF("Reading from directory '%s'...", in_dir); + if (!(dir_in = opendir(in_dir))) { PFATAL("cannot open directory %s", in_dir); } - if (!(dir_out = opendir(out_file))) { + if (!collect_coverage) { + + if (!(dir_out = opendir(out_file))) { + + if (mkdir(out_file, 0700)) { - if (mkdir(out_file, 0700)) { + PFATAL("cannot create output directory %s", out_file); - PFATAL("cannot create output directory %s", out_file); + } } + } else { + + if ((coverage_map = (u8 *)malloc(map_size)) == NULL) + FATAL("coult not grab memory"); + edges_only = 0; + raw_instr_output = 1; + } u8 *use_dir = "."; @@ -977,6 +1038,7 @@ int main(int argc, char **argv_orig, char **envp) { afl_fsrv_start(fsrv, use_argv, &stop_soon, get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + map_size = fsrv->map_size; if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); @@ -1004,7 +1066,8 @@ int main(int argc, char **argv_orig, char **envp) { if (-1 == stat(infile, &statbuf) || !S_ISREG(statbuf.st_mode)) continue; #endif - snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name); + if (!collect_coverage) + snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name); if (read_file(infile)) { @@ -1016,9 +1079,12 @@ int main(int argc, char **argv_orig, char **envp) { } - showmap_run_target_forkserver(fsrv, use_argv, in_data, in_len); + showmap_run_target_forkserver(fsrv, in_data, in_len); ck_free(in_data); - tcnt = write_results_to_file(fsrv, outfile); + if (collect_coverage) + analyze_results(fsrv); + else + tcnt = write_results_to_file(fsrv, outfile); } @@ -1029,6 +1095,13 @@ int main(int argc, char **argv_orig, char **envp) { closedir(dir_in); if (dir_out) { closedir(dir_out); } + if (collect_coverage) { + + memcpy(fsrv->trace_bits, coverage_map, map_size); + tcnt = write_results_to_file(fsrv, out_file); + + } + } else { if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) @@ -1042,8 +1115,14 @@ int main(int argc, char **argv_orig, char **envp) { if (!quiet_mode) { if (!tcnt) { FATAL("No instrumentation detected" cRST); } - OKF("Captured %u tuples (highest value %u, total values %u) in '%s'." cRST, + OKF("Captured %u tuples (highest value %u, total values %llu) in " + "'%s'." cRST, tcnt, highest, total, out_file); + if (collect_coverage) + OKF("A coverage of %u edges were achieved out of %u existing (%.02f%%) " + "with %llu input files.", + tcnt, map_size, ((float)tcnt * 100) / (float)map_size, + fsrv->total_execs); } diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 68fcdd14..b50d8597 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -250,7 +250,7 @@ static s32 write_to_file(u8 *path, u8 *mem, u32 len) { /* Execute target application. Returns 0 if the changes are a dud, or 1 if they should be kept. */ -static u8 tmin_run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, +static u8 tmin_run_target(afl_forkserver_t *fsrv, u8 *mem, u32 len, u8 first_run) { afl_fsrv_write_to_testcase(fsrv, mem, len); @@ -342,7 +342,7 @@ static u8 tmin_run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, /* Actually minimize! */ -static void minimize(afl_forkserver_t *fsrv, char **argv) { +static void minimize(afl_forkserver_t *fsrv) { static u32 alpha_map[256]; @@ -380,7 +380,7 @@ static void minimize(afl_forkserver_t *fsrv, char **argv) { memset(tmp_buf + set_pos, '0', use_len); u8 res; - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -453,7 +453,7 @@ next_del_blksize: /* Tail */ memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len); - res = tmin_run_target(fsrv, argv, tmp_buf, del_pos + tail_len, 0); + res = tmin_run_target(fsrv, tmp_buf, del_pos + tail_len, 0); if (res) { @@ -524,7 +524,7 @@ next_del_blksize: } - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -560,7 +560,7 @@ next_del_blksize: if (orig == '0') { continue; } tmp_buf[i] = '0'; - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -623,6 +623,7 @@ finalize_all: static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; afl_fsrv_killall(); @@ -1131,7 +1132,7 @@ int main(int argc, char **argv_orig, char **envp) { ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", fsrv->mem_limit, fsrv->exec_tmout, edges_only ? ", edges only" : ""); - tmin_run_target(fsrv, use_argv, in_data, in_len, 1); + tmin_run_target(fsrv, in_data, in_len, 1); if (hang_mode && !fsrv->last_run_timed_out) { @@ -1169,7 +1170,7 @@ int main(int argc, char **argv_orig, char **envp) { } - minimize(fsrv, use_argv); + minimize(fsrv); ACTF("Writing output to '%s'...", output_file); diff --git a/test/test-all.sh b/test/test-all.sh new file mode 100755 index 00000000..8df4bef9 --- /dev/null +++ b/test/test-all.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +. ./test-pre.sh + +. ./test-basic.sh + +. ./test-llvm.sh + +. ./test-llvm-lto.sh + +. ./test-gcc-plugin.sh + +. ./test-libextensions.sh + +. ./test-qemu-mode.sh + +. ./test-unicorn-mode.sh + +. ./test-custom-mutators.sh + +. ./test-unittests.sh + +. ./test-post.sh diff --git a/test/test-basic.sh b/test/test-basic.sh new file mode 100755 index 00000000..06c40efe --- /dev/null +++ b/test/test-basic.sh @@ -0,0 +1,126 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" +test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "i386" && { + test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && { + ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 + test -e test-instr.plain && { + $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] ${AFL_GCC} instrumentation present and working correctly" + } + } || { + $ECHO "$RED[!] ${AFL_GCC} instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 11 && { + $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] ${AFL_GCC} instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + } || { + $ECHO "$RED[!] ${AFL_GCC} failed" + echo CUT------------------------------------------------------------------CUT + uname -a + ../${AFL_GCC} -o test-instr.plain ../test-instr.c + echo CUT------------------------------------------------------------------CUT + CODE=1 + } + test -e test-compcov.harden && { + grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && { + $ECHO "$GREEN[+] ${AFL_GCC} hardened mode succeeded and is working" + } || { + $ECHO "$RED[!] ${AFL_GCC} hardened mode is not hardened" + CODE=1 + } + rm -f test-compcov.harden + } || { + $ECHO "$RED[!] ${AFL_GCC} hardened mode compilation failed" + CODE=1 + } + # now we want to be sure that afl-fuzz is working + # make sure core_pattern is set to core on linux + (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { + $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" + true + }) || + # make sure crash reporter is disabled on Mac OS X + (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { + $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" + true + }) || { + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for ${AFL_GCC}, this will take approx 10 seconds" + { + ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with ${AFL_GCC}" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with ${AFL_GCC}" + CODE=1 + } + echo 000000000000000000000000 > in/in2 + echo 111 > in/in3 + mkdir -p in2 + ../afl-cmin -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null 2>&1 # why is afl-forkserver writing to stderr? + CNT=`ls in2/* 2>/dev/null | wc -l` + case "$CNT" in + *2) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;; + *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" + CODE=1 + ;; + esac + rm -f in2/in* + export AFL_QUIET=1 + if command -v bash >/dev/null ; then { + ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null + CNT=`ls in2/* 2>/dev/null | wc -l` + case "$CNT" in + *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; + *) $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" + CODE=1 + ;; + esac + } else { + $ECHO "$GRAY[*] no bash available, cannot test afl-cmin.bash" + } + fi + ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 + SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` + test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" + test "$SIZE" = 1 || { + $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" + CODE=1 + } + rm -rf in out errors in2 + unset AFL_QUIET + } + rm -f test-instr.plain + } || { + $ECHO "$YELLOW[-] afl is not compiled, cannot test" + INCOMPLETE=1 + } +} || { + $ECHO "$GREY[[*] not an intel platform, skipped tests of afl-gcc" + #this is not incomplete as this feature doesnt exist, so all good + AFL_TEST_COUNT=$((AFL_TEST_COUNT-1)) +} + +. ./test-post.sh diff --git a/test/test-custom-mutators.sh b/test/test-custom-mutators.sh new file mode 100755 index 00000000..4d73739f --- /dev/null +++ b/test/test-custom-mutators.sh @@ -0,0 +1,125 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: custom mutator" +test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { + # normalize path + CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../examples/custom_mutators;pwd) + test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && { + unset AFL_CC + # Compile the vulnerable program for single mutator + test -e ../afl-clang-fast && { + ../afl-clang-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 + } || { + test -e ../afl-gcc-fast && { + ../afl-gcc-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 + } || { + ../afl-gcc -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 + } + } + # Compile the vulnerable program for multiple mutators + test -e ../afl-clang-fast && { + ../afl-clang-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 + } || { + test -e ../afl-gcc-fast && { + ../afl-gcc-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 + } || { + ../afl-gcc -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 + } + } + # Compile the custom mutator + cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 + cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 + test -e test-custom-mutator -a -e ./libexamplemutator.so && { + # Create input directory + mkdir -p in + echo "00000" > in/in + + # Run afl-fuzz w/ the C mutator + $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 5 seconds" + { + AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 + } >>errors 2>&1 + + # Check results + test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here + $ECHO "$GREEN[+] afl-fuzz is working correctly with the C mutator" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with the C mutator" + CODE=1 + } + + # Clean + rm -rf out errors core.* + + # Run afl-fuzz w/ multiple C mutators + $ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 5 seconds" + { + AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1 + } >>errors 2>&1 + + test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here + $ECHO "$GREEN[+] afl-fuzz is working correctly with multiple C mutators" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with multiple C mutators" + CODE=1 + } + + # Clean + rm -rf out errors core.* + + # Run afl-fuzz w/ the Python mutator + $ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 5 seconds" + { + export PYTHONPATH=${CUSTOM_MUTATOR_PATH} + export AFL_PYTHON_MODULE=example + AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V5 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 + unset PYTHONPATH + unset AFL_PYTHON_MODULE + } >>errors 2>&1 + + # Check results + test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here + $ECHO "$GREEN[+] afl-fuzz is working correctly with the Python mutator" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with the Python mutator" + CODE=1 + } + + # Clean + rm -rf in out errors core.* + rm -rf ${CUSTOM_MUTATOR_PATH}/__pycache__/ + rm -f test-multiple-mutators test-custom-mutator libexamplemutator.so libexamplemutator2.so + } || { + ls . + ls ${CUSTOM_MUTATOR_PATH} + $ECHO "$RED[!] cannot compile the test program or the custom mutator" + CODE=1 + } + + #test "$CODE" = 1 && { $ECHO "$YELLOW[!] custom mutator tests currently will not fail travis" ; CODE=0 ; } + + make -C ../examples/custom_mutators clean > /dev/null 2>&1 + rm -f test-custom-mutator + rm -f test-custom-mutators + } || { + $ECHO "$YELLOW[-] no custom mutators in $CUSTOM_MUTATOR_PATH, cannot test" + INCOMPLETE=1 + } + unset CUSTOM_MUTATOR_PATH +} || { + $ECHO "$YELLOW[-] no python support in afl-fuzz, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh new file mode 100755 index 00000000..2ed10a72 --- /dev/null +++ b/test/test-gcc-plugin.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: gcc_plugin" +test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && { + SAVE_AFL_CC=${AFL_CC} + export AFL_CC=`command -v gcc` + ../afl-gcc-fast -o test-instr.plain.gccpi ../test-instr.c > /dev/null 2>&1 + AFL_HARDEN=1 ../afl-gcc-fast -o test-compcov.harden.gccpi test-compcov.c > /dev/null 2>&1 + test -e test-instr.plain.gccpi && { + $ECHO "$GREEN[+] gcc_plugin compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain.gccpi > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain.gccpi < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] gcc_plugin instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] gcc_plugin instrumentation present and working correctly" + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { + $ECHO "$GREEN[+] gcc_plugin run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] gcc_plugin instrumentation produces a weird numbers: $TUPLES" + $ECHO "$YELLOW[-] this is a known issue in gcc, not afl++. It is not flagged as an error because travis builds would all fail otherwise :-(" + #CODE=1 + } + } + } || { + $ECHO "$RED[!] gcc_plugin instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + } || { + $ECHO "$RED[!] gcc_plugin failed" + CODE=1 + } + + test -e test-compcov.harden.gccpi && test_compcov_binary_functionality ./test-compcov.harden.gccpi && { + grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden.gccpi > /dev/null 2>&1 && { + $ECHO "$GREEN[+] gcc_plugin hardened mode succeeded and is working" + } || { + $ECHO "$RED[!] gcc_plugin hardened mode is not hardened" + CODE=1 + } + rm -f test-compcov.harden.gccpi + } || { + $ECHO "$RED[!] gcc_plugin hardened mode compilation failed" + CODE=1 + } + # now we want to be sure that afl-fuzz is working + (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { + $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" + true + }) || + # make sure crash reporter is disabled on Mac OS X + (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { + $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" + CODE=1 + true + }) || { + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for gcc_plugin, this will take approx 10 seconds" + { + ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain.gccpi >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with gcc_plugin" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with gcc_plugin" + CODE=1 + } + rm -rf in out errors + } + rm -f test-instr.plain.gccpi + + # now for the special gcc_plugin things + echo foobar.c > instrumentlist.txt + AFL_GCC_INSTRUMENT_FILE=instrumentlist.txt ../afl-gcc-fast -o test-compcov test-compcov.c > /dev/null 2>&1 + test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { + echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 1 tuples" && { + $ECHO "$GREEN[+] gcc_plugin instrumentlist feature works correctly" + } || { + $ECHO "$RED[!] gcc_plugin instrumentlist feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] gcc_plugin instrumentlist feature compilation failed" + CODE=1 + } + rm -f test-compcov test.out instrumentlist.txt + ../afl-gcc-fast -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 + test -e test-persistent && { + echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { + $ECHO "$GREEN[+] gcc_plugin persistent mode feature works correctly" + } || { + $ECHO "$RED[!] gcc_plugin persistent mode feature failed to work" + CODE=1 + } + } || { + $ECHO "$RED[!] gcc_plugin persistent mode feature compilation failed" + CODE=1 + } + rm -f test-persistent + export AFL_CC=${SAVE_AFL_CC} +} || { + $ECHO "$YELLOW[-] gcc_plugin not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-libextensions.sh b/test/test-libextensions.sh new file mode 100755 index 00000000..905a4cbc --- /dev/null +++ b/test/test-libextensions.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +. ./test-pre.sh + +test -z "$AFL_CC" && unset AFL_CC + +$ECHO "$BLUE[*] Testing: shared library extensions" +cc $CFLAGS -o test-compcov test-compcov.c > /dev/null 2>&1 +test -e ../libtokencap.so && { + AFL_TOKEN_FILE=token.out LD_PRELOAD=../libtokencap.so DYLD_INSERT_LIBRARIES=../libtokencap.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov foobar > /dev/null 2>&1 + grep -q BUGMENOT token.out > /dev/null 2>&1 && { + $ECHO "$GREEN[+] libtokencap did successfully capture tokens" + } || { + $ECHO "$RED[!] libtokencap did not capture tokens" + CODE=1 + } + rm -f token.out +} || { + $ECHO "$YELLOW[-] libtokencap is not compiled, cannot test" + INCOMPLETE=1 +} +test -e ../libdislocator.so && { + { + ulimit -c 1 + # DYLD_INSERT_LIBRARIES and DYLD_FORCE_FLAT_NAMESPACE is used on Darwin/MacOSX + LD_PRELOAD=../libdislocator.so DYLD_INSERT_LIBRARIES=../libdislocator.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov BUFFEROVERFLOW > test.out 2>/dev/null + } > /dev/null 2>&1 + grep -q BUFFEROVERFLOW test.out > /dev/null 2>&1 && { + $ECHO "$RED[!] libdislocator did not detect the memory corruption" + CODE=1 + } || { + $ECHO "$GREEN[+] libdislocator did successfully detect the memory corruption" + } + rm -f test.out core test-compcov.core core.test-compcov +} || { + $ECHO "$YELLOW[-] libdislocator is not compiled, cannot test" + INCOMPLETE=1 +} +rm -f test-compcov + +test -z "$AFL_CC" && { + if type gcc >/dev/null; then + export AFL_CC=gcc + else + if type clang >/dev/null; then + export AFL_CC=clang + fi + fi +} + +. ./test-post.sh diff --git a/test/test-llvm-lto.sh b/test/test-llvm-lto.sh new file mode 100755 index 00000000..6b327633 --- /dev/null +++ b/test/test-llvm-lto.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: LTO llvm_mode" +test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { + # on FreeBSD need to set AFL_CC + test `uname -s` = 'FreeBSD' && { + if type clang >/dev/null; then + export AFL_CC=`command -v clang` + else + export AFL_CC=`$LLVM_CONFIG --bindir`/clang + fi + } + + ../afl-clang-lto -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + test -e test-instr.plain && { + $ECHO "$GREEN[+] llvm_mode LTO compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] llvm_mode LTO instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] llvm_mode LTO instrumentation present and working correctly" + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { + $ECHO "$GREEN[+] llvm_mode LTO run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + } + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + } || { + $ECHO "$RED[!] LTO llvm_mode failed" + CODE=1 + } + rm -f test-instr.plain + + echo foobar.c > instrumentlist.txt + AFL_DEBUG=1 AFL_LLVM_INSTRUMENT_FILE=instrumentlist.txt ../afl-clang-lto -o test-compcov test-compcov.c > test.out 2>&1 + test -e test-compcov && { + grep -q "No instrumentation targets found" test.out && { + $ECHO "$GREEN[+] llvm_mode LTO instrumentlist feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentlist feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode LTO instrumentlist feature compilation failed" + CODE=1 + } + rm -f test-compcov test.out instrumentlist.txt + ../afl-clang-lto -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 + test -e test-persistent && { + echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { + $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode LTO persistent mode feature failed to work" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode LTO persistent mode feature compilation failed" + CODE=1 + } + rm -f test-persistent +} || { + $ECHO "$YELLOW[-] LTO llvm_mode not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-llvm.sh b/test/test-llvm.sh new file mode 100755 index 00000000..feeb3992 --- /dev/null +++ b/test/test-llvm.sh @@ -0,0 +1,230 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: llvm_mode, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" +test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { + # on FreeBSD need to set AFL_CC + test `uname -s` = 'FreeBSD' && { + if type clang >/dev/null; then + export AFL_CC=`command -v clang` + else + export AFL_CC=`$LLVM_CONFIG --bindir`/clang + fi + } + ../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1 + test -e test-instr.plain && { + $ECHO "$GREEN[+] llvm_mode compilation succeeded" + echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + test -e test-instr.plain.0 -a -e test-instr.plain.1 && { + diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { + $ECHO "$RED[!] llvm_mode instrumentation should be different on different input but is not" + CODE=1 + } || { + $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly" + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 8 && { + $ECHO "$GREEN[+] llvm_mode run reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] llvm_mode instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + } + } || { + $ECHO "$RED[!] llvm_mode instrumentation failed" + CODE=1 + } + rm -f test-instr.plain.0 test-instr.plain.1 + } || { + $ECHO "$RED[!] llvm_mode failed" + CODE=1 + } + test -e test-compcov.harden && test_compcov_binary_functionality ./test-compcov.harden && { + grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && { + $ECHO "$GREEN[+] llvm_mode hardened mode succeeded and is working" + } || { + $ECHO "$RED[!] llvm_mode hardened mode is not hardened" + CODE=1 + } + rm -f test-compcov.harden + } || { + $ECHO "$RED[!] llvm_mode hardened mode compilation failed" + CODE=1 + } + # now we want to be sure that afl-fuzz is working + (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { + $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" + true + }) || + # make sure crash reporter is disabled on Mac OS X + (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { + $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" + CODE=1 + true + }) || { + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for llvm_mode, this will take approx 10 seconds" + { + ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode" + CODE=1 + } + test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" || { + echo 000000000000000000000000 > in/in2 + echo 111 > in/in3 + mkdir -p in2 + ../afl-cmin -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null 2>&1 # why is afl-forkserver writing to stderr? + CNT=`ls in2/* 2>/dev/null | wc -l` + case "$CNT" in + *2) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;; + *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" + CODE=1 + ;; + esac + rm -f in2/in* + export AFL_QUIET=1 + if type bash >/dev/null ; then { + ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null + CNT=`ls in2/* 2>/dev/null | wc -l` + case "$CNT" in + *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; + *) $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" + CODE=1 + ;; + esac + } else { + $ECHO "$YELLOW[-] no bash available, cannot test afl-cmin.bash" + INCOMPLETE=1 + } + fi + ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 + SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` + test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" + test "$SIZE" = 1 || { + $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" + CODE=1 + } + rm -rf in2 + } + rm -rf in out errors + } + rm -f test-instr.plain + + # now for the special llvm_mode things + test -e ../libLLVMInsTrim.so && { + AFL_LLVM_INSTRUMENT=CFG AFL_LLVM_INSTRIM_LOOPHEAD=1 ../afl-clang-fast -o test-instr.instrim ../test-instr.c > /dev/null 2>test.out + test -e test-instr.instrim && { + TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.instrim 2>&1 | grep Captur | awk '{print$3}'` + test "$TUPLES" -gt 2 -a "$TUPLES" -lt 5 && { + $ECHO "$GREEN[+] llvm_mode InsTrim reported $TUPLES instrumented locations which is fine" + } || { + $ECHO "$RED[!] llvm_mode InsTrim instrumentation produces weird numbers: $TUPLES" + CODE=1 + } + rm -f test-instr.instrim test.out + } || { + $ECHO "$RED[!] llvm_mode InsTrim compilation failed" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] llvm_mode InsTrim not compiled, cannot test" + INCOMPLETE=1 + } + AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast -o test-compcov.compcov test-compcov.c > test.out 2>&1 + test -e test-compcov.compcov && test_compcov_binary_functionality ./test-compcov.compcov && { + grep --binary-files=text -Eq " [ 123][0-9][0-9] location| [3-9][0-9] location" test.out && { + $ECHO "$GREEN[+] llvm_mode laf-intel/compcov feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode laf-intel/compcov feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode laf-intel/compcov feature compilation failed" + CODE=1 + } + rm -f test-compcov.compcov test.out + AFL_LLVM_INSTRUMENT=AFL AFL_LLVM_LAF_SPLIT_FLOATS=1 ../afl-clang-fast -o test-floatingpoint test-floatingpoint.c >errors 2>&1 + test -e test-floatingpoint && { + mkdir -p in + echo ZZZZ > in/in + $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 30 seconds" + { + AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -s 1 -V30 -m ${MEM_LIMIT} -i in -o out -- ./test-floatingpoint >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/crashes/id:* 2>/dev/null )" && { + $ECHO "$GREEN[+] llvm_mode laf-intel floatingpoint splitting feature works correctly" + } || { + cat errors + $ECHO "$RED[!] llvm_mode laf-intel floatingpoint splitting feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode laf-intel floatingpoint splitting feature compilation failed" + CODE=1 + } + rm -f test-floatingpoint test.out in/in errors core.* + echo foobar.c > instrumentlist.txt + AFL_DEBUG=1 AFL_LLVM_INSTRUMENT_FILE=instrumentlist.txt ../afl-clang-fast -o test-compcov test-compcov.c > test.out 2>&1 + test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { + grep -q "No instrumentation targets found" test.out && { + $ECHO "$GREEN[+] llvm_mode instrumentlist feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode instrumentlist feature failed" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode instrumentlist feature compilation failed" + CODE=1 + } + rm -f test-compcov test.out instrumentlist.txt + AFL_LLVM_CMPLOG=1 ../afl-clang-fast -o test-cmplog test-cmplog.c > /dev/null 2>&1 + test -e test-cmplog && { + $ECHO "$GREY[*] running afl-fuzz for llvm_mode cmplog, this will take approx 10 seconds" + { + mkdir -p in + echo 0000000000000000000000000 > in/in + AFL_BENCH_UNTIL_CRASH=1 ../afl-fuzz -m none -V60 -i in -o out -c./test-cmplog -- ./test-cmplog >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/crashes/id:000000* out/hangs/id:000000* 2>/dev/null )" & { + $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode cmplog" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode cmplog" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] we cannot test llvm_mode cmplog because it is not present" + INCOMPLETE=1 + } + rm -rf errors test-cmplog in core.* + ../afl-clang-fast -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 + test -e test-persistent && { + echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { + $ECHO "$GREEN[+] llvm_mode persistent mode feature works correctly" + } || { + $ECHO "$RED[!] llvm_mode persistent mode feature failed to work" + CODE=1 + } + } || { + $ECHO "$RED[!] llvm_mode persistent mode feature compilation failed" + CODE=1 + } + rm -f test-persistent +} || { + $ECHO "$YELLOW[-] llvm_mode not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-post.sh b/test/test-post.sh new file mode 100755 index 00000000..0911e2cd --- /dev/null +++ b/test/test-post.sh @@ -0,0 +1,14 @@ +#!/bin/sh +AFL_TEST_DEPTH=$((AFL_TEST_DEPTH-1)) + +if [ $AFL_TEST_DEPTH = 0 ]; then +# All runs done :) + +$ECHO "$GREY[*] $AFL_TEST_COUNT test cases completed.$RESET" +test "$INCOMPLETE" = "0" && $ECHO "$GREEN[+] all test cases executed" +test "$INCOMPLETE" = "1" && $ECHO "$YELLOW[-] not all test cases were executed" +test "$CODE" = "0" && $ECHO "$GREEN[+] all tests were successful :-)$RESET" +test "$CODE" = "0" || $ECHO "$RED[!] failure in tests :-($RESET" +exit $CODE + +fi diff --git a/test/test-pre.sh b/test/test-pre.sh new file mode 100755 index 00000000..e3393962 --- /dev/null +++ b/test/test-pre.sh @@ -0,0 +1,132 @@ +#!/bin/sh + +# All tests should start with sourcing test-pre.sh and finish with sourcing test-post.sh +# They may set an error code with $CODE=1 +# If tests are incomplete, they may set $INCOMPLETE=1 + +AFL_TEST_COUNT=$((AFL_TEST_COUNT+1)) +AFL_TEST_DEPTH=$((AFL_TEST_DEPTH+1)) + +if [ $AFL_TEST_DEPTH = 1 ]; then +# First run :) + +# +# Ensure we have: test, type, diff, grep -qE +# +test -z "" 2>/dev/null || { echo Error: test command not found ; exit 1 ; } +GREP=`type grep > /dev/null 2>&1 && echo OK` +test "$GREP" = OK || { echo Error: grep command not found ; exit 1 ; } +echo foobar | grep -qE 'asd|oob' 2>/dev/null || { echo Error: grep command does not support -q and/or -E option ; exit 1 ; } +test -e ./test-all.sh || cd $(dirname $0) || exit 1 +test -e ./test-all.sh || { echo Error: you must be in the test/ directory ; exit 1 ; } +export AFL_PATH=`pwd`/.. +export AFL_NO_AFFINITY=1 # workaround for travis that fails for no avail cores + +echo 1 > test.1 +echo 1 > test.2 +OK=OK +diff test.1 test.2 >/dev/null 2>&1 || OK= +rm -f test.1 test.2 +test -z "$OK" && { echo Error: diff is not working ; exit 1 ; } +test -z "$LLVM_CONFIG" && LLVM_CONFIG=llvm-config + +# check for '-a' option of grep +if grep -a test test-all.sh >/dev/null 2>&1; then + GREPAOPTION=' -a' +else + GREPAOPTION= +fi + +test_compcov_binary_functionality() { + RUN="../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- $1" + $RUN 'LIBTOKENCAP' | grep 'your string was LIBTOKENCAP' \ + && $RUN 'BUGMENOT' | grep 'your string was BUGMENOT' \ + && $RUN 'BANANA' | grep 'your string started with BAN' \ + && $RUN 'APRI' | grep 'your string was APRI' \ + && $RUN 'kiWI' | grep 'your string was Kiwi' \ + && $RUN 'Avocado' | grep 'your string was avocado' \ + && $RUN 'GRAX' 3 | grep 'your string was a prefix of Grapes' \ + && $RUN 'LOCALVARIABLE' | grep 'local var memcmp works!' \ + && $RUN 'abc' | grep 'short local var memcmp works!' \ + && $RUN 'GLOBALVARIABLE' | grep 'global var memcmp works!' +} > /dev/null + +ECHO="printf %b\\n" +$ECHO \\101 2>&1 | grep -qE '^A' || { + ECHO= + test -e /bin/printf && { + ECHO="/bin/printf %b\\n" + $ECHO "\\101" 2>&1 | grep -qE '^A' || ECHO= + } +} +test -z "$ECHO" && { printf Error: printf command does not support octal character codes ; exit 1 ; } + +export AFL_EXIT_WHEN_DONE=1 +export AFL_SKIP_CPUFREQ=1 +export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 +unset AFL_NO_X86 +unset AFL_QUIET +unset AFL_DEBUG +unset AFL_HARDEN +unset AFL_USE_ASAN +unset AFL_USE_MSAN +unset AFL_USE_UBSAN +unset AFL_TMPDIR +unset AFL_CC +unset AFL_PRELOAD +unset AFL_GCC_INSTRUMENT_FILE +unset AFL_LLVM_INSTRUMENT_FILE +unset AFL_LLVM_INSTRIM +unset AFL_LLVM_LAF_SPLIT_SWITCHES +unset AFL_LLVM_LAF_TRANSFORM_COMPARES +unset AFL_LLVM_LAF_SPLIT_COMPARES +unset AFL_QEMU_PERSISTENT_ADDR +unset AFL_QEMU_PERSISTENT_RETADDR_OFFSET +unset AFL_QEMU_PERSISTENT_GPR +unset AFL_QEMU_PERSISTENT_RET +unset AFL_QEMU_PERSISTENT_HOOK +unset AFL_QEMU_PERSISTENT_CNT +unset AFL_CUSTOM_MUTATOR_LIBRARY +unset AFL_PYTHON_MODULE +unset AFL_PRELOAD +unset LD_PRELOAD + +rm -rf in in2 out + +export ASAN_OPTIONS=detect_leaks=0:allocator_may_return_null=1:abort_on_error=1:symbolize=0 +export AFL_LLVM_INSTRUMENT=AFL + +# on OpenBSD we need to work with llvm from /usr/local/bin +test -e /usr/local/bin/opt && { + export PATH="/usr/local/bin:${PATH}" +} +# on MacOS X we prefer afl-clang over afl-gcc, because +# afl-gcc does not work there +test `uname -s` = 'Darwin' -o `uname -s` = 'FreeBSD' && { + AFL_GCC=afl-clang +} || { + AFL_GCC=afl-gcc +} +command -v gcc >/dev/null 2>&1 || AFL_GCC=afl-clang + +SYS=`uname -m` + +GREY="\\033[1;90m" +BLUE="\\033[1;94m" +GREEN="\\033[0;32m" +RED="\\033[0;31m" +YELLOW="\\033[1;93m" +RESET="\\033[0m" + +MEM_LIMIT=none + +export PATH="${PATH}:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" + +$ECHO "${RESET}${GREY}[*] starting afl++ test framework ..." + +test -z "$SYS" && $ECHO "$YELLOW[-] uname -m did not succeed" + +CODE=0 +INCOMPLETE=0 + +fi \ No newline at end of file diff --git a/test/test-qemu-mode.sh b/test/test-qemu-mode.sh new file mode 100755 index 00000000..85a0b8b5 --- /dev/null +++ b/test/test-qemu-mode.sh @@ -0,0 +1,217 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: qemu_mode" +test -e ../afl-qemu-trace && { + cc -pie -fPIE -o test-instr ../test-instr.c + cc -o test-compcov test-compcov.c + test -e test-instr -a -e test-compcov && { + { + mkdir -p in + echo 00000 > in/in + $ECHO "$GREY[*] running afl-fuzz for qemu_mode, this will take approx 10 seconds" + { + ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode" + RUNTIME=`grep execs_done out/fuzzer_stats | awk '{print$3}'` + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode" + CODE=1 + } + rm -f errors + + $ECHO "$GREY[*] running afl-fuzz for qemu_mode AFL_ENTRYPOINT, this will take approx 6 seconds" + { + { + if file test-instr | grep -q "32-bit"; then + # for 32-bit reduce 8 nibbles to the lower 7 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` + else + # for 64-bit reduce 16 nibbles to the lower 9 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` + fi + export AFL_ENTRYPOINT=`expr 0x4${ADDR_LOWER_PART}` + $ECHO AFL_ENTRYPOINT=$AFL_ENTRYPOINT - $(nm test-instr | grep "T main") - $(file ./test-instr) + ../afl-fuzz -m ${MEM_LIMIT} -V2 -Q -i in -o out -- ./test-instr + unset AFL_ENTRYPOINT + } >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode AFL_ENTRYPOINT" + RUNTIME=`grep execs_done out/fuzzer_stats | awk '{print$3}'` + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode AFL_ENTRYPOINT" + CODE=1 + } + rm -f errors + + test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { + test -e ../libcompcov.so && { + $ECHO "$GREY[*] running afl-fuzz for qemu_mode compcov, this will take approx 10 seconds" + { + export AFL_PRELOAD=../libcompcov.so + export AFL_COMPCOV_LEVEL=2 + ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-compcov >>errors 2>&1 + unset AFL_PRELOAD + unset AFL_COMPCOV_LEVEL + } >>errors 2>&1 + test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode compcov" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode compcov" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] we cannot test qemu_mode compcov because it is not present" + INCOMPLETE=1 + } + rm -f errors + } || { + $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode compcov" + } + + test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { + $ECHO "$GREY[*] running afl-fuzz for qemu_mode cmplog, this will take approx 10 seconds" + { + ../afl-fuzz -m none -V10 -Q -c 0 -i in -o out -- ./test-compcov >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode cmplog" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode cmplog" + CODE=1 + } + rm -f errors + } || { + $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode cmplog" + } + + test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { + $ECHO "$GREY[*] running afl-fuzz for persistent qemu_mode, this will take approx 10 seconds" + { + if file test-instr | grep -q "32-bit"; then + # for 32-bit reduce 8 nibbles to the lower 7 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` + else + # for 64-bit reduce 16 nibbles to the lower 9 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` + fi + export AFL_QEMU_PERSISTENT_ADDR=`expr 0x4${ADDR_LOWER_PART}` + export AFL_QEMU_PERSISTENT_GPR=1 + $ECHO "Info: AFL_QEMU_PERSISTENT_ADDR=$AFL_QEMU_PERSISTENT_ADDR <= $(nm test-instr | grep "T main" | awk '{print $1}')" + env|grep AFL_|sort + file test-instr + ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr + unset AFL_QEMU_PERSISTENT_ADDR + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with persistent qemu_mode" + RUNTIMEP=`grep execs_done out/fuzzer_stats | awk '{print$3}'` + test -n "$RUNTIME" -a -n "$RUNTIMEP" && { + DIFF=`expr $RUNTIMEP / $RUNTIME` + test "$DIFF" -gt 1 && { # must be at least twice as fast + $ECHO "$GREEN[+] persistent qemu_mode was noticeable faster than standard qemu_mode" + } || { + $ECHO "$YELLOW[-] persistent qemu_mode was not noticeable faster than standard qemu_mode" + } + } || { + $ECHO "$YELLOW[-] we got no data on executions performed? weird!" + } + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with persistent qemu_mode" + CODE=1 + } + rm -rf in out errors + } || { + $ECHO "$YELLOW[-] not an intel or arm platform, cannot test persistent qemu_mode" + } + + test -e ../qemu_mode/unsigaction/unsigaction32.so && { + ${AFL_CC} -o test-unsigaction32 -m32 test-unsigaction.c >> errors 2>&1 && { + ./test-unsigaction32 + RETVAL_NORMAL32=$? + LD_PRELOAD=../qemu_mode/unsigaction/unsigaction32.so ./test-unsigaction32 + RETVAL_LIBUNSIGACTION32=$? + test $RETVAL_NORMAL32 = "2" -a $RETVAL_LIBUNSIGACTION32 = "0" && { + $ECHO "$GREEN[+] qemu_mode unsigaction library (32 bit) ignores signals" + } || { + test $RETVAL_NORMAL32 != "2" && { + $ECHO "$RED[!] cannot trigger signal in test program (32 bit)" + } + test $RETVAL_LIBUNSIGACTION32 != "0" && { + $ECHO "$RED[!] signal in test program (32 bit) is not ignored with unsigaction" + } + CODE=1 + } + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] cannot compile test program (32 bit) for unsigaction library" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (32 bit) because it is not present" + INCOMPLETE=1 + } + test -e ../qemu_mode/unsigaction/unsigaction64.so && { + ${AFL_CC} -o test-unsigaction64 -m64 test-unsigaction.c >> errors 2>&1 && { + ./test-unsigaction64 + RETVAL_NORMAL64=$? + LD_PRELOAD=../qemu_mode/unsigaction/unsigaction64.so ./test-unsigaction64 + RETVAL_LIBUNSIGACTION64=$? + test $RETVAL_NORMAL64 = "2" -a $RETVAL_LIBUNSIGACTION64 = "0" && { + $ECHO "$GREEN[+] qemu_mode unsigaction library (64 bit) ignores signals" + } || { + test $RETVAL_NORMAL64 != "2" && { + $ECHO "$RED[!] cannot trigger signal in test program (64 bit)" + } + test $RETVAL_LIBUNSIGACTION64 != "0" && { + $ECHO "$RED[!] signal in test program (64 bit) is not ignored with unsigaction" + } + CODE=1 + } + unset LD_PRELOAD + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] cannot compile test program (64 bit) for unsigaction library" + CODE=1 + } + } || { + $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (64 bit) because it is not present" + INCOMPLETE=1 + } + rm -rf errors test-unsigaction32 test-unsigaction64 + } + } || { + $ECHO "$RED[!] gcc compilation of test targets failed - what is going on??" + CODE=1 + } + + rm -f test-instr test-compcov +} || { + $ECHO "$YELLOW[-] qemu_mode is not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-unicorn-mode.sh b/test/test-unicorn-mode.sh new file mode 100755 index 00000000..eb2ad294 --- /dev/null +++ b/test/test-unicorn-mode.sh @@ -0,0 +1,112 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Testing: unicorn_mode" +test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shellcode && { + test -e ../unicorn_mode/samples/simple/simple_target.bin -a -e ../unicorn_mode/samples/compcov_x64/compcov_target.bin && { + { + # We want to see python errors etc. in logs, in case something doesn't work + export AFL_DEBUG_CHILD_OUTPUT=1 + + # some python version should be available now + PYTHONS="`command -v python3` `command -v python` `command -v python2`" + EASY_INSTALL_FOUND=0 + for PYTHON in $PYTHONS ; do + + if $PYTHON -c "help('easy_install');" </dev/null | grep -q module ; then + + EASY_INSTALL_FOUND=1 + PY=$PYTHON + break + + fi + + done + if [ "0" = $EASY_INSTALL_FOUND ]; then + + echo "[-] Error: Python setup-tools not found. Run 'sudo apt-get install python-setuptools'." + PREREQ_NOTFOUND=1 + + fi + + + cd ../unicorn_mode/samples/persistent + make >>errors 2>&1 + $ECHO "$GREY[*] running afl-fuzz for unicorn_mode (persistent), this will take approx 25 seconds" + AFL_DEBUG_CHILD_OUTPUT=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode (persistent)" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode (persistent)" + CODE=1 + } + + rm -rf out errors >/dev/null + make clean >/dev/null + cd ../../../test + + # travis workaround + test "$PY" = "/opt/pyenv/shims/python" -a -x /usr/bin/python && PY=/usr/bin/python + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] Using python binary $PY" + if ! $PY -c 'import unicornafl' 2>/dev/null ; then + $ECHO "$YELLOW[-] we cannot test unicorn_mode for python because it is not present" + INCOMPLETE=1 + else + { + $ECHO "$GREY[*] running afl-fuzz for unicorn_mode in python, this will take approx 25 seconds" + { + ../afl-fuzz -m ${MEM_LIMIT} -V25 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/simple/simple_test_harness.py @@ >>errors 2>&1 + } >>errors 2>&1 + test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode" + CODE=1 + } + rm -f errors + + printf '\x01\x01' > in/in + # This seed is close to the first byte of the comparison. + # If CompCov works, a new tuple will appear in the map => new input in queue + $ECHO "$GREY[*] running afl-fuzz for unicorn_mode compcov, this will take approx 35 seconds" + { + export AFL_COMPCOV_LEVEL=2 + ../afl-fuzz -m ${MEM_LIMIT} -V35 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ >>errors 2>&1 + unset AFL_COMPCOV_LEVEL + } >>errors 2>&1 + test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { + $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode compcov" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode compcov" + CODE=1 + } + rm -rf in out errors + } + fi + + unset AFL_DEBUG_CHILD_OUTPUT + + } + } || { + $ECHO "$RED[!] missing sample binaries in unicorn_mode/samples/ - what is going on??" + CODE=1 + } + +} || { + $ECHO "$YELLOW[-] unicorn_mode is not compiled, cannot test" + INCOMPLETE=1 +} + +. ./test-post.sh diff --git a/test/test-unittests.sh b/test/test-unittests.sh new file mode 100755 index 00000000..f540b5f8 --- /dev/null +++ b/test/test-unittests.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +. ./test-pre.sh + +$ECHO "$BLUE[*] Execution cmocka Unit-Tests $GREY" +unset AFL_CC +make -C .. unit || CODE=1 INCOMPLETE=1 : + +. ./test-post.sh diff --git a/test/test.sh b/test/test.sh deleted file mode 100755 index 46843d4a..00000000 --- a/test/test.sh +++ /dev/null @@ -1,1181 +0,0 @@ -#!/bin/sh - -# -# Ensure we have: test, type, diff, grep -qE -# -test -z "" 2>/dev/null || { echo Error: test command not found ; exit 1 ; } -GREP=`type grep > /dev/null 2>&1 && echo OK` -test "$GREP" = OK || { echo Error: grep command not found ; exit 1 ; } -echo foobar | grep -qE 'asd|oob' 2>/dev/null || { echo Error: grep command does not support -q and/or -E option ; exit 1 ; } -test -e ./test.sh || cd $(dirname $0) || exit 1 -test -e ./test.sh || { echo Error: you must be in the test/ directory ; exit 1 ; } -export AFL_PATH=`pwd`/.. - -echo 1 > test.1 -echo 1 > test.2 -OK=OK -diff test.1 test.2 >/dev/null 2>&1 || OK= -rm -f test.1 test.2 -test -z "$OK" && { echo Error: diff is not working ; exit 1 ; } -test -z "$LLVM_CONFIG" && LLVM_CONFIG=llvm-config - -# check for '-a' option of grep -if grep -a test test.sh >/dev/null 2>&1; then - GREPAOPTION=' -a' -else - GREPAOPTION= -fi - -test_compcov_binary_functionality() { - RUN="../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- $1" - $RUN 'LIBTOKENCAP' | grep 'your string was LIBTOKENCAP' \ - && $RUN 'BUGMENOT' | grep 'your string was BUGMENOT' \ - && $RUN 'BANANA' | grep 'your string started with BAN' \ - && $RUN 'APRI' | grep 'your string was APRI' \ - && $RUN 'kiWI' | grep 'your string was Kiwi' \ - && $RUN 'Avocado' | grep 'your string was avocado' \ - && $RUN 'GRAX' 3 | grep 'your string was a prefix of Grapes' \ - && $RUN 'LOCALVARIABLE' | grep 'local var memcmp works!' \ - && $RUN 'abc' | grep 'short local var memcmp works!' \ - && $RUN 'GLOBALVARIABLE' | grep 'global var memcmp works!' -} > /dev/null - -ECHO="printf %b\\n" -$ECHO \\101 2>&1 | grep -qE '^A' || { - ECHO= - test -e /bin/printf && { - ECHO="/bin/printf %b\\n" - $ECHO "\\101" 2>&1 | grep -qE '^A' || ECHO= - } -} -test -z "$ECHO" && { printf Error: printf command does not support octal character codes ; exit 1 ; } - -CODE=0 -INCOMPLETE=0 - -export AFL_EXIT_WHEN_DONE=1 -export AFL_SKIP_CPUFREQ=1 -export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 -unset AFL_NO_X86 -unset AFL_QUIET -unset AFL_DEBUG -unset AFL_HARDEN -unset AFL_USE_ASAN -unset AFL_USE_MSAN -unset AFL_USE_UBSAN -unset AFL_TMPDIR -unset AFL_CC -unset AFL_PRELOAD -unset AFL_GCC_INSTRUMENT_FILE -unset AFL_LLVM_INSTRUMENT_FILE -unset AFL_LLVM_INSTRIM -unset AFL_LLVM_LAF_SPLIT_SWITCHES -unset AFL_LLVM_LAF_TRANSFORM_COMPARES -unset AFL_LLVM_LAF_SPLIT_COMPARES -unset AFL_QEMU_PERSISTENT_ADDR -unset AFL_QEMU_PERSISTENT_RETADDR_OFFSET -unset AFL_QEMU_PERSISTENT_GPR -unset AFL_QEMU_PERSISTENT_RET -unset AFL_QEMU_PERSISTENT_HOOK -unset AFL_QEMU_PERSISTENT_CNT -unset AFL_CUSTOM_MUTATOR_LIBRARY -unset AFL_PYTHON_MODULE -unset AFL_PRELOAD -unset LD_PRELOAD - -rm -rf in in2 out - -export ASAN_OPTIONS=detect_leaks=0:allocator_may_return_null=1:abort_on_error=1:symbolize=0 -export AFL_LLVM_INSTRUMENT=AFL - -# on OpenBSD we need to work with llvm from /usr/local/bin -test -e /usr/local/bin/opt && { - export PATH="/usr/local/bin:${PATH}" -} -# on MacOS X we prefer afl-clang over afl-gcc, because -# afl-gcc does not work there -test `uname -s` = 'Darwin' -o `uname -s` = 'FreeBSD' && { - AFL_GCC=afl-clang -} || { - AFL_GCC=afl-gcc -} -command -v gcc >/dev/null 2>&1 || AFL_GCC=afl-clang - -SYS=`uname -m` - -GREY="\\033[1;90m" -BLUE="\\033[1;94m" -GREEN="\\033[0;32m" -RED="\\033[0;31m" -YELLOW="\\033[1;93m" -RESET="\\033[0m" - -MEM_LIMIT=none - -export PATH="${PATH}:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" - -$ECHO "${RESET}${GREY}[*] starting afl++ test framework ..." - -test -z "$SYS" && $ECHO "$YELLOW[-] uname -m did not succeed" - -$ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" -test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "i386" && { - test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && { - ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1 - AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 - test -e test-instr.plain && { - $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 - test -e test-instr.plain.0 -a -e test-instr.plain.1 && { - diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" - CODE=1 - } || { - $ECHO "$GREEN[+] ${AFL_GCC} instrumentation present and working correctly" - } - } || { - $ECHO "$RED[!] ${AFL_GCC} instrumentation failed" - CODE=1 - } - rm -f test-instr.plain.0 test-instr.plain.1 - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 11 && { - $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] ${AFL_GCC} instrumentation produces weird numbers: $TUPLES" - CODE=1 - } - } || { - $ECHO "$RED[!] ${AFL_GCC} failed" - echo CUT------------------------------------------------------------------CUT - uname -a - ../${AFL_GCC} -o test-instr.plain ../test-instr.c - echo CUT------------------------------------------------------------------CUT - CODE=1 - } - test -e test-compcov.harden && { - grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && { - $ECHO "$GREEN[+] ${AFL_GCC} hardened mode succeeded and is working" - } || { - $ECHO "$RED[!] ${AFL_GCC} hardened mode is not hardened" - CODE=1 - } - rm -f test-compcov.harden - } || { - $ECHO "$RED[!] ${AFL_GCC} hardened mode compilation failed" - CODE=1 - } - # now we want to be sure that afl-fuzz is working - # make sure core_pattern is set to core on linux - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || - # make sure crash reporter is disabled on Mac OS X - (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { - $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" - true - }) || { - mkdir -p in - echo 0 > in/in - $ECHO "$GREY[*] running afl-fuzz for ${AFL_GCC}, this will take approx 10 seconds" - { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with ${AFL_GCC}" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with ${AFL_GCC}" - CODE=1 - } - echo 000000000000000000000000 > in/in2 - echo 111 > in/in3 - mkdir -p in2 - ../afl-cmin -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null 2>&1 # why is afl-forkserver writing to stderr? - CNT=`ls in2/* 2>/dev/null | wc -l` - case "$CNT" in - *2) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;; - *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" - CODE=1 - ;; - esac - rm -f in2/in* - export AFL_QUIET=1 - if command -v bash >/dev/null ; then { - ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null - CNT=`ls in2/* 2>/dev/null | wc -l` - case "$CNT" in - *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; - *) $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" - CODE=1 - ;; - esac - } else { - $ECHO "$YELLOW[-] no bash available, cannot test afl-cmin.bash" - INCOMPLETE=1 - } - fi - ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 - SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` - test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" - test "$SIZE" = 1 || { - $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" - CODE=1 - } - rm -rf in out errors in2 - unset AFL_QUIET - } - rm -f test-instr.plain - } || { - $ECHO "$YELLOW[-] afl is not compiled, cannot test" - INCOMPLETE=1 - } -} || { - $ECHO "$YELLOW[-] not an intel platform, cannot test afl-gcc" -} - -$ECHO "$BLUE[*] Testing: llvm_mode, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" -test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { - # on FreeBSD need to set AFL_CC - test `uname -s` = 'FreeBSD' && { - if type clang >/dev/null; then - export AFL_CC=`command -v clang` - else - export AFL_CC=`$LLVM_CONFIG --bindir`/clang - fi - } - ../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 - AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1 - test -e test-instr.plain && { - $ECHO "$GREEN[+] llvm_mode compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 - test -e test-instr.plain.0 -a -e test-instr.plain.1 && { - diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] llvm_mode instrumentation should be different on different input but is not" - CODE=1 - } || { - $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { - $ECHO "$GREEN[+] llvm_mode run reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] llvm_mode instrumentation produces weird numbers: $TUPLES" - CODE=1 - } - } - } || { - $ECHO "$RED[!] llvm_mode instrumentation failed" - CODE=1 - } - rm -f test-instr.plain.0 test-instr.plain.1 - } || { - $ECHO "$RED[!] llvm_mode failed" - CODE=1 - } - test -e test-compcov.harden && test_compcov_binary_functionality ./test-compcov.harden && { - grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && { - $ECHO "$GREEN[+] llvm_mode hardened mode succeeded and is working" - } || { - $ECHO "$RED[!] llvm_mode hardened mode is not hardened" - CODE=1 - } - rm -f test-compcov.harden - } || { - $ECHO "$RED[!] llvm_mode hardened mode compilation failed" - CODE=1 - } - # now we want to be sure that afl-fuzz is working - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || - # make sure crash reporter is disabled on Mac OS X - (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { - $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" - CODE=1 - true - }) || { - mkdir -p in - echo 0 > in/in - $ECHO "$GREY[*] running afl-fuzz for llvm_mode, this will take approx 10 seconds" - { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode" - CODE=1 - } - test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" || { - echo 000000000000000000000000 > in/in2 - echo 111 > in/in3 - mkdir -p in2 - ../afl-cmin -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null 2>&1 # why is afl-forkserver writing to stderr? - CNT=`ls in2/* 2>/dev/null | wc -l` - case "$CNT" in - *2) $ECHO "$GREEN[+] afl-cmin correctly minimized the number of testcases" ;; - *) $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" - CODE=1 - ;; - esac - rm -f in2/in* - export AFL_QUIET=1 - if type bash >/dev/null ; then { - ../afl-cmin.bash -m ${MEM_LIMIT} -i in -o in2 -- ./test-instr.plain >/dev/null - CNT=`ls in2/* 2>/dev/null | wc -l` - case "$CNT" in - *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; - *) $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" - CODE=1 - ;; - esac - } else { - $ECHO "$YELLOW[-] no bash available, cannot test afl-cmin.bash" - INCOMPLETE=1 - } - fi - ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 - SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` - test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" - test "$SIZE" = 1 || { - $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" - CODE=1 - } - rm -rf in2 - } - rm -rf in out errors - } - rm -f test-instr.plain - - # now for the special llvm_mode things - test -e ../libLLVMInsTrim.so && { - AFL_LLVM_INSTRUMENT=CFG AFL_LLVM_INSTRIM_LOOPHEAD=1 ../afl-clang-fast -o test-instr.instrim ../test-instr.c > /dev/null 2>test.out - test -e test-instr.instrim && { - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.instrim 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 2 -a "$TUPLES" -lt 5 && { - $ECHO "$GREEN[+] llvm_mode InsTrim reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] llvm_mode InsTrim instrumentation produces weird numbers: $TUPLES" - CODE=1 - } - rm -f test-instr.instrim test.out - } || { - $ECHO "$RED[!] llvm_mode InsTrim compilation failed" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] llvm_mode InsTrim not compiled, cannot test" - INCOMPLETE=1 - } - AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast -o test-compcov.compcov test-compcov.c > test.out 2>&1 - test -e test-compcov.compcov && test_compcov_binary_functionality ./test-compcov.compcov && { - grep --binary-files=text -Eq " [ 123][0-9][0-9] location| [3-9][0-9] location" test.out && { - $ECHO "$GREEN[+] llvm_mode laf-intel/compcov feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode laf-intel/compcov feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode laf-intel/compcov feature compilation failed" - CODE=1 - } - rm -f test-compcov.compcov test.out - AFL_LLVM_INSTRUMENT=AFL AFL_LLVM_LAF_SPLIT_FLOATS=1 ../afl-clang-fast -o test-floatingpoint test-floatingpoint.c >errors 2>&1 - test -e test-floatingpoint && { - mkdir -p in - echo ZZZZ > in/in - $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 30 seconds" - { - AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -s 1 -V30 -m ${MEM_LIMIT} -i in -o out -- ./test-floatingpoint >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/crashes/id:* 2>/dev/null )" && { - $ECHO "$GREEN[+] llvm_mode laf-intel floatingpoint splitting feature works correctly" - } || { - cat errors - $ECHO "$RED[!] llvm_mode laf-intel floatingpoint splitting feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode laf-intel floatingpoint splitting feature compilation failed" - CODE=1 - } - rm -f test-floatingpoint test.out in/in - echo foobar.c > instrumentlist.txt - AFL_DEBUG=1 AFL_LLVM_INSTRUMENT_FILE=instrumentlist.txt ../afl-clang-fast -o test-compcov test-compcov.c > test.out 2>&1 - test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { - grep -q "No instrumentation targets found" test.out && { - $ECHO "$GREEN[+] llvm_mode instrumentlist feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode instrumentlist feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode instrumentlist feature compilation failed" - CODE=1 - } - rm -f test-compcov test.out instrumentlist.txt - AFL_LLVM_CMPLOG=1 ../afl-clang-fast -o test-cmplog test-cmplog.c > /dev/null 2>&1 - test -e test-cmplog && { - $ECHO "$GREY[*] running afl-fuzz for llvm_mode cmplog, this will take approx 10 seconds" - { - mkdir -p in - echo 0000000000000000000000000 > in/in - ../afl-fuzz -m none -V10 -i in -o out -c./test-cmplog -- ./test-cmplog >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode cmplog" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode cmplog" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] we cannot test llvm_mode cmplog because it is not present" - INCOMPLETE=1 - } - rm -rf errors test-cmplog in - ../afl-clang-fast -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 - test -e test-persistent && { - echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { - $ECHO "$GREEN[+] llvm_mode persistent mode feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode persistent mode feature failed to work" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode persistent mode feature compilation failed" - CODE=1 - } - rm -f test-persistent -} || { - $ECHO "$YELLOW[-] llvm_mode not compiled, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Testing: LTO llvm_mode" -test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { - # on FreeBSD need to set AFL_CC - test `uname -s` = 'FreeBSD' && { - if type clang >/dev/null; then - export AFL_CC=`command -v clang` - else - export AFL_CC=`$LLVM_CONFIG --bindir`/clang - fi - } - - ../afl-clang-lto -o test-instr.plain ../test-instr.c > /dev/null 2>&1 - test -e test-instr.plain && { - $ECHO "$GREEN[+] llvm_mode LTO compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 - test -e test-instr.plain.0 -a -e test-instr.plain.1 && { - diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] llvm_mode LTO instrumentation should be different on different input but is not" - CODE=1 - } || { - $ECHO "$GREEN[+] llvm_mode LTO instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { - $ECHO "$GREEN[+] llvm_mode LTO run reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] llvm_mode LTO instrumentation produces weird numbers: $TUPLES" - CODE=1 - } - } - } || { - $ECHO "$RED[!] llvm_mode LTO instrumentation failed" - CODE=1 - } - rm -f test-instr.plain.0 test-instr.plain.1 - } || { - $ECHO "$RED[!] LTO llvm_mode failed" - CODE=1 - } - rm -f test-instr.plain - - echo foobar.c > instrumentlist.txt - AFL_DEBUG=1 AFL_LLVM_INSTRUMENT_FILE=instrumentlist.txt ../afl-clang-lto -o test-compcov test-compcov.c > test.out 2>&1 - test -e test-compcov && { - grep -q "No instrumentation targets found" test.out && { - $ECHO "$GREEN[+] llvm_mode LTO instrumentlist feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode LTO instrumentlist feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode LTO instrumentlist feature compilation failed" - CODE=1 - } - rm -f test-compcov test.out instrumentlist.txt - ../afl-clang-lto -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 - test -e test-persistent && { - echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { - $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" - } || { - $ECHO "$RED[!] llvm_mode LTO persistent mode feature failed to work" - CODE=1 - } - } || { - $ECHO "$RED[!] llvm_mode LTO persistent mode feature compilation failed" - CODE=1 - } - rm -f test-persistent -} || { - $ECHO "$YELLOW[-] LTO llvm_mode not compiled, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Testing: gcc_plugin" -test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && { - SAVE_AFL_CC=${AFL_CC} - export AFL_CC=`command -v gcc` - ../afl-gcc-fast -o test-instr.plain.gccpi ../test-instr.c > /dev/null 2>&1 - AFL_HARDEN=1 ../afl-gcc-fast -o test-compcov.harden.gccpi test-compcov.c > /dev/null 2>&1 - test -e test-instr.plain.gccpi && { - $ECHO "$GREEN[+] gcc_plugin compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain.gccpi > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain.gccpi < /dev/null > /dev/null 2>&1 - test -e test-instr.plain.0 -a -e test-instr.plain.1 && { - diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] gcc_plugin instrumentation should be different on different input but is not" - CODE=1 - } || { - $ECHO "$GREEN[+] gcc_plugin instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { - $ECHO "$GREEN[+] gcc_plugin run reported $TUPLES instrumented locations which is fine" - } || { - $ECHO "$RED[!] gcc_plugin instrumentation produces a weird numbers: $TUPLES" - $ECHO "$YELLOW[-] this is a known issue in gcc, not afl++. It is not flagged as an error because travis builds would all fail otherwise :-(" - #CODE=1 - } - } - } || { - $ECHO "$RED[!] gcc_plugin instrumentation failed" - CODE=1 - } - rm -f test-instr.plain.0 test-instr.plain.1 - } || { - $ECHO "$RED[!] gcc_plugin failed" - CODE=1 - } - - test -e test-compcov.harden.gccpi && test_compcov_binary_functionality ./test-compcov.harden.gccpi && { - grep -Eq$GREPAOPTION 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden.gccpi > /dev/null 2>&1 && { - $ECHO "$GREEN[+] gcc_plugin hardened mode succeeded and is working" - } || { - $ECHO "$RED[!] gcc_plugin hardened mode is not hardened" - CODE=1 - } - rm -f test-compcov.harden.gccpi - } || { - $ECHO "$RED[!] gcc_plugin hardened mode compilation failed" - CODE=1 - } - # now we want to be sure that afl-fuzz is working - (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && { - $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET" - true - }) || - # make sure crash reporter is disabled on Mac OS X - (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { - $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" - CODE=1 - true - }) || { - mkdir -p in - echo 0 > in/in - $ECHO "$GREY[*] running afl-fuzz for gcc_plugin, this will take approx 10 seconds" - { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain.gccpi >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with gcc_plugin" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with gcc_plugin" - CODE=1 - } - rm -rf in out errors - } - rm -f test-instr.plain.gccpi - - # now for the special gcc_plugin things - echo foobar.c > instrumentlist.txt - AFL_GCC_INSTRUMENT_FILE=instrumentlist.txt ../afl-gcc-fast -o test-compcov test-compcov.c > /dev/null 2>&1 - test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { - echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 1 tuples" && { - $ECHO "$GREEN[+] gcc_plugin instrumentlist feature works correctly" - } || { - $ECHO "$RED[!] gcc_plugin instrumentlist feature failed" - CODE=1 - } - } || { - $ECHO "$RED[!] gcc_plugin instrumentlist feature compilation failed" - CODE=1 - } - rm -f test-compcov test.out instrumentlist.txt - ../afl-gcc-fast -o test-persistent ../examples/persistent_demo/persistent_demo.c > /dev/null 2>&1 - test -e test-persistent && { - echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { - $ECHO "$GREEN[+] gcc_plugin persistent mode feature works correctly" - } || { - $ECHO "$RED[!] gcc_plugin persistent mode feature failed to work" - CODE=1 - } - } || { - $ECHO "$RED[!] gcc_plugin persistent mode feature compilation failed" - CODE=1 - } - rm -f test-persistent - export AFL_CC=${SAVE_AFL_CC} -} || { - $ECHO "$YELLOW[-] gcc_plugin not compiled, cannot test" - INCOMPLETE=1 -} - -test -z "$AFL_CC" && unset AFL_CC - -$ECHO "$BLUE[*] Testing: shared library extensions" -cc $CFLAGS -o test-compcov test-compcov.c > /dev/null 2>&1 -test -e ../libtokencap.so && { - AFL_TOKEN_FILE=token.out LD_PRELOAD=../libtokencap.so DYLD_INSERT_LIBRARIES=../libtokencap.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov foobar > /dev/null 2>&1 - grep -q BUGMENOT token.out > /dev/null 2>&1 && { - $ECHO "$GREEN[+] libtokencap did successfully capture tokens" - } || { - $ECHO "$RED[!] libtokencap did not capture tokens" - CODE=1 - } - rm -f token.out -} || { - $ECHO "$YELLOW[-] libtokencap is not compiled, cannot test" - INCOMPLETE=1 -} -test -e ../libdislocator.so && { - { - ulimit -c 1 - # DYLD_INSERT_LIBRARIES and DYLD_FORCE_FLAT_NAMESPACE is used on Darwin/MacOSX - LD_PRELOAD=../libdislocator.so DYLD_INSERT_LIBRARIES=../libdislocator.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov BUFFEROVERFLOW > test.out 2>/dev/null - } > /dev/null 2>&1 - grep -q BUFFEROVERFLOW test.out > /dev/null 2>&1 && { - $ECHO "$RED[!] libdislocator did not detect the memory corruption" - CODE=1 - } || { - $ECHO "$GREEN[+] libdislocator did successfully detect the memory corruption" - } - rm -f test.out core test-compcov.core core.test-compcov -} || { - $ECHO "$YELLOW[-] libdislocator is not compiled, cannot test" - INCOMPLETE=1 -} -rm -f test-compcov -#test -e ../libradamsa.so && { -# # on FreeBSD need to set AFL_CC -# test `uname -s` = 'FreeBSD' && { -# if type clang >/dev/null; then -# export AFL_CC=`command -v clang` -# else -# export AFL_CC=`$LLVM_CONFIG --bindir`/clang -# fi -# } -# test -e test-instr.plain || ../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 -# test -e test-instr.plain || ../afl-gcc-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1 -# test -e test-instr.plain || ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1 -# test -e test-instr.plain && { -# mkdir -p in -# printf 1 > in/in -# $ECHO "$GREY[*] running afl-fuzz with radamsa, this will take approx 10 seconds" -# { -# ../afl-fuzz -RR -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain -# } >>errors 2>&1 -# test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { -# $ECHO "$GREEN[+] libradamsa performs good - and very slow - mutations" -# } || { -# echo CUT------------------------------------------------------------------CUT -# cat errors -# echo CUT------------------------------------------------------------------CUT -# $ECHO "$RED[!] libradamsa failed" -# CODE=1 -# } -# rm -rf in out errors test-instr.plain -# } || { -# $ECHO "$YELLOW[-] compilation of test target failed, cannot test libradamsa" -# INCOMPLETE=1 -# } -#} || { -# $ECHO "$YELLOW[-] libradamsa is not compiled, cannot test" -# INCOMPLETE=1 -#} - -test -z "$AFL_CC" && { - if type gcc >/dev/null; then - export AFL_CC=gcc - else - if type clang >/dev/null; then - export AFL_CC=clang - fi - fi -} - -$ECHO "$BLUE[*] Testing: qemu_mode" -test -e ../afl-qemu-trace && { - cc -pie -fPIE -o test-instr ../test-instr.c - cc -o test-compcov test-compcov.c - test -e test-instr -a -e test-compcov && { - { - mkdir -p in - echo 00000 > in/in - $ECHO "$GREY[*] running afl-fuzz for qemu_mode, this will take approx 10 seconds" - { - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode" - RUNTIME=`grep execs_done out/fuzzer_stats | awk '{print$3}'` - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode" - CODE=1 - } - rm -f errors - - $ECHO "$GREY[*] running afl-fuzz for qemu_mode AFL_ENTRYPOINT, this will take approx 6 seconds" - { - { - if file test-instr | grep -q "32-bit"; then - # for 32-bit reduce 8 nibbles to the lower 7 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` - else - # for 64-bit reduce 16 nibbles to the lower 9 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` - fi - export AFL_ENTRYPOINT=`expr 0x4${ADDR_LOWER_PART}` - $ECHO AFL_ENTRYPOINT=$AFL_ENTRYPOINT - $(nm test-instr | grep "T main") - $(file ./test-instr) - ../afl-fuzz -m ${MEM_LIMIT} -V2 -Q -i in -o out -- ./test-instr - unset AFL_ENTRYPOINT - } >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode AFL_ENTRYPOINT" - RUNTIME=`grep execs_done out/fuzzer_stats | awk '{print$3}'` - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode AFL_ENTRYPOINT" - CODE=1 - } - rm -f errors - - test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { - test -e ../libcompcov.so && { - $ECHO "$GREY[*] running afl-fuzz for qemu_mode compcov, this will take approx 10 seconds" - { - export AFL_PRELOAD=../libcompcov.so - export AFL_COMPCOV_LEVEL=2 - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-compcov >>errors 2>&1 - unset AFL_PRELOAD - unset AFL_COMPCOV_LEVEL - } >>errors 2>&1 - test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode compcov" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode compcov" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] we cannot test qemu_mode compcov because it is not present" - INCOMPLETE=1 - } - rm -f errors - } || { - $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode compcov" - } - - test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { - $ECHO "$GREY[*] running afl-fuzz for qemu_mode cmplog, this will take approx 10 seconds" - { - ../afl-fuzz -m none -V10 -Q -c 0 -i in -o out -- ./test-compcov >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode cmplog" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode cmplog" - CODE=1 - } - rm -f errors - } || { - $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode cmplog" - } - - test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { - $ECHO "$GREY[*] running afl-fuzz for persistent qemu_mode, this will take approx 10 seconds" - { - if file test-instr | grep -q "32-bit"; then - # for 32-bit reduce 8 nibbles to the lower 7 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` - else - # for 64-bit reduce 16 nibbles to the lower 9 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` - fi - export AFL_QEMU_PERSISTENT_ADDR=`expr 0x4${ADDR_LOWER_PART}` - export AFL_QEMU_PERSISTENT_GPR=1 - $ECHO "Info: AFL_QEMU_PERSISTENT_ADDR=$AFL_QEMU_PERSISTENT_ADDR <= $(nm test-instr | grep "T main" | awk '{print $1}')" - env|grep AFL_|sort - file test-instr - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr - unset AFL_QEMU_PERSISTENT_ADDR - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with persistent qemu_mode" - RUNTIMEP=`grep execs_done out/fuzzer_stats | awk '{print$3}'` - test -n "$RUNTIME" -a -n "$RUNTIMEP" && { - DIFF=`expr $RUNTIMEP / $RUNTIME` - test "$DIFF" -gt 1 && { # must be at least twice as fast - $ECHO "$GREEN[+] persistent qemu_mode was noticeable faster than standard qemu_mode" - } || { - $ECHO "$YELLOW[-] persistent qemu_mode was not noticeable faster than standard qemu_mode" - } - } || { - $ECHO "$YELLOW[-] we got no data on executions performed? weird!" - } - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with persistent qemu_mode" - CODE=1 - } - rm -rf in out errors - } || { - $ECHO "$YELLOW[-] not an intel or arm platform, cannot test persistent qemu_mode" - } - - test -e ../qemu_mode/unsigaction/unsigaction32.so && { - ${AFL_CC} -o test-unsigaction32 -m32 test-unsigaction.c >> errors 2>&1 && { - ./test-unsigaction32 - RETVAL_NORMAL32=$? - LD_PRELOAD=../qemu_mode/unsigaction/unsigaction32.so ./test-unsigaction32 - RETVAL_LIBUNSIGACTION32=$? - test $RETVAL_NORMAL32 = "2" -a $RETVAL_LIBUNSIGACTION32 = "0" && { - $ECHO "$GREEN[+] qemu_mode unsigaction library (32 bit) ignores signals" - } || { - test $RETVAL_NORMAL32 != "2" && { - $ECHO "$RED[!] cannot trigger signal in test program (32 bit)" - } - test $RETVAL_LIBUNSIGACTION32 != "0" && { - $ECHO "$RED[!] signal in test program (32 bit) is not ignored with unsigaction" - } - CODE=1 - } - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] cannot compile test program (32 bit) for unsigaction library" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (32 bit) because it is not present" - INCOMPLETE=1 - } - test -e ../qemu_mode/unsigaction/unsigaction64.so && { - ${AFL_CC} -o test-unsigaction64 -m64 test-unsigaction.c >> errors 2>&1 && { - ./test-unsigaction64 - RETVAL_NORMAL64=$? - LD_PRELOAD=../qemu_mode/unsigaction/unsigaction64.so ./test-unsigaction64 - RETVAL_LIBUNSIGACTION64=$? - test $RETVAL_NORMAL64 = "2" -a $RETVAL_LIBUNSIGACTION64 = "0" && { - $ECHO "$GREEN[+] qemu_mode unsigaction library (64 bit) ignores signals" - } || { - test $RETVAL_NORMAL64 != "2" && { - $ECHO "$RED[!] cannot trigger signal in test program (64 bit)" - } - test $RETVAL_LIBUNSIGACTION64 != "0" && { - $ECHO "$RED[!] signal in test program (64 bit) is not ignored with unsigaction" - } - CODE=1 - } - unset LD_PRELOAD - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] cannot compile test program (64 bit) for unsigaction library" - CODE=1 - } - } || { - $ECHO "$YELLOW[-] we cannot test qemu_mode unsigaction library (64 bit) because it is not present" - INCOMPLETE=1 - } - rm -rf errors test-unsigaction32 test-unsigaction64 - } - } || { - $ECHO "$RED[!] gcc compilation of test targets failed - what is going on??" - CODE=1 - } - - rm -f test-instr test-compcov -} || { - $ECHO "$YELLOW[-] qemu_mode is not compiled, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Testing: unicorn_mode" -test -d ../unicorn_mode/unicornafl && { - test -e ../unicorn_mode/samples/simple/simple_target.bin -a -e ../unicorn_mode/samples/compcov_x64/compcov_target.bin && { - { - # We want to see python errors etc. in logs, in case something doesn't work - export AFL_DEBUG_CHILD_OUTPUT=1 - - # some python version should be available now - PYTHONS="`command -v python3` `command -v python` `command -v python2`" - EASY_INSTALL_FOUND=0 - for PYTHON in $PYTHONS ; do - - if $PYTHON -c "help('easy_install');" </dev/null | grep -q module ; then - - EASY_INSTALL_FOUND=1 - PY=$PYTHON - break - - fi - - done - if [ "0" = $EASY_INSTALL_FOUND ]; then - - echo "[-] Error: Python setup-tools not found. Run 'sudo apt-get install python-setuptools'." - PREREQ_NOTFOUND=1 - - fi - - - cd ../unicorn_mode/samples/persistent - make >>errors 2>&1 - $ECHO "$GREY[*] running afl-fuzz for unicorn_mode (persistent), this will take approx 25 seconds" - AFL_DEBUG_CHILD_OUTPUT=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode (persistent)" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode (persistent)" - CODE=1 - } - - rm -rf out errors >/dev/null - make clean >/dev/null - cd ../../../test - - # travis workaround - test "$PY" = "/opt/pyenv/shims/python" -a -x /usr/bin/python && PY=/usr/bin/python - mkdir -p in - echo 0 > in/in - $ECHO "$GREY[*] Using python binary $PY" - if ! $PY -c 'import unicornafl' 2>/dev/null ; then - $ECHO "$YELLOW[-] we cannot test unicorn_mode for python because it is not present" - INCOMPLETE=1 - else - { - $ECHO "$GREY[*] running afl-fuzz for unicorn_mode in python, this will take approx 25 seconds" - { - ../afl-fuzz -m ${MEM_LIMIT} -V25 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/simple/simple_test_harness.py @@ >>errors 2>&1 - } >>errors 2>&1 - test -n "$( ls out/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode" - CODE=1 - } - rm -f errors - - printf '\x01\x01' > in/in - # This seed is close to the first byte of the comparison. - # If CompCov works, a new tuple will appear in the map => new input in queue - $ECHO "$GREY[*] running afl-fuzz for unicorn_mode compcov, this will take approx 35 seconds" - { - export AFL_COMPCOV_LEVEL=2 - ../afl-fuzz -m ${MEM_LIMIT} -V35 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ >>errors 2>&1 - unset AFL_COMPCOV_LEVEL - } >>errors 2>&1 - test -n "$( ls out/queue/id:000001* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode compcov" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode compcov" - CODE=1 - } - rm -rf in out errors - } - fi - - unset AFL_DEBUG_CHILD_OUTPUT - - } - } || { - $ECHO "$RED[!] missing sample binaries in unicorn_mode/samples/ - what is going on??" - CODE=1 - } - -} || { - $ECHO "$YELLOW[-] unicorn_mode is not compiled, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Testing: custom mutator" -test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { - # normalize path - CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../examples/custom_mutators;pwd) - test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && { - unset AFL_CC - # Compile the vulnerable program for single mutator - test -e ../afl-clang-fast && { - ../afl-clang-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 - } || { - test -e ../afl-gcc-fast && { - ../afl-gcc-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 - } || { - ../afl-gcc -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 - } - } - # Compile the vulnerable program for multiple mutators - test -e ../afl-clang-fast && { - ../afl-clang-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 - } || { - test -e ../afl-gcc-fast && { - ../afl-gcc-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 - } || { - ../afl-gcc -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 - } - } - # Compile the custom mutator - cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 - cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 - test -e test-custom-mutator -a -e ./libexamplemutator.so && { - # Create input directory - mkdir -p in - echo "00000" > in/in - - # Run afl-fuzz w/ the C mutator - $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 5 seconds" - { - AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 - } >>errors 2>&1 - - # Check results - test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here - $ECHO "$GREEN[+] afl-fuzz is working correctly with the C mutator" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with the C mutator" - CODE=1 - } - - # Clean - rm -rf out errors - - # Run afl-fuzz w/ multiple C mutators - $ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 5 seconds" - { - AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1 - } >>errors 2>&1 - - test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here - $ECHO "$GREEN[+] afl-fuzz is working correctly with multiple C mutators" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with multiple C mutators" - CODE=1 - } - - # Clean - rm -rf out errors - - # Run afl-fuzz w/ the Python mutator - $ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 5 seconds" - { - export PYTHONPATH=${CUSTOM_MUTATOR_PATH} - export AFL_PYTHON_MODULE=example - AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V5 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 - unset PYTHONPATH - unset AFL_PYTHON_MODULE - } >>errors 2>&1 - - # Check results - test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && { # TODO: update here - $ECHO "$GREEN[+] afl-fuzz is working correctly with the Python mutator" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with the Python mutator" - CODE=1 - } - - # Clean - rm -rf in out errors - rm -rf ${CUSTOM_MUTATOR_PATH}/__pycache__/ - rm -f test-multiple-mutators test-custom-mutator libexamplemutator.so libexamplemutator2.so - } || { - ls . - ls ${CUSTOM_MUTATOR_PATH} - $ECHO "$RED[!] cannot compile the test program or the custom mutator" - CODE=1 - } - - #test "$CODE" = 1 && { $ECHO "$YELLOW[!] custom mutator tests currently will not fail travis" ; CODE=0 ; } - - make -C ../examples/custom_mutators clean > /dev/null 2>&1 - rm -f test-custom-mutator - rm -f test-custom-mutators - } || { - $ECHO "$YELLOW[-] no custom mutators in $CUSTOM_MUTATOR_PATH, cannot test" - INCOMPLETE=1 - } - unset CUSTOM_MUTATOR_PATH -} || { - $ECHO "$YELLOW[-] no python support in afl-fuzz, cannot test" - INCOMPLETE=1 -} - -$ECHO "$BLUE[*] Execution cmocka Unit-Tests $GREY" -unset AFL_CC -make -C .. unit || CODE=1 INCOMPLETE=1 : - -$ECHO "$GREY[*] all test cases completed.$RESET" -test "$INCOMPLETE" = "0" && $ECHO "$GREEN[+] all test cases executed" -test "$INCOMPLETE" = "1" && $ECHO "$YELLOW[-] not all test cases were executed" -test "$CODE" = "0" && $ECHO "$GREEN[+] all tests were successful :-)$RESET" -test "$CODE" = "0" || $ECHO "$RED[!] failure in tests :-($RESET" -exit $CODE diff --git a/test/travis/bionic/Dockerfile b/test/travis/bionic/Dockerfile index d1b53e70..00ab96f9 100644 --- a/test/travis/bionic/Dockerfile +++ b/test/travis/bionic/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get update && apt-get -y install \ ENV AFL_NO_UI=1 ENV AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 +ENV LLVM_CONFIG=llvm-config-6.0 RUN cd / && \ git clone https://github.com/AFLplusplus/AFLplusplus && \ diff --git a/test/unittests/unit_hash.c b/test/unittests/unit_hash.c index 041d107a..22245ed6 100644 --- a/test/unittests/unit_hash.c +++ b/test/unittests/unit_hash.c @@ -30,6 +30,7 @@ extern void exit(int status); extern void __real_exit(int status); void __wrap_exit(int status); void __wrap_exit(int status) { + (void)status; assert(0); } @@ -39,11 +40,13 @@ extern int printf(const char *format, ...); extern int __real_printf(const char *format, ...); int __wrap_printf(const char *format, ...); int __wrap_printf(const char *format, ...) { + (void)format; return 1; } /* Rand with 0 seed would broke in the past */ static void test_hash(void **state) { + (void)state; char bitmap[64] = {0}; u64 hash0 = hash64(bitmap, sizeof(bitmap), 0xa5b35705); @@ -62,6 +65,8 @@ static void test_hash(void **state) { } int main(int argc, char **argv) { + (void)argc; + (void)argv; const struct CMUnitTest tests[] = { cmocka_unit_test(test_hash) diff --git a/test/unittests/unit_list.c b/test/unittests/unit_list.c index 4c2063b6..43665f1a 100644 --- a/test/unittests/unit_list.c +++ b/test/unittests/unit_list.c @@ -27,23 +27,26 @@ extern void mock_assert(const int result, const char* const expression, (compile with `--wrap=exit`) */ extern void exit(int status); extern void __real_exit(int status); -void __wrap_exit(int status); +//void __wrap_exit(int status); void __wrap_exit(int status) { + (void)status; assert(0); } /* ignore all printfs */ #undef printf extern int printf(const char *format, ...); -extern int __real_printf(const char *format, ...); +//extern int __real_printf(const char *format, ...); int __wrap_printf(const char *format, ...); int __wrap_printf(const char *format, ...) { + (void)format; return 1; } static list_t testlist = {.element_prealloc_count = 0}; static void test_contains(void **state) { + (void)state; u32 one = 1; u32 two = 2; @@ -56,6 +59,7 @@ static void test_contains(void **state) { } static void test_foreach(void **state) { + (void)state; u32 one = 1; u32 two = 2; @@ -75,6 +79,7 @@ static void test_foreach(void **state) { } static void test_long_list(void **state) { + (void)state; u32 result1 = 0; u32 result2 = 0; @@ -118,6 +123,8 @@ static void test_long_list(void **state) { } int main(int argc, char **argv) { + (void)argc; + (void)argv; const struct CMUnitTest tests[] = { cmocka_unit_test(test_contains), diff --git a/test/unittests/unit_maybe_alloc.c b/test/unittests/unit_maybe_alloc.c index 429d38ed..e452e2f2 100644 --- a/test/unittests/unit_maybe_alloc.c +++ b/test/unittests/unit_maybe_alloc.c @@ -28,6 +28,7 @@ void __wrap_exit(int status); extern void exit(int status); extern void __real_exit(int status); void __wrap_exit(int status) { + (void) status; assert(0); } @@ -35,12 +36,30 @@ int __wrap_printf(const char *format, ...); /* ignore all printfs */ #undef printf extern int printf(const char *format, ...); -extern int __real_printf(const char *format, ...); +//extern int __real_printf(const char *format, ...); int __wrap_printf(const char *format, ...) { + (void)format; return 1; } -#define BUF_PARAMS (void **)&buf, &size +#define VOID_BUF (void **)&buf + +static void *create_fake_maybe_grow_of(size_t size) { + + size += AFL_ALLOC_SIZE_OFFSET; + + // fake a realloc buf + + struct afl_alloc_buf *buf = malloc(size); + if (!buf) { + perror("Could not allocate fake buf"); + return NULL; + } + buf->complete_size = size; // The size + void *actual_buf = (void *)(buf->buf); + return actual_buf; + +} /* static int setup(void **state) { @@ -50,90 +69,132 @@ static int setup(void **state) { } */ +static void test_pow2(void **state) { + (void)state; + + assert_int_equal(next_pow2(64), 64); + assert_int_equal(next_pow2(63), 64); + assert_int_not_equal(next_pow2(65), 65); + assert_int_equal(next_pow2(0x100), 0x100); + assert_int_equal(next_pow2(0x180), 0x200); + assert_int_equal(next_pow2(108), 0x80); + assert_int_equal(next_pow2(0), 0); + assert_int_equal(next_pow2(1), 1); + assert_int_equal(next_pow2(2), 2); + assert_int_equal(next_pow2(3), 4); + assert_int_equal(next_pow2(0xFFFFFF), 0x1000000); + assert_int_equal(next_pow2(0xFFFFFFF), 0x10000000); + assert_int_equal(next_pow2(0xFFFFFF0), 0x10000000); + assert_int_equal(next_pow2(SIZE_MAX), 0); + assert_int_equal(next_pow2(-1), 0); + assert_int_equal(next_pow2(-2), 0); + +} + static void test_null_allocs(void **state) { + (void)state; void *buf = NULL; - size_t size = 0; - void *ptr = ck_maybe_grow(BUF_PARAMS, 100); + void *ptr = afl_realloc(VOID_BUF, 100); + if (unlikely(!buf)) { PFATAL("alloc"); } + size_t size = afl_alloc_bufsize(buf); assert_true(buf == ptr); assert_true(size >= 100); - ck_free(ptr); + afl_free(ptr); } static void test_nonpow2_size(void **state) { + (void)state; + + char *buf = create_fake_maybe_grow_of(150); - char *buf = ck_alloc(150); - size_t size = 150; buf[140] = '5'; - char *ptr = ck_maybe_grow(BUF_PARAMS, 160); + + char *ptr = afl_realloc(VOID_BUF, 160); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size_t size = afl_alloc_bufsize(buf); assert_ptr_equal(buf, ptr); assert_true(size >= 160); assert_true(buf[140] == '5'); - ck_free(ptr); + afl_free(ptr); } static void test_zero_size(void **state) { + (void)state; char *buf = NULL; size_t size = 0; - assert_non_null(maybe_grow(BUF_PARAMS, 0)); - free(buf); + char *new_buf = afl_realloc(VOID_BUF, 0); + assert_non_null(new_buf); + assert_ptr_equal(buf, new_buf); + afl_free(buf); buf = NULL; size = 0; - char *ptr = ck_maybe_grow(BUF_PARAMS, 100); + char *ptr = afl_realloc(VOID_BUF, 100); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size = afl_alloc_bufsize(buf); assert_non_null(ptr); assert_ptr_equal(buf, ptr); assert_true(size >= 100); - expect_assert_failure(ck_maybe_grow(BUF_PARAMS, 0)); - - ck_free(ptr); + afl_free(ptr); } + static void test_unchanged_size(void **state) { + (void)state; + + // fake a realloc buf + void *actual_buf = create_fake_maybe_grow_of(100); - void *buf = ck_alloc(100); - size_t size = 100; - void *buf_before = buf; - void *buf_after = ck_maybe_grow(BUF_PARAMS, 100); - assert_ptr_equal(buf, buf_after); + void *buf_before = actual_buf; + void *buf_after = afl_realloc(&actual_buf, 100); + if (unlikely(!buf_after)) { PFATAL("alloc"); } + assert_ptr_equal(actual_buf, buf_after); assert_ptr_equal(buf_after, buf_before); - ck_free(buf); + afl_free(buf_after); } static void test_grow_multiple(void **state) { + (void)state; char *buf = NULL; size_t size = 0; - char *ptr = ck_maybe_grow(BUF_PARAMS, 100); + char *ptr = afl_realloc(VOID_BUF, 100); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size = afl_alloc_bufsize(ptr); assert_ptr_equal(ptr, buf); assert_true(size >= 100); - assert_int_equal(size, next_pow2(size)); + assert_int_equal(size, next_pow2(size) - AFL_ALLOC_SIZE_OFFSET); buf[50] = '5'; - ptr = (char *)ck_maybe_grow(BUF_PARAMS, 1000); + ptr = (char *)afl_realloc(VOID_BUF, 1000); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size = afl_alloc_bufsize(ptr); assert_ptr_equal(ptr, buf); assert_true(size >= 100); - assert_int_equal(size, next_pow2(size)); + assert_int_equal(size, next_pow2(size) - AFL_ALLOC_SIZE_OFFSET); buf[500] = '5'; - ptr = (char *)ck_maybe_grow(BUF_PARAMS, 10000); + ptr = (char *)afl_realloc(VOID_BUF, 10000); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size = afl_alloc_bufsize(ptr); assert_ptr_equal(ptr, buf); assert_true(size >= 10000); - assert_int_equal(size, next_pow2(size)); + assert_int_equal(size, next_pow2(size) - AFL_ALLOC_SIZE_OFFSET); buf[5000] = '5'; assert_int_equal(buf[50], '5'); assert_int_equal(buf[500], '5'); assert_int_equal(buf[5000], '5'); - ck_free(buf); + afl_free(buf); } @@ -146,8 +207,11 @@ static int teardown(void **state) { */ int main(int argc, char **argv) { + (void)argc; + (void)argv; const struct CMUnitTest tests[] = { + cmocka_unit_test(test_pow2), cmocka_unit_test(test_null_allocs), cmocka_unit_test(test_nonpow2_size), cmocka_unit_test(test_zero_size), diff --git a/test/unittests/unit_preallocable.c b/test/unittests/unit_preallocable.c index b0963a15..ea16da85 100644 --- a/test/unittests/unit_preallocable.c +++ b/test/unittests/unit_preallocable.c @@ -29,6 +29,7 @@ extern void exit(int status); extern void __real_exit(int status); void __wrap_exit(int status); void __wrap_exit(int status) { + (void)status; assert(0); } @@ -36,8 +37,9 @@ void __wrap_exit(int status) { #undef printf extern int printf(const char *format, ...); extern int __real_printf(const char *format, ...); -int __wrap_printf(const char *format, ...); +//int __wrap_printf(const char *format, ...); int __wrap_printf(const char *format, ...) { + (void)format; return 1; } @@ -51,9 +53,10 @@ typedef struct prealloc_me #define PREALLOCED_BUF_SIZE (64) prealloc_me_t prealloc_me_buf[PREALLOCED_BUF_SIZE]; -size_t prealloc_me_size = 0; +s32 prealloc_me_size = 0; static void test_alloc_free(void **state) { + (void)state; prealloc_me_t *prealloced = NULL; PRE_ALLOC(prealloced, prealloc_me_buf, PREALLOCED_BUF_SIZE, prealloc_me_size); @@ -63,6 +66,7 @@ static void test_alloc_free(void **state) { } static void test_prealloc_overflow(void **state) { + (void)state; u32 i = 0; prealloc_me_t *prealloced[PREALLOCED_BUF_SIZE + 10]; @@ -102,6 +106,8 @@ static void test_prealloc_overflow(void **state) { } int main(int argc, char **argv) { + (void)argc; + (void)argv; const struct CMUnitTest tests[] = { cmocka_unit_test(test_alloc_free), diff --git a/test/unittests/unit_rand.c b/test/unittests/unit_rand.c index 0a90d8d1..1ad02a80 100644 --- a/test/unittests/unit_rand.c +++ b/test/unittests/unit_rand.c @@ -29,8 +29,9 @@ extern void mock_assert(const int result, const char* const expression, (compile with `--wrap=exit`) */ extern void exit(int status); extern void __real_exit(int status); -void __wrap_exit(int status); +//void __wrap_exit(int status); void __wrap_exit(int status) { + (void)status; assert(0); } @@ -40,11 +41,13 @@ extern int printf(const char *format, ...); extern int __real_printf(const char *format, ...); int __wrap_printf(const char *format, ...); int __wrap_printf(const char *format, ...) { + (void)format; return 1; } /* Rand with 0 seed would broke in the past */ static void test_rand_0(void **state) { + (void)state; afl_state_t afl = {0}; rand_set_seed(&afl, 0); @@ -58,6 +61,7 @@ static void test_rand_0(void **state) { } static void test_rand_below(void **state) { + (void)state; afl_state_t afl = {0}; rand_set_seed(&afl, 1337); @@ -70,6 +74,8 @@ static void test_rand_below(void **state) { } int main(int argc, char **argv) { + (void)argc; + (void)argv; const struct CMUnitTest tests[] = { cmocka_unit_test(test_rand_0), |