diff options
35 files changed, 669 insertions, 591 deletions
diff --git a/GNUmakefile b/GNUmakefile index 027ebfd9..ae529ece 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -97,7 +97,13 @@ ifneq "$(shell uname -m)" "x86_64" endif endif -CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) +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)\" @@ -305,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 "==========================================" diff --git a/README.md b/README.md index 6e324cb0..b3dc5e45 100644 --- a/README.md +++ b/README.md @@ -97,21 +97,20 @@ only new bytes in the other cycle. 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) + * 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). * 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 :-) @@ -212,6 +211,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 @@ -228,6 +228,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: @@ -305,15 +306,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 @@ -341,12 +344,15 @@ 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 +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: @@ -421,6 +427,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 @@ -469,23 +476,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 @@ -507,7 +515,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 @@ -535,8 +543,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. @@ -568,7 +576,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`. @@ -586,7 +594,21 @@ 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), @@ -599,6 +621,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. @@ -607,6 +634,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) diff --git a/TODO.md b/TODO.md index e81b82a3..e74fa1d5 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,7 @@ - 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/docs/Changelog.md b/docs/Changelog.md index 25c7a761..3c28ff98 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,18 +19,22 @@ 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 - llvm_mode: - now supports llvm 12! - support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm sancov, and also supports function matching! + - added neverzero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) + - fixes for llvm 4.0 + - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) - - LTO: skipping ctors and ifuncs in fix map address instrumentation + - LTO: laf-intel and redqueen/cmplog are now applied at link time + to prevent llvm optimizing away the splits - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/docs/FAQ.md b/docs/FAQ.md index e690635a..997f4c40 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -3,9 +3,11 @@ ## 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) + 2. [How do I fuzz a network service?](#how-to-fuzz-a-network-service) + 3. [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program) + 4. [What is an edge?](#what-is-an-edge) + 5. [Why is my stability below 100%?](#why-is-my-stability-below-100) + 6. [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) @@ -20,6 +22,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. 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/include/afl-fuzz.h b/include/afl-fuzz.h index e251183c..ad7b0cd6 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -177,6 +177,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 { @@ -583,8 +591,9 @@ typedef struct afl_state { 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 */ @@ -623,8 +632,6 @@ typedef struct afl_state { u32 document_counter; #endif - void *maybe_add_auto; - /* statistics file */ double last_bitmap_cvg, last_stability, last_eps; @@ -929,7 +936,7 @@ u8 has_new_bits(afl_state_t *, u8 *); void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32); void load_extras(afl_state_t *, u8 *); -void maybe_add_auto(void *, u8 *, u32); +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 *); diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 832b2de4..306cc622 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -177,43 +177,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_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(). */ @@ -222,8 +185,6 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { #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() @@ -489,55 +450,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 @@ -548,8 +460,6 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { #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() @@ -713,24 +623,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) { @@ -754,12 +646,6 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func, #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 */ diff --git a/include/forkserver.h b/include/forkserver.h index 89f23ab7..59a9f150 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -91,9 +91,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 (*autodict_func)(void *afl_ptr, u8 *mem, u32 len); } afl_forkserver_t; diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile index 49af8e11..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..." diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 4d8c4719..206e2682 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 || @@ -194,7 +194,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 +256,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_list.md b/llvm_mode/README.instrument_list.md index d4739dda..1fc06414 100644 --- a/llvm_mode/README.instrument_list.md +++ b/llvm_mode/README.instrument_list.md @@ -14,13 +14,13 @@ 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 should be compiled with or without instrumentation. +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 flexbility. +The llvm sancov list format is fully supported by afl++, however afl++ has +more flexibility. ## 2) Building the LLVM module @@ -35,13 +35,13 @@ 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 instrumentd (AFL_LLVM_DENYLIST). +(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 the instrument file list (to avoid +function/filename entry contained in this instrument file list (to avoid breaking the matching when absolute paths are used during compilation). -**NOTE:** In optimization functions might be inlined and then not match! +**NOTE:** In builds with optimization enabled functions might be inlined and would not match! For example if your source tree looks like this: ``` @@ -52,7 +52,7 @@ 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: +and you only want to test feature_a, then create a instrument file list file containing: ``` feature_a/a1.cpp feature_a/a2.cpp @@ -69,7 +69,7 @@ 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 intelligent to identify if an entry is a filename or a function. +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: ``` @@ -79,7 +79,7 @@ and function entries like this: ``` fun: MallocFoo ``` -Note that whitespace is ignored and comments (`# foo`) supported. +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. diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index efaba122..99b17430 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -246,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); + + } } @@ -282,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"; @@ -312,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) @@ -321,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; @@ -329,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 { @@ -965,6 +1021,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 0b50c547..d9e63bd3 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -67,8 +67,11 @@ bool isIgnoreFunction(const llvm::Function *F) { "__libc_csu", "__asan", "__msan", + "__cmplog", + "__sancov", "msan.", "LLVMFuzzer", + "__decide_deferred", "maybe_duplicate_stderr", "discard_output", "close_stdout", @@ -253,101 +256,63 @@ void initInstrumentList() { } -bool isInInstrumentList(llvm::Function *F) { - - // 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 we do not have a the instrument file list return true - if (!allowListFiles.empty() || !allowListFunctions.empty()) { - - if (!allowListFunctions.empty()) { - - std::string instFunction = F->getName().str(); - - for (std::list<std::string>::iterator it = allowListFunctions.begin(); - it != allowListFunctions.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", - instFunction.c_str()); - return true; - - } - - } - - } +void scanForDangerousFunctions(llvm::Module *M) { - } + if (!M) return; - if (!allowListFiles.empty()) { +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 9) - // let's try to get the filename for the function - auto bb = &F->getEntryBlock(); - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - DebugLoc Loc = IP->getDebugLoc(); + for (GlobalIFunc &IF : M->ifuncs()) { -#if LLVM_VERSION_MAJOR >= 4 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) - if (Loc) { + 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()); - DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); - - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); + } - if (instFilename.str().empty()) { + GlobalVariable *GV = M->getNamedGlobal("llvm.global_ctors"); + if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { + ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer()); - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); + if (InitList) { - } + for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { - } + if (ConstantStruct *CS = + dyn_cast<ConstantStruct>(InitList->getOperand(i))) { - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { + if (CS->getNumOperands() >= 2) { - for (std::list<std::string>::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { + if (CS->getOperand(1)->isNullValue()) + break; // Found a null terminator, stop here. - /* 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 */ + ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0)); + int Priority = CI ? CI->getSExtValue() : 0; - if (instFilename.str().length() >= it->length()) { + 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 (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + if (!F->isDeclaration() && + strncmp(F->getName().str().c_str(), "__afl", 5) != 0) { - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " - "instrumenting ... \n", - F->getName().str().c_str(), instFilename.str().c_str()); - return true; + 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()); } @@ -361,63 +326,67 @@ bool isInInstrumentList(llvm::Function *F) { } -#else - if (!Loc.isUnknown()) { + } - DILocation cDILoc(Loc.getAsMDNode(F->getContext())); +#endif - unsigned int instLine = cDILoc.getLineNumber(); - StringRef instFilename = cDILoc.getFilename(); +} - (void)instLine; - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { +static std::string getSourceName(llvm::Function *F) { - for (std::list<std::string>::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { + // let's try to get the filename for the function + auto bb = &F->getEntryBlock(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + DebugLoc Loc = IP->getDebugLoc(); - /* 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 LLVM_VERSION_MAJOR >= 4 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) + if (Loc) { - if (instFilename.str().length() >= it->length()) { + DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + StringRef instFilename = cDILoc->getFilename(); - return true; + if (instFilename.str().empty()) { - } + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { instFilename = oDILoc->getFilename(); } - } + } - } + return instFilename.str(); - } + } - } +#else + if (!Loc.isUnknown()) { - } + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + StringRef instFilename = cDILoc.getFilename(); + + /* Continue only if we know where we actually are */ + return instFilename.str(); + + } #endif - else { - // 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; + return std::string(""); - } +} - return false; +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()) { @@ -455,62 +424,75 @@ bool isInInstrumentList(llvm::Function *F) { if (!denyListFiles.empty()) { - // let's try to get the filename for the function - auto bb = &F->getEntryBlock(); - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - DebugLoc Loc = IP->getDebugLoc(); + std::string source_file = getSourceName(F); -#if LLVM_VERSION_MAJOR >= 4 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) - if (Loc) { + if (!source_file.empty()) { - DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); + for (std::list<std::string>::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (instFilename.str().empty()) { + if (source_file.length() >= it->length()) { - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); + return false; + + } } } - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { + } else { - for (std::list<std::string>::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { + // 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()); - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + } - if (instFilename.str().length() >= it->length()) { + } - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + } - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the denylist (%s), not " - "instrumenting ... \n", - F->getName().str().c_str(), instFilename.str().c_str()); - return false; + // if we do not have a instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { - } + return_default = false; - } + if (!allowListFunctions.empty()) { + + std::string instFunction = F->getName().str(); + + for (std::list<std::string>::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; } @@ -520,35 +502,31 @@ bool isInInstrumentList(llvm::Function *F) { } -#else - if (!Loc.isUnknown()) { - - DILocation cDILoc(Loc.getAsMDNode(F->getContext())); - - unsigned int instLine = cDILoc.getLineNumber(); - StringRef instFilename = cDILoc.getFilename(); + if (!allowListFiles.empty()) { - (void)instLine; - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { + std::string source_file = getSourceName(F); - for (std::list<std::string>::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { + if (!source_file.empty()) { - /* 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 */ + for (std::list<std::string>::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - if (instFilename.str().length() >= it->length()) { + /* 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 (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + if (source_file.length() >= it->length()) { - return false; + 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; } @@ -556,29 +534,24 @@ bool isInInstrumentList(llvm::Function *F) { } - } + } 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; -#endif - else { - - // 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 be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return true; + } } - return true; - } - return true; // not reached + 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 fd8e48a7..a4caf77b 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -150,7 +150,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"); @@ -217,79 +217,9 @@ bool AFLLTOPass::runOnModule(Module &M) { } - */ - - std::vector<std::string> module_block_list; - - if (map_addr) { - - 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, - "Warning: Found an ifunc with name %s that points to resolver " - "function %s, we cannot instrument this, putting it into a " - "block list.\n", - ifunc_name.str().c_str(), r_name.str().c_str()); - module_block_list.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 && - Priority <= 5) { - - if (!be_quiet) - fprintf(stderr, - "Warning: Found constructor function %s with prio " - "%u, we cannot instrument this, putting it into a " - "block list.\n", - F->getName().str().c_str(), Priority); - module_block_list.push_back(F->getName().str()); - - } - - } - - } - - } - - } - - } + */ - } - - } + scanForDangerousFunctions(&M); /* Instrument all the things! */ @@ -307,26 +237,6 @@ bool AFLLTOPass::runOnModule(Module &M) { if (F.size() < function_minimum_size) continue; if (isIgnoreFunction(&F)) continue; - if (module_block_list.size()) { - - for (auto bname : module_block_list) { - - std::string fname = F.getName().str(); - - if (fname.compare(bname) == 0) { - - if (!be_quiet) - WARNF( - "Skipping instrumentation of dangerous early running function " - "%s", - fname.c_str()); - - } - - } - - } - // the instrument file list check AttributeList Attrs = F.getAttributes(); if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) { @@ -686,7 +596,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-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 618abe48..a791d720 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 || @@ -236,7 +236,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 +292,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 20151aea..5479c3da 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -44,6 +44,8 @@ #include <sys/wait.h> #include <sys/types.h> +#include "llvm/Config/llvm-config.h" + #ifdef __linux__ #include "snapshot-inl.h" #endif @@ -52,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 @@ -62,6 +62,8 @@ #endif #endif +#define CTOR_PRIO 3 + #include <sys/mman.h> #include <fcntl.h> @@ -75,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; @@ -186,12 +184,21 @@ 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; + if (!__afl_area_ptr) { __afl_area_ptr = __afl_area_initial; } char *id_str = getenv(SHM_ENV_VAR); if (__afl_final_loc) { + if (__afl_area_ptr && __afl_final_loc && + __afl_final_loc > MAP_INITIAL_SIZE && + __afl_area_ptr != __afl_area_initial) { + + munmap(__afl_area_ptr, __afl_final_loc); + __afl_area_ptr = __afl_area_initial; + + } + if (__afl_final_loc % 8) __afl_final_loc = (((__afl_final_loc + 7) >> 3) << 3); @@ -851,7 +858,7 @@ void __afl_manual_init(void) { if (getenv("AFL_DEBUG")) fprintf(stderr, - "DEBUG: disabled instrumenation because of " + "DEBUG: disabled instrumentation because of " "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); } @@ -879,7 +886,7 @@ __attribute__((constructor())) void __afl_auto_init(void) { /* Initialization of the shmem - earliest possible because of LTO fixed mem. */ -__attribute__((constructor(0))) void __afl_auto_early(void) { +__attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; @@ -889,6 +896,24 @@ __attribute__((constructor(0))) void __afl_auto_early(void) { } +/* preset __afl_area_ptr */ + +__attribute__((constructor(0))) void __afl_auto_first(void) { + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + u8 *ptr; + + if (__afl_final_loc > MAP_INITIAL_SIZE) { + + ptr = (u8 *)mmap(NULL, __afl_final_loc, PROT_READ | PROT_WRITE, MAP_PRIVATE, + -1, 0); + + 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. @@ -899,7 +924,7 @@ __attribute__((constructor(0))) void __afl_auto_early(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 @@ -931,8 +956,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 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/src/afl-forkserver.c b/src/afl-forkserver.c index 56475320..173cc70f 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); @@ -638,13 +640,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->autodict_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."); @@ -657,11 +665,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."); @@ -680,7 +693,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) { @@ -711,8 +725,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, while (offset < (u32)status && (u8)dict[offset] + offset < (u32)status) { - fsrv->function_ptr(fsrv->function_opt, dict + offset + 1, - (u8)dict[offset]); + fsrv->autodict_func(fsrv->afl_ptr, dict + offset + 1, + (u8)dict[offset]); offset += (1 + dict[offset]); count++; @@ -871,7 +885,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 without AFL_LLVM_MAP_ADDR might solve " - "your problem\n\n" + "your \n" + " problem\n\n" " - Less likely, there is a horrible bug in the fuzzer. If other " "options\n" diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index d273818d..db57061d 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 @@ -407,7 +407,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; @@ -436,7 +436,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; @@ -594,7 +594,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-extras.c b/src/afl-fuzz-extras.c index 097871c8..17f02984 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; } @@ -354,14 +359,10 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { } /* 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 +376,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. */ @@ -452,10 +453,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 +461,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 +471,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((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt), - sizeof(struct extra_data), compare_extras_len); + sizeof(struct auto_extra_data), compare_auto_extras_len); } @@ -544,7 +540,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 +575,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-one.c b/src/afl-fuzz-one.c index 4db6febf..6d52b2b4 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -841,7 +841,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); } @@ -852,7 +852,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); } @@ -3108,7 +3108,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); } @@ -3119,7 +3119,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); } diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 9716be95..f21dd0b0 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); @@ -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; } @@ -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)); } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 94cfc383..5381723d 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; @@ -871,7 +872,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 aab785e1..a8416eb1 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -102,8 +102,9 @@ void afl_state_init_1(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.autodict_func = (void (*)(void *, u8 *, u32)) & maybe_add_auto; afl->cal_cycles = CAL_CYCLES; afl->cal_cycles_long = CAL_CYCLES_LONG; diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 0aa116e5..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) { @@ -588,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" @@ -624,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); @@ -639,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; @@ -820,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; } @@ -910,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]; @@ -924,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 = "."; @@ -978,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); @@ -1005,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)) { @@ -1019,7 +1081,10 @@ int main(int argc, char **argv_orig, char **envp) { 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); } @@ -1030,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) @@ -1043,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/test/test-basic.sh b/test/test-basic.sh index 9e4b03c3..06c40efe 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -99,8 +99,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc ;; esac } else { - $ECHO "$YELLOW[-] no bash available, cannot test afl-cmin.bash" - INCOMPLETE=1 + $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 @@ -119,8 +118,9 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc INCOMPLETE=1 } } || { - $ECHO "$YELLOW[-] not an intel platform, cannot test afl-gcc" + $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-post.sh b/test/test-post.sh index c1e22498..0911e2cd 100755 --- a/test/test-post.sh +++ b/test/test-post.sh @@ -4,7 +4,7 @@ AFL_TEST_DEPTH=$((AFL_TEST_DEPTH-1)) if [ $AFL_TEST_DEPTH = 0 ]; then # All runs done :) -$ECHO "$GREY[*] all test cases completed.$RESET" +$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" diff --git a/test/test-pre.sh b/test/test-pre.sh index 3e2d475d..e3393962 100755 --- a/test/test-pre.sh +++ b/test/test-pre.sh @@ -4,6 +4,7 @@ # 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 |