about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile40
-rw-r--r--README.md7
-rwxr-xr-xafl-wine-trace12
-rw-r--r--docs/ChangeLog3
-rw-r--r--include/afl-fuzz.h5
-rw-r--r--llvm_mode/Makefile2
-rw-r--r--llvm_mode/afl-llvm-pass.so.cc4
-rw-r--r--qemu_mode/README.md67
-rwxr-xr-xqemu_mode/build_qemu_support.sh2
-rwxr-xr-xqemu_mode/libcompcov/compcovtestbin8624 -> 0 bytes
-rw-r--r--qemu_mode/patches/afl-qemu-common.h1
-rw-r--r--qemu_mode/patches/afl-qemu-cpu-inl.h4
-rw-r--r--qemu_mode/patches/afl-qemu-cpu-translate-inl.h2
-rw-r--r--qemu_mode/unsigaction/Makefile34
-rw-r--r--qemu_mode/unsigaction/README.md7
-rw-r--r--qemu_mode/unsigaction/unsigaction.c3
-rw-r--r--src/afl-fuzz-globals.c4
-rw-r--r--src/afl-fuzz-one.c17
-rw-r--r--src/afl-fuzz-run.c12
-rw-r--r--test/test-compcov.c35
-rwxr-xr-xtest/test.sh136
21 files changed, 317 insertions, 80 deletions
diff --git a/Makefile b/Makefile
index 5d9a55a6..42c6d737 100644
--- a/Makefile
+++ b/Makefile
@@ -80,7 +80,7 @@ endif
 
 all:	test_x86 test_shm test_python27 ready $(PROGS) afl-as test_build all_done
 
-tests:
+tests:	source-only
 	@cd test ; ./test.sh
 
 help:
@@ -92,6 +92,8 @@ help:
 	@echo "distrib: everything (for both binary-only and source code fuzzing)"
 	@echo "install: installs everything you have compiled with the build option above"
 	@echo "clean: cleans everything. for qemu_mode and unicorn_mode it means it deletes all downloads as well"
+	@echo "tests: this runs the test framework. It is more catered for the developers, but if you run into problems this helps pinpointing the problem"
+	@echo "document: creates afl-fuzz-document which will only do one run and save all manipulated inputs into out/queue/mutations"
 	@echo "help: shows these build options :-)"
 	@echo "=========================================="
 	@echo "Recommended: \"distrib\" or \"source-only\", then \"install\""
@@ -151,31 +153,36 @@ afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
 	$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
 	ln -sf afl-as as
 
-afl-common.o : src/afl-common.c include/common.h
-	$(CC) $(CFLAGS) -c src/afl-common.c
+src/afl-common.o : src/afl-common.c include/common.h
+	$(CC) $(CFLAGS) -c src/afl-common.c -o src/afl-common.o
 
-afl-forkserver.o : src/afl-forkserver.c include/forkserver.h
-	$(CC) $(CFLAGS) -c src/afl-forkserver.c
+src/afl-forkserver.o : src/afl-forkserver.c include/forkserver.h
+	$(CC) $(CFLAGS) -c src/afl-forkserver.c -o src/afl-forkserver.o
 
-afl-sharedmem.o : src/afl-sharedmem.c include/sharedmem.h
-	$(CC) $(CFLAGS) -c src/afl-sharedmem.c
+src/afl-sharedmem.o : src/afl-sharedmem.c include/sharedmem.h
+	$(CC) $(CFLAGS) -c src/afl-sharedmem.c -o src/afl-sharedmem.o
 
-afl-fuzz: include/afl-fuzz.h $(AFL_FUZZ_FILES) afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86
-	$(CC) $(CFLAGS) $(AFL_FUZZ_FILES) afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS) $(PYFLAGS)
+afl-fuzz: include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
+	$(CC) $(CFLAGS) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS) $(PYFLAGS)
 
-afl-showmap: src/afl-showmap.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86
-	$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o -o $@ $(LDFLAGS)
+afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o $(COMM_HDR) | test_x86
+	$(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS)
 
-afl-tmin: src/afl-tmin.c afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86
-	$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS)
+afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
+	$(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS)
 
-afl-analyze: src/afl-analyze.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86
-	$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o -o $@ $(LDFLAGS)
+afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o $(COMM_HDR) | test_x86
+	$(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS)
 
 afl-gotcpu: src/afl-gotcpu.c $(COMM_HDR) | test_x86
 	$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
 
 
+# document all mutations and only do one run (use with only one input file!)
+document: include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
+	$(CC) $(CFLAGS) $(AFL_FUZZ_FILES) -D_AFL_DOCUMENT_MUTATIONS src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o afl-fuzz-document $(LDFLAGS) $(PYFLAGS)
+
+
 code-format:
 	./.custom-format.py -i src/*.c
 	./.custom-format.py -i include/*.h
@@ -221,11 +228,12 @@ all_done: test_build
 .NOTPARALLEL: clean
 
 clean:
-	rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.1.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast *.so unicorn_mode/24f55a7973278f20f0de21b904851d99d4716263.tar.gz *.8
+	rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.1.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast *.so unicorn_mode/24f55a7973278f20f0de21b904851d99d4716263.tar.gz *.8
 	rm -rf out_dir qemu_mode/qemu-3.1.1 unicorn_mode/unicorn
 	$(MAKE) -C llvm_mode clean
 	$(MAKE) -C libdislocator clean
 	$(MAKE) -C libtokencap clean
+	$(MAKE) -C qemu_mode/unsigaction clean
 	$(MAKE) -C qemu_mode/libcompcov clean
 
 distrib: all
diff --git a/README.md b/README.md
index b1a04611..8c8e74e9 100644
--- a/README.md
+++ b/README.md
@@ -45,17 +45,22 @@
   * NeverZero patch for afl-gcc, llvm_mode, qemu_mode and unicorn_mode which prevents a wrapping map value to zero, increases coverage
   
   * Persistent mode and deferred forkserver for qemu_mode
+  
+  * Win32 PE binary-only fuzzing with QEMU and Wine
 
   A more thorough list is available in the PATCHES file.
 
   | Feature/Instrumentation | LLVM | GCC | QEMU | Unicorn |
   | ----------------------- |:----:|:---:|:----:| -------:|
   | laf-intel / CompCov     |  x   |     |  x   |    x    |
-  | NeverZero               |  x   |  x  |  x   |    x    |
+  | NeverZero               |  x(1)|  x  |  x   |    x    |
   | Persistent mode         |  x   |     |  x   |         |
   | Whitelist               |  x   |     |      |         |
   | InsTrim                 |  x   |     |      |         |
 
+  (1) only in LLVM >= 9.0 due to a bug in llvm in previous versions
+
+
   So all in all this is the best-of AFL that is currently out there :-)
 
   For new versions and additional information, check out:
diff --git a/afl-wine-trace b/afl-wine-trace
index fa552051..f8284d7e 100755
--- a/afl-wine-trace
+++ b/afl-wine-trace
@@ -18,13 +18,19 @@ os.environ["WINELOADERNOEXEC"] = "1"
 
 pe = pefile.PE(sys.argv[1])
 
-os.environ["AFL_ENTRYPOINT"] = "0x%x" % (pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.AddressOfEntryPoint)
+if "AFL_ENTRYPOINT" not in os.environ:
+    os.environ["AFL_ENTRYPOINT"] = "0x%x" % (pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.AddressOfEntryPoint)
 if not os.getenv("AFL_INST_LIBS"):
     if "AFL_CODE_START" not in os.environ:
         os.environ["AFL_CODE_START"] = "0x%x" % (pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.BaseOfCode)
     if "AFL_CODE_END" not in os.environ:
         os.environ["AFL_CODE_END"] = "0x%x" % (pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.BaseOfCode + pe.OPTIONAL_HEADER.SizeOfCode)
 
+if pe.FILE_HEADER.Machine == pefile.MACHINE_TYPE["IMAGE_FILE_MACHINE_AMD64"] or pe.FILE_HEADER.Machine == pefile.MACHINE_TYPE["IMAGE_FILE_MACHINE_IA64"]:
+    os.environ["LD_PRELOAD"] = os.path.join(my_dir, "qemu_mode/unsigaction/unsigaction64.so")
+else:
+    os.environ["LD_PRELOAD"] = os.path.join(my_dir, "qemu_mode/unsigaction/unsigaction32.so")
+
 if os.getenv("WINECOV_QEMU_PATH"):
     qemu_path = os.getenv("WINECOV_QEMU_PATH")
 elif os.path.exists(os.path.join(my_dir, "afl-qemu-trace")):
@@ -36,7 +42,7 @@ else:
     elif pe.FILE_HEADER.Machine == pefile.MACHINE_TYPE["IMAGE_FILE_MACHINE_I386"]:
         qemu_path += "i386"
     else:
-        print ("[afl-wine-trace] unsupported architecture\n")
+        print ("[wine-cov] unsuppoted architecture\n")
         exit(1)
     qemu_path = shutil.which(qemu_path)
 
@@ -49,7 +55,7 @@ else:
     elif pe.FILE_HEADER.Machine == pefile.MACHINE_TYPE["IMAGE_FILE_MACHINE_I386"]:
         pass
     else:
-        print ("[wine-cov] unsuppoted architecture\n")
+        print ("[afl-wine-trace] unsopported architecture\n")
         exit(1)
 
 os.execve(qemu_path, [qemu_path, wine_path] + sys.argv[1:], os.environ)
diff --git a/docs/ChangeLog b/docs/ChangeLog
index 7beb32c3..0b945d36 100644
--- a/docs/ChangeLog
+++ b/docs/ChangeLog
@@ -20,6 +20,7 @@ Version ++2.54d (dev):
   - persistent mode for QEMU (see qemu_mode/README.md)
   - custom mutator library is now a standard mutator, to exclusivly use it
     add AFL_CUSTOM_MUTATOR_ONLY (that will trigger the previous behaviour)
+  - new library qemu_mode/unsigaction which filters sigaction events
   - no more unlinking the input file, this way the input file can also be a
     FIFO or disk partition
   - setting LLVM_CONFIG for llvm_mode will now again switch to the selected
@@ -27,6 +28,8 @@ Version ++2.54d (dev):
   - fuzzing strategy yields for custom mutator were missing from the UI, added them :)
   - added "make tests" which will perform checks to see that all functionality
     is working as expected. this is currently the starting point, its not complete :)
+  - added mutation documentation feature ("make document"), creates afl-fuzz-document
+    and saves all mutations of the first run on the first file into out/queue/mutations
   - libtokencap and libdislocator now compile to the afl_root directory and are
     installed to the .../lib/afl directory when present during make install
   - reducing duplicate code in afl-fuzz
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index 9536e06a..4912b3f0 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -682,5 +682,10 @@ static u64 get_cur_time_us(void) {
 
 }
 
+#ifdef _AFL_DOCUMENT_MUTATIONS
+  extern u8 do_document;
+  extern u32 document_counter;
+#endif
+
 #endif
 
diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile
index 2d35505e..96aba898 100644
--- a/llvm_mode/Makefile
+++ b/llvm_mode/Makefile
@@ -83,7 +83,7 @@ CXX        = $(LLVM_BINDIR)/clang++
 
 ifeq "$(shell test -e $(CC) || echo 1 )" "1"
   # llvm-config --bindir is not providing a valid path, so ...
-  ifeq "$(shell test -e "$(BIN_DIR)/clang" && echo 1)" "1"
+  ifeq "$(shell test -e '$(BIN_DIR)/clang' && echo 1)" "1"
     # we found one in the local install directory, lets use these
     CC         = $(BIN_DIR)/clang
     CXX        = $(BIN_DIR)/clang++
diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc
index 5d531a87..58acd9be 100644
--- a/llvm_mode/afl-llvm-pass.so.cc
+++ b/llvm_mode/afl-llvm-pass.so.cc
@@ -105,9 +105,7 @@ bool AFLCoverage::runOnModule(Module &M) {
 
     SAYF(cCYA "afl-llvm-pass" VERSION cRST " by <lszekeres@google.com>\n");
 
-  } else
-
-    be_quiet = 1;
+  } else if (getenv("AFL_QUIET")) be_quiet = 1;
 
   /* Decide instrumentation ratio */
 
diff --git a/qemu_mode/README.md b/qemu_mode/README.md
index 54fbf874..b994e4c6 100644
--- a/qemu_mode/README.md
+++ b/qemu_mode/README.md
@@ -19,7 +19,7 @@ Andrea Fioraldi. Special thanks to abiondo that re-enabled TCG chaining.
 
 ## 2) How to use
 
-The feature is implemented with a patch to QEMU 3.1.0. The simplest way
+The feature is implemented with a patch to QEMU 3.1.1. The simplest way
 to build it is to run ./build_qemu_support.sh. The script will download,
 configure, and compile the QEMU binary for you.
 
@@ -44,25 +44,19 @@ Note: if you want the QEMU helper to be installed on your system for all
 users, you need to build it before issuing 'make install' in the parent
 directory.
 
-## 3) Options
+## 3) Bonus feature #1: deferred initialization
 
-There is ./libcompcov/ which implements laf-intel (splitting memcmp,
-strncmp, etc. to make these conditions easier solvable by afl-fuzz).
-Highly recommended.
+As for LLVM mode (referes to its README for mode details) QEMU mode support
+the deferred initialization.
 
-The option that enables QEMU CompareCoverage is AFL_COMPCOV_LEVEL.
-AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate
-values / read-only memory. AFL_COMPCOV_LEVEL=2 instruments all
-comparison instructions and memory comparison functions when libcompcov
-is preloaded. Comparison instructions are currently instrumented only
-on the x86 and x86_64 targets.
-
-Another option is the environment variable AFL_ENTRYPOINT which allows
-move the forkserver to a different part, e.g. just before the file is
+This can be enabled setting the environment variable AFL_ENTRYPOINT which allows
+to move the forkserver to a different part, e.g. just before the file is
 opened (e.g. way after command line parsing and config file loading, etc)
 which can be a huge speed improvement. Note that the specified address
 must be an address of a basic block.
 
+## 4) Bonus feature #2: persistent mode
+
 QEMU mode support also persistent mode for x86 and x86_64 targets.
 The environment variable to enable it is AFL_QEMU_PERSISTENT_ADDR=`start addr`.
 In this variable you must specify the address of the function that
@@ -78,11 +72,42 @@ Note that the format of the addresses in such variables is hex.
 
 Note that the base address of PIE binaries in QEMU user is 0x4000000000.
 
-Warning: in x86_64 parameters are passed via registers and so the target
-function of persistent mode cannot make use of arguments. An option to restore
-the state of each GPR each iteration of the loop is planned.
+With the env variable AFL_QEMU_PERSISTENT_GPR you can tell QEMU to save the
+original value of general purpose registers and restore them ech cycle.
+This allow to use as persistent loop functions that make use of arguments on 
+x86_64.
+
+With AFL_QEMU_PERSISTENT_RETADDR_OFFSET you can specify the offset from the
+stack pointer in which QEME can find the return address when `start addr` is
+hitted.
+
+Use this mode with caution, problably will not work at first shot.
+
+## 5) Bonus feature #3: CompareCoverage
+
+CompareCoverage is a sub-instrumentation with effects similar to laf-intel.
+
+The option that enables QEMU CompareCoverage is AFL_COMPCOV_LEVEL.
+There is also ./libcompcov/ which implements CompareCoverage for *cmp functions
+(splitting memcmp, strncmp, etc. to make these conditions easier solvable by
+afl-fuzz).
+AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate
+values / read-only memory. AFL_COMPCOV_LEVEL=2 instruments all
+comparison instructions and memory comparison functions when libcompcov
+is preloaded. Comparison instructions are currently instrumented only
+on the x86 and x86_64 targets.
+
+Highly recommended.
+
+## 6) Bonus feature #3: Wine mode
+
+AFL++ QEMU can use Wine to fuzz WIn32 PE binaries. Use the -W flag of afl-fuzz.
+
+Note that some binaries require user interaction with GUI and must be patched.
+
+For examples look [here](https://github.com/andreafioraldi/WineAFLplusplusDEMO).
 
-## 4) Notes on linking
+## 7) Notes on linking
 
 The feature is supported only on Linux. Supporting BSD may amount to porting
 the changes made to linux-user/elfload.c and applying them to
@@ -103,7 +128,7 @@ practice, this means two things:
 Setting AFL_INST_LIBS=1 can be used to circumvent the .text detection logic
 and instrument every basic block encountered.
 
-## 5) Benchmarking
+## 8) Benchmarking
 
 If you want to compare the performance of the QEMU instrumentation with that of
 afl-gcc compiled code against the same target, you need to build the
@@ -118,7 +143,7 @@ Comparative measurements of execution speed or instrumentation coverage will be
 fairly meaningless if the optimization levels or instrumentation scopes don't
 match.
 
-## 6) Gotchas, feedback, bugs
+## 9) Gotchas, feedback, bugs
 
 If you need to fix up checksums or do other cleanup on mutated test cases, see
 experimental/post_library/ for a viable solution.
@@ -139,7 +164,7 @@ with -march=core2, can help.
 Beyond that, this is an early-stage mechanism, so fields reports are welcome.
 You can send them to <afl-users@googlegroups.com>.
 
-## 7) Alternatives: static rewriting
+## 10) Alternatives: static rewriting
 
 Statically rewriting binaries just once, instead of attempting to translate
 them at run time, can be a faster alternative. That said, static rewriting is
diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh
index 0ca3f494..02379eb2 100755
--- a/qemu_mode/build_qemu_support.sh
+++ b/qemu_mode/build_qemu_support.sh
@@ -212,6 +212,8 @@ fi
 
 echo "[+] Building libcompcov ..."
 make -C libcompcov
+echo "[+] Building unsigaction ..."
+make -C unsigaction
 echo "[+] libcompcov ready"
 echo "[+] All done for qemu_mode, enjoy!"
 
diff --git a/qemu_mode/libcompcov/compcovtest b/qemu_mode/libcompcov/compcovtest
deleted file mode 100755
index 0bb68d60..00000000
--- a/qemu_mode/libcompcov/compcovtest
+++ /dev/null
Binary files differdiff --git a/qemu_mode/patches/afl-qemu-common.h b/qemu_mode/patches/afl-qemu-common.h
index 2b7bd51c..e435c62f 100644
--- a/qemu_mode/patches/afl-qemu-common.h
+++ b/qemu_mode/patches/afl-qemu-common.h
@@ -61,6 +61,7 @@ extern target_long    persistent_stack_offset;
 extern unsigned char  persistent_first_pass;
 extern unsigned char  persistent_save_gpr;
 extern target_ulong   persistent_saved_gpr[CPU_NB_REGS];
+extern int            persisent_retaddr_offset;
 
 extern __thread abi_ulong afl_prev_loc;
 
diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h
index ee7d12e0..cfea04d6 100644
--- a/qemu_mode/patches/afl-qemu-cpu-inl.h
+++ b/qemu_mode/patches/afl-qemu-cpu-inl.h
@@ -90,6 +90,7 @@ target_long   persistent_stack_offset;
 unsigned char persistent_first_pass;
 unsigned char persistent_save_gpr;
 target_ulong  persistent_saved_gpr[CPU_NB_REGS];
+int           persisent_retaddr_offset;
 
 /* Instrumentation ratio: */
 
@@ -218,6 +219,9 @@ static void afl_setup(void) {
   
   if (getenv("AFL_QEMU_PERSISTENT_GPR"))
     persistent_save_gpr = 1;
+  
+  if (getenv("AFL_QEMU_PERSISTENT_RETADDR_OFFSET"))
+    persisent_retaddr_offset = strtoll(getenv("AFL_QEMU_PERSISTENT_RETADDR_OFFSET"), NULL, 16);
 
   if (getenv("AFL_QEMU_PERSISTENT_CNT"))
     afl_persistent_cnt = strtoll(getenv("AFL_QEMU_PERSISTENT_CNT"), NULL, 16);
diff --git a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
index 48b11c64..126cf255 100644
--- a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
+++ b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
@@ -195,7 +195,7 @@ static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
       if (afl_persistent_ret_addr == 0) {                                     \
                                                                               \
         TCGv_ptr paddr = tcg_const_ptr(afl_persistent_addr);                  \
-        tcg_gen_st_tl(paddr, cpu_regs[R_ESP], 0);                             \
+        tcg_gen_st_tl(paddr, cpu_regs[R_ESP], persisent_retaddr_offset);      \
                                                                               \
       }                                                                       \
       tcg_gen_afl_call0(&afl_persistent_loop);                                \
diff --git a/qemu_mode/unsigaction/Makefile b/qemu_mode/unsigaction/Makefile
new file mode 100644
index 00000000..9aa96330
--- /dev/null
+++ b/qemu_mode/unsigaction/Makefile
@@ -0,0 +1,34 @@
+#
+# american fuzzy lop - unsigaction
+# --------------------------------
+#
+# Written by Andrea Fioraldi <andreafioraldi@gmail.com>
+#
+# Copyright 2019 Andrea Fioraldi. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+
+ifndef AFL_NO_X86
+
+all: lib_i386 lib_amd64
+
+lib_i386:
+	$(CC) -m32 -fPIC -shared unsigaction.c -o unsigaction32.so
+
+lib_amd64:
+	$(CC) -fPIC -shared unsigaction.c -o unsigaction64.so
+
+clean:
+	rm -f unsigaction32.so unsigaction64.so
+
+else
+
+all:
+	@echo "[!] Note: skipping compilation of unsigaction (AFL_NO_X86 set)."
+
+endif
diff --git a/qemu_mode/unsigaction/README.md b/qemu_mode/unsigaction/README.md
new file mode 100644
index 00000000..0f3707fd
--- /dev/null
+++ b/qemu_mode/unsigaction/README.md
@@ -0,0 +1,7 @@
+# unsigation
+
+This library disable sigaction when preloaded.
+
+Mainly needed by Wine mode but can be used as a separate tool.
+
+A similar solution can be found in is [preeny](https://github.com/zardus/preeny).
diff --git a/qemu_mode/unsigaction/unsigaction.c b/qemu_mode/unsigaction/unsigaction.c
new file mode 100644
index 00000000..004084f9
--- /dev/null
+++ b/qemu_mode/unsigaction/unsigaction.c
@@ -0,0 +1,3 @@
+int sigaction(int signum, void *act, void *oldact) {
+  return 0;
+}
diff --git a/src/afl-fuzz-globals.c b/src/afl-fuzz-globals.c
index 1358a1fb..01b242b8 100644
--- a/src/afl-fuzz-globals.c
+++ b/src/afl-fuzz-globals.c
@@ -259,3 +259,7 @@ PyObject *py_functions[PY_FUNC_COUNT];
 
 #endif
 
+#ifdef _AFL_DOCUMENT_MUTATIONS
+  u8 do_document;
+  u32 document_counter;
+#endif
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index 59483b8f..1824f0b2 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -4231,6 +4231,7 @@ pacemaker_fuzzing:
 
 #define core_fuzzing(a) common_fuzzing((a), MOpt_globals_core)
 
+
 void pso_updating(void) {
 
   g_now += 1;
@@ -4310,6 +4311,22 @@ void pso_updating(void) {
 u8 fuzz_one(char** argv) {
 
   int key_val_lv = 0;
+
+#ifdef _AFL_DOCUMENT_MUTATIONS
+  if (do_document == 0) {
+    char *fn = alloc_printf("%s/mutations", out_dir);
+    if (fn) {
+      do_document = mkdir(fn, 0700); // if it exists we do not care
+      do_document = 1;
+      ck_free(fn);
+    } else
+      PFATAL("malloc()");
+  } else {
+    do_document = 2;
+    stop_soon = 2;
+  }
+#endif
+
   if (limit_time_sig == 0) {
 
     key_val_lv = fuzz_one_original(argv);
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index f2f663dc..220433fc 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -251,6 +251,18 @@ void write_to_testcase(void* mem, u32 len) {
 
   s32 fd = out_fd;
 
+#ifdef _AFL_DOCUMENT_MUTATIONS
+  s32 doc_fd;
+  char *fn = alloc_printf("%s/mutations/%09u:%s", out_dir, document_counter++, describe_op(0));
+  if (fn != NULL) {
+    if ((doc_fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0) {
+      if (write(doc_fd, mem, len) != len) PFATAL("write to mutation file failed: %s", fn);
+      close(doc_fd);
+    }
+    ck_free(fn);
+  }
+#endif
+
   if (out_file) {
 
     // unlink(out_file);                                     /* Ignore errors.
diff --git a/test/test-compcov.c b/test/test-compcov.c
index bbad3aed..978d5551 100644
--- a/test/test-compcov.c
+++ b/test/test-compcov.c
@@ -4,25 +4,26 @@
 #include <string.h>
 
 int main(int argc, char** argv) {
+  char *input = argv[1], *buf, buffer[20];
 
-  char *buf;
-
-  if (argc > 1) {
-  
-    if (strcmp(argv[1], "LIBTOKENCAP") == 0)
-      printf("your string was libtokencap\n");
-    else if (strcmp(argv[1], "BUGMENOT") == 0)
-      printf("your string was bugmenot\n");
-    else if (strcmp(argv[1], "BUFFEROVERFLOW") == 0) {
-      buf = malloc(16);
-      strcpy(buf, "TEST");
-      strcat(buf, argv[1]);
-      printf("This will only crash with libdislocator: %s\n", buf);
-      return 0;
-    } else
-      printf("I do not know your string\n");
-  
+  if (argc < 2) {
+    ssize_t ret = read(0, buffer, sizeof(buffer) - 1);
+    buffer[ret] = 0;
+    input = buffer;
   }
+  
+  if (strcmp(input, "LIBTOKENCAP") == 0)
+    printf("your string was libtokencap\n");
+  else if (strcmp(input, "BUGMENOT") == 0)
+    printf("your string was bugmenot\n");
+  else if (strcmp(input, "BUFFEROVERFLOW") == 0) {
+    buf = malloc(16);
+    strcpy(buf, "TEST");
+    strcat(buf, input);
+    printf("This will only crash with libdislocator: %s\n", buf);
+    return 0;
+  } else
+    printf("I do not know your string\n");
 
   return 0;
 
diff --git a/test/test.sh b/test/test.sh
index 0e2ba52b..d230cf71 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -27,15 +27,29 @@ test -z "$ECHO" && { echo Error: echo command does not support -e option ; exit
 
 export AFL_EXIT_WHEN_DONE=1
 export AFL_SKIP_CPUFREQ=1
+unset AFL_QUIET
+unset AFL_DEBUG
+unset AFL_HARDEN
+unset AFL_USE_ASAN
+unset AFL_USE_MSAN
+unset AFL_CC
+unset AFL_PRELOAD
+unset AFL_LLVM_WHITELIST
+unset AFL_LLVM_INSTRIM
+unset AFL_LLVM_LAF_SPLIT_SWITCHES
+unset AFL_LLVM_LAF_TRANSFORM_COMPARES
+unset AFL_LLVM_LAF_SPLIT_COMPARES
 
 GREY="\\x1b[1;90m"
+BLUE="\\x1b[1;94m"
 GREEN="\\x1b[0;32m"
 RED="\\x1b[0;31m"
 YELLOW="\\x1b[1;93m"
 RESET="\\x1b[0m"
 
-$ECHO "$RESET"
+$ECHO "${RESET}${GREY}[*] starting afl++ test framework ..."
 
+$ECHO "$BLUE[*] Testing: afl-gcc, afl-showmap and afl-fuzz"
 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-instr.harden ../test-instr.c > /dev/null 2>&1
@@ -53,33 +67,95 @@ test -e ../afl-gcc -a -e ../afl-showmap -a -e ../afl-fuzz && {
   test -e test-instr.harden && {
     grep -qa fstack-protector-all test-instr.harden > /dev/null 2>&1 && {
       $ECHO "$GREEN[+] afl-gcc hardened mode succeeded and is working"
-    } || $ECHO "$RED[!] hardened mode is not hardened"
+    } || $ECHO "$RED[!] afl-gcc hardened mode is not hardened"
     rm -f test-instr.harden
   } || $ECHO "$RED[!] afl-gcc hardened mode compilation failed"
   # now we want to be sure that afl-fuzz is working  
   test -n "$TIMEOUT" && {
     mkdir -p in
     echo 0 > in/in
-    $ECHO "$GREY[*] running afl-fuzz, this will take approx 10 seconds"
+    $ECHO "$GREY[*] running afl-fuzz for afl-gcc, this will take approx 10 seconds"
     {
       timeout -s KILL 10 ../afl-fuzz -i in -o out -- ./test-instr.plain > /dev/null 2>&1
     } > /dev/null 2>&1
     test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
-      $ECHO "$GREEN[+] afl-fuzz is working correctly"
-    } || $ECHO "$RED[!] afl-fuzz is not working correctly"
+      $ECHO "$GREEN[+] afl-fuzz is working correctly with afl-gcc"
+    } || $ECHO "$RED[!] afl-fuzz is not working correctly with afl-gcc"
     rm -rf in out
   } || $ECHO "$YELLOW[-] we cannot test afl-fuzz because we are missing the timeout command"
   rm -f test-instr.plain
 } || $ECHO "$YELLOW[-] afl is not compiled, cannot test"
 
+$ECHO "$BLUE[*] Testing: llvm_mode"
 test -e ../afl-clang-fast && {
+  ../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 -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1
+    ../afl-showmap -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 instrumentation should be different on different input but is not"
+      } || $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly"
+    } || $ECHO "$RED[!] llvm_mode instrumentation failed"
+    rm -f test-instr.plain.0 test-instr.plain.1
+  } || $ECHO "$RED[!] llvm_mode failed"
+  test -e test-compcov.harden && {
+    grep -Eqa '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"
+    rm -f test-compcov.harden
+  } || $ECHO "$RED[!] llvm_mode hardened mode compilation failed"
+  # now we want to be sure that afl-fuzz is working  
+  test -n "$TIMEOUT" && {
+    mkdir -p in
+    echo 0 > in/in
+    $ECHO "$GREY[*] running afl-fuzz for llvm_mode, this will take approx 10 seconds"
+    {
+      timeout -s KILL 10 ../afl-fuzz -i in -o out -- ./test-instr.plain > /dev/null 2>&1
+    } > /dev/null 2>&1
+    test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
+      $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode"
+    } || $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode"
+    rm -rf in out
+  } || $ECHO "$YELLOW[-] we cannot test afl-fuzz because we are missing the timeout command"
+  rm -f test-instr.plain
 
-  echo todo: llvm_mode
-
+  # now for the special llvm_mode things
+  AFL_LLVM_INSTRIM=1 AFL_LLVM_INSTRIM_LOOPHEAD=1 ../afl-clang-fast -o test-compcov.instrim test-compcov.c > /dev/null 2> test.out
+  test -e test-compcov.instrim && {
+    grep -Eq " [1-3] location" test.out && {
+      $ECHO "$GREEN[+] llvm_mode InsTrim feature works correctly"
+    } || $ECHO "$RED[!] llvm_mode InsTrim feature failed"
+  } || $ECHO "$RED[!] llvm_mode InsTrim feature compilation failed"
+  rm -f test-compcov.instrim test.out
+  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 > /dev/null 2> test.out
+  test -e test-compcov.compcov && {
+    grep -Eq " [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"
+  } || $ECHO "$RED[!] llvm_mode laf-intel/compcov feature compilation failed"
+  rm -f test-compcov.compcov test.out
+  echo foobar.c > whitelist.txt
+  AFL_LLVM_WHITELIST=whitelist.txt ../afl-clang-fast -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 whitelist feature works correctly"
+    } || $ECHO "$RED[!] llvm_mode whitelist feature failed"
+  } || $ECHO "$RED[!] llvm_mode whitelist feature compilation failed"
+  rm -f test-compcov test.out whitelist.txt
+  ../afl-clang-fast -o test-persistent ../experimental/persistent_demo/persistent_demo.c > /dev/null 2>&1
+  test -e test-persistent && {
+    echo foo | ../afl-showmap -o /dev/null -q -r ./test-persistent && {
+      $ECHO "$GREEN[+] lvm_mode persistent mode feature works correctly"
+    } || $ECHO "$RED[!] llvm_mode persistent mode feature failed to work"
+  } || $ECHO "$RED[!] llvm_mode persistent mode feature compilation failed"
+  rm -f test-persistent
 } || $ECHO "$YELLOW[-] llvm_mode not compiled, cannot test"
 
+$ECHO "$BLUE[*] Testing: shared library extensions"
 gcc -o test-compcov test-compcov.c > /dev/null 2>&1
-
 test -e ../libtokencap.so && {
   AFL_TOKEN_FILE=token.out LD_PRELOAD=../libtokencap.so ./test-compcov foobar > /dev/null 2>&1
   grep -q BUGMENOT token.out > /dev/null 2>&1 && {
@@ -87,7 +163,6 @@ test -e ../libtokencap.so && {
   } || $ECHO "$RED[!] libtokencap did not capture tokens"
   rm -f token.out
 } || $ECHO "$YELLOW[-] libtokencap is not compiled, cannot test"
-
 test -e ../libdislocator.so && {
   {
     ulimit -c 1
@@ -98,17 +173,44 @@ test -e ../libdislocator.so && {
   } || $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"
+rm -f test-compcov
 
+$ECHO "$BLUE[*] Testing: qemu_mode"
 test -e ../afl-qemu-trace && {
-
-  echo todo: qemu_mode
-  # we will use test-compcov for testing libcompcov
-
+  gcc -o test-instr ../test-instr.c
+  gcc -o test-compcov test-compcov.c
+  test -e test-instr -a -e test-compcov && {
+    test -n "$TIMEOUT" && {
+      mkdir -p in
+      echo 0 > in/in
+      $ECHO "$GREY[*] running afl-fuzz for qemu_mode, this will take approx 10 seconds"
+      {
+        timeout -s KILL 10 ../afl-fuzz -Q -i in -o out -- ./test-instr > /dev/null 2>&1
+      } > /dev/null 2>&1
+      test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
+        $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode"
+      } || $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode"
+
+      test -e ../libcompcov.so && {
+        $ECHO "$GREY[*] running afl-fuzz for qemu_mode libcompcov, this will take approx 10 seconds"
+        {
+          export AFL_PRELOAD=../libcompcov.so 
+          export AFL_COMPCOV_LEVEL=2
+          timeout -s KILL 10 ../afl-fuzz -Q -i in -o out -- ./test-compcov > /dev/null 2>&1
+        } > /dev/null 2>&1
+        test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
+          $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode libcompcov"
+        } || $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode libcompcov"
+      } || $ECHO "$YELLOW[-] we cannot test qemu_mode libcompcov because it is not present"
+      rm -rf in out
+    } || $ECHO "$YELLOW[-] we cannot test afl-fuzz because we are missing the timeout command"
+  } || $ECHO "$RED[-] gcc compilation of test targets failed - what is going on??"
+  
+  $ECHO "$YELLOW[?] we need a test case for qemu_mode persistent mode"
+
+  rm -f test-instr test-compcov
 } || $ECHO "$YELLOW[-] qemu_mode is not compiled, cannot test"
 
-rm -f test-compcov
-
-$ECHO "$GREY[*] all tests completed!"
-$ECHO "$RESET"
+$ECHO "$GREY[*] all test cases completed.$RESET"
 
 # unicorn_mode ?