about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.md55
-rw-r--r--TODO.md2
-rwxr-xr-xafl-cmin42
-rw-r--r--docs/Changelog.md7
-rw-r--r--docs/parallel_fuzzing.md8
-rw-r--r--frida_mode/GNUmakefile4
-rw-r--r--frida_mode/src/cmplog/cmplog.c149
-rw-r--r--frida_mode/src/cmplog/cmplog_x64.c22
-rw-r--r--frida_mode/src/ctx/ctx_x64.c22
-rw-r--r--frida_mode/src/ctx/ctx_x86.c6
-rw-r--r--frida_mode/src/main.c6
-rw-r--r--frida_mode/test/jpeg/GNUmakefile172
-rw-r--r--frida_mode/test/jpeg/Makefile19
-rw-r--r--frida_mode/test/jpeg/aflpp_qemu_driver_hook.c97
-rwxr-xr-xfrida_mode/test/jpeg/get_symbol_addr.py36
-rw-r--r--frida_mode/test/proj4/GNUmakefile172
-rw-r--r--frida_mode/test/proj4/Makefile19
-rw-r--r--frida_mode/test/proj4/aflpp_qemu_driver_hook.c97
-rwxr-xr-xfrida_mode/test/proj4/get_symbol_addr.py36
-rw-r--r--include/forkserver.h1
-rw-r--r--instrumentation/afl-compiler-rt.o.c25
-rw-r--r--src/afl-forkserver.c13
-rw-r--r--src/afl-fuzz-cmplog.c2
-rw-r--r--src/afl-fuzz-init.c115
-rw-r--r--src/afl-fuzz-stats.c17
-rw-r--r--src/afl-fuzz.c9
-rw-r--r--src/afl-showmap.c206
27 files changed, 1058 insertions, 301 deletions
diff --git a/README.md b/README.md
index 91f28118..c0a22e54 100644
--- a/README.md
+++ b/README.md
@@ -25,12 +25,17 @@
   For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast`
   with `AFL_LLVM_CMPLOG=1`.
 
-## Major changes in afl++ 3.00 onwards:
+## Major behaviour changes in afl++ 3.00 onwards:
 
 With afl++ 3.13-3.20 we introduce frida_mode (-O) to have an alternative for
 binary-only fuzzing. It is slower than Qemu mode but works on MacOS, Android,
 iOS etc.
 
+With afl++ 3.14 we introduced the following changes from previous behaviours:
+  * afl-fuzz: deterministic fuzzing it not a default for -M main anymore
+  * afl-cmin/afl-showmap -i now descends into subdirectories (afl-cmin.bash
+    however does not)
+
 With afl++ 3.10 we introduced the following changes from previous behaviours:
   * The '+' feature of the '-t' option now means to  auto-calculate the timeout
     with the value given being the maximum timeout. The original meaning of
@@ -38,7 +43,6 @@ With afl++ 3.10 we introduced the following changes from previous behaviours:
 
 With afl++ 3.00 we introduced changes that break some previous afl and afl++
 behaviours and defaults:
-
   * There are no llvm_mode and gcc_plugin subdirectories anymore and there is
     only one compiler: afl-cc. All previous compilers now symlink to this one.
     All instrumentation source code is now in the `instrumentation/` folder.
@@ -572,8 +576,15 @@ to use afl-clang-lto as the compiler. You also have the option to generate
 a dictionary yourself, see [utils/libtokencap/README.md](utils/libtokencap/README.md).
 
 afl-fuzz has a variety of options that help to workaround target quirks like
-specific locations for the input file (`-f`), not performing deterministic
-fuzzing (`-d`) and many more. Check out `afl-fuzz -h`.
+specific locations for the input file (`-f`), performing deterministic
+fuzzing (`-D`) and many more. Check out `afl-fuzz -h`.
+
+We highly recommend that you set a memory limit for running the target with `-m`
+which defines the maximum memory in MB. This prevents a potential
+out-of-memory problem for your system plus helps you detect missing `malloc()`
+failure handling in the target.
+Play around with various -m values until you find one that safely works for all
+your input seeds (if you have good ones and then double or quadrouple that.
 
 By default afl-fuzz never stops fuzzing. To terminate afl++ simply press Control-C
 or send a signal SIGINT. You can limit the number of executions or approximate runtime
@@ -614,23 +625,28 @@ For every secondary fuzzer there should be a variation, e.g.:
  * one to three fuzzers should fuzz a target compiled with laf-intel/COMPCOV
    (see above). Important note: If you run more than one laf-intel/COMPCOV
    fuzzer and you want them to share their intermediate results, the main
-   fuzzer (`-M`) must be one of the them!
+   fuzzer (`-M`) must be one of the them! (Although this is not really
+   recommended.)
 
 All other secondaries should be used like this:
- * A third to a half with the MOpt mutator enabled: `-L 0`
- * run with a different power schedule, available are:
-   `fast (default), explore, coe, lin, quad, exploit, mmopt, rare, seek`
-   which you can set with e.g. `-p seek`
+ * A quarter to a third with the MOpt mutator enabled: `-L 0`
+ * run with a different power schedule, recommended are:
+   `fast (default), explore, coe, lin, quad, exploit and rare`
+   which you can set with e.g. `-p explore`
+ * a few instances should use the old queue cycling with `-Z`
 
 Also it is recommended to set `export AFL_IMPORT_FIRST=1` to load testcases
 from other fuzzers in the campaign first.
 
+If you have a large corpus, a corpus from a previous run or are fuzzing in
+a CI, then also set `export AFL_CMPLOG_ONLY_NEW=1` and `export AFL_FAST_CAL=1`.
+
 You can also use different fuzzers.
 If you are using afl spinoffs or afl conforming fuzzers, then just use the
 same -o directory and give it a unique `-S` name.
 Examples are:
  * [Eclipser](https://github.com/SoftSec-KAIST/Eclipser/)
- * [Untracer](https://github.com/FoRTE-Research/UnTracer-AFL)
+ * [symcc](https://github.com/eurecom-s/symcc/)
  * [AFLsmart](https://github.com/aflsmart/aflsmart)
  * [FairFuzz](https://github.com/carolemieux/afl-rb)
  * [Neuzz](https://github.com/Dongdongshe/neuzz)
@@ -638,9 +654,11 @@ 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 with -entropic, etc.
+However you can also sync afl++ with honggfuzz, libfuzzer with `-entropic=1`, etc.
 Just show the main fuzzer (-M) with the `-F` option where the queue/work
 directory of a different fuzzer is, e.g. `-F /src/target/honggfuzz`.
+Using honggfuzz (with `-n 1` or `-n 2`) and libfuzzer in parallel is highly
+recommended!
 
 #### c) The status of the fuzz campaign
 
@@ -767,25 +785,26 @@ campaigns as these are much shorter runnings.
      corpus needs to be loaded.
   * `AFL_CMPLOG_ONLY_NEW` - only perform cmplog on new found paths, not the
     initial corpus as this very likely has been done for them already.
-  * Keep the generated corpus, use afl-cmin and reuse it everytime!
+  * Keep the generated corpus, use afl-cmin and reuse it every time!
 
 2. Additionally randomize the afl++ compilation options, e.g.
   * 40% for `AFL_LLVM_CMPLOG`
   * 10% for `AFL_LLVM_LAF_ALL`
 
 3. Also randomize the afl-fuzz runtime options, e.g.
-  * 60% for `AFL_DISABLE_TRIM`
+  * 65% for `AFL_DISABLE_TRIM`
   * 50% use a dictionary generated by `AFL_LLVM_DICT2FILE`
-  * 50% use MOpt (`-L 0`)
+  * 40% use MOpt (`-L 0`)
   * 40% for `AFL_EXPAND_HAVOC_NOW`
-  * 30% for old queue processing (`-Z`)
+  * 20% for old queue processing (`-Z`)
   * for CMPLOG targets, 60% for `-l 2`, 40% for `-l 3`
 
 4. Do *not* run any `-M` modes, just running `-S` modes is better for CI fuzzing.
-   `-M` enables deterministic fuzzing, old queue handling etc. which is good for
-   a fuzzing campaign but not good for short CI runs.
+   `-M` enables old queue handling etc. which is good for a fuzzing campaign but
+   not good for short CI runs.
 
-How this can look like can e.g. be seen at afl++'s setup in Google's [oss-fuzz](https://github.com/google/oss-fuzz/blob/4bb61df7905c6005000f5766e966e6fe30ab4559/infra/base-images/base-builder/compile_afl#L69).
+How this can look like can e.g. be seen at afl++'s setup in Google's [oss-fuzz](https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/compile_afl)
+and [clusterfuzz](https://github.com/google/clusterfuzz/blob/master/src/python/bot/fuzzers/afl/launcher.py).
 
 ## Fuzzing binary-only targets
 
diff --git a/TODO.md b/TODO.md
index 398f3d11..1c616b4a 100644
--- a/TODO.md
+++ b/TODO.md
@@ -2,13 +2,11 @@
 
 ## Roadmap 3.00+
 
- - align map to 64 bytes but keep real IDs
  - Update afl->pending_not_fuzzed for MOpt
  - put fuzz target in top line of UI
  - afl-plot to support multiple plot_data
  - afl_custom_fuzz_splice_optin()
  - afl_custom_splice()
- - intel-pt tracer
  - better autodetection of shifting runtime timeout values
  - cmplog: use colorization input for havoc?
  - parallel builds for source-only targets
diff --git a/afl-cmin b/afl-cmin
index 9fa63ec6..e71873d3 100755
--- a/afl-cmin
+++ b/afl-cmin
@@ -296,13 +296,13 @@ BEGIN {
     exit 1
   }
 
-  if (0 == system( "test -d "in_dir"/default" )) {
-    in_dir = in_dir "/default"
-  }
-
-  if (0 == system( "test -d "in_dir"/queue" )) {
-    in_dir = in_dir "/queue"
-  }
+  #if (0 == system( "test -d "in_dir"/default" )) {
+  #  in_dir = in_dir "/default"
+  #}
+  #
+  #if (0 == system( "test -d "in_dir"/queue" )) {
+  #  in_dir = in_dir "/queue"
+  #}
 
   system("rm -rf "trace_dir" 2>/dev/null");
   system("rm "out_dir"/id[:_]* 2>/dev/null")
@@ -355,30 +355,35 @@ BEGIN {
   } else {
     stat_format = "-f '%z %N'" # *BSD, MacOS
   }
-  cmdline = "(cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} + | sort -k1n -k2r)"
+  cmdline = "(cd "in_dir" && find . \\( ! -name \".*\" -a -type d \\) -o -type f -exec stat "stat_format" \\{\\} + | sort -k1n -k2r)"
   #cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format" 2>/dev/null) | sort -k1n -k2r"
   #cmdline = "(cd "in_dir" && stat "stat_format" *) | sort -k1n -k2r"
   #cmdline = "(cd "in_dir" && ls | xargs stat "stat_format" ) | sort -k1n -k2r"
   while (cmdline | getline) {
     sub(/^[0-9]+ (\.\/)?/,"",$0)
-    infilesSmallToBig[i++] = $0
+    infilesSmallToBigFull[i] = $0
+    sub(/.*\//, "", $0)
+    infilesSmallToBig[i] = $0
+    infilesSmallToBigMap[infilesSmallToBig[i]] = infilesSmallToBigFull[i]
+    infilesSmallToBigFullMap[infilesSmallToBigFull[i]] = infilesSmallToBig[i]
+    i++
   }
   in_count = i
 
-  first_file = infilesSmallToBig[0]
+  first_file = infilesSmallToBigFull[0]
   
-  # Make sure that we're not dealing with a directory.
-
-  if (0 == system("test -d ""\""in_dir"/"first_file"\"")) {
-    print "[-] Error: The input directory is empty or contains subdirectories - please fix." > "/dev/stderr"
-    exit 1
-  }
+  #if (0 == system("test -d ""\""in_dir"/"first_file"\"")) {
+  #  print "[-] Error: The input directory is empty or contains subdirectories - please fix." > "/dev/stderr"
+  #  exit 1
+  #}
 
-  if (0 == system("ln \""in_dir"/"first_file"\" "trace_dir"/.link_test")) {
+  system(">\""in_dir"/.afl-cmin.test\"")
+  if (0 == system("ln \""in_dir"/.afl-cmin.test\" "trace_dir"/.link_test")) {
     cp_tool = "ln"
   } else {
     cp_tool = "cp"
   }
+  system("rm -f \""in_dir"/.afl-cmin.test\"")
 
   if (!ENVIRON["AFL_SKIP_BIN_CHECK"]) {
     # Make sure that we can actually get anything out of afl-showmap before we
@@ -511,7 +516,8 @@ BEGIN {
 
     # copy file unless already done
     if (! (fn in file_already_copied)) {
-      system(cp_tool" \""in_dir"/"fn"\" \""out_dir"/"fn"\"")
+      realfile = infilesSmallToBigMap[fn]
+      system(cp_tool" \""in_dir"/"realfile"\" \""out_dir"/"fn"\"")
       file_already_copied[fn] = ""
       ++out_count
       #printf "tuple nr %d (%d cnt=%d) -> %s\n",tcnt,key,key_count[key],fn > trace_dir"/.log"
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 9f70535a..475240c2 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -16,6 +16,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
     - if the target becomes unavailable check out out/default/error.txt
       for an indicator why
     - AFL_CAL_FAST was a dead env, now does the same as AFL_FAST_CAL
+    - reverse read the queue on resumes (more effective)
   - afl-cc:
     - Update to COMPCOV/laf-intel that speeds up the instrumentation
       process a lot - thanks to Michael Rodler/f0rki for the PR!
@@ -24,10 +25,14 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
     - support partial linking
     - We do support llvm versions from 3.8 to 5.0 again
   - frida_mode:
-    - fix for cmplog
+    - several fixes for cmplog
     - remove need for AFL_FRIDA_PERSISTENT_RETADDR_OFFSET
     - feature parity of aarch64 with intel now (persistent, cmplog,
       in-memory testcases, asan)
+  - qemu_mode:
+    - performance fix when cmplog was used
+  - afl-cmin and afl-showmap -i do now descend into subdirectories
+    (like afl-fuzz does) - note that afl-cmin.bash does not!
   - afl_analyze:
     - fix timeout handling
     - add forkserver support for better performance
diff --git a/docs/parallel_fuzzing.md b/docs/parallel_fuzzing.md
index 8f2afe1b..23872899 100644
--- a/docs/parallel_fuzzing.md
+++ b/docs/parallel_fuzzing.md
@@ -1,7 +1,11 @@
 # Tips for parallel fuzzing
 
-  This document talks about synchronizing afl-fuzz jobs on a single machine
-  or across a fleet of systems. See README.md for the general instruction manual.
+This document talks about synchronizing afl-fuzz jobs on a single machine
+or across a fleet of systems. See README.md for the general instruction manual.
+
+Note that this document is rather outdated. please refer to the main document
+section on multiple core usage [../README.md#Using multiple cores](../README.md#b-using-multiple-coresthreads)
+for up to date strategies!
 
 ## 1) Introduction
 
diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile
index 329d9f7f..2f637412 100644
--- a/frida_mode/GNUmakefile
+++ b/frida_mode/GNUmakefile
@@ -25,8 +25,7 @@ RT_CFLAGS:=-Wno-unused-parameter \
 LDFLAGS+=-shared \
 		 -lpthread \
 		 -lresolv \
-		 -ldl \
-		 -z noexecstack \
+		 -ldl
 
 ifdef DEBUG
 CFLAGS+=-Werror \
@@ -60,6 +59,7 @@ else
 ifdef DEBUG
  RT_CFLAGS:=$(RT_CFLAGS) -Wno-prio-ctor-dtor
 endif
+LDFLAGS+=-z noexecstack
 endif
 
 ifeq "$(shell uname)" "Linux"
diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c
index 3df7d13d..0e3fbe53 100644
--- a/frida_mode/src/cmplog/cmplog.c
+++ b/frida_mode/src/cmplog/cmplog.c
@@ -1,7 +1,9 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <syscall.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
 
 #include "frida-gum.h"
 
@@ -10,18 +12,21 @@
 #include "util.h"
 
 #define DEFAULT_MMAP_MIN_ADDR (32UL << 10)
-#define FD_TMP_MAX_SIZE 65536
+#define MAX_MEMFD_SIZE (64UL << 10)
 
 extern struct cmp_map *__afl_cmp_map;
+static GArray *        cmplog_ranges = NULL;
+static GHashTable *    hash_yes = NULL;
+static GHashTable *    hash_no = NULL;
 
-static GArray *cmplog_ranges = NULL;
-static int     fd_tmp = -1;
-static ssize_t fd_tmp_size = 0;
+static long page_size = 0;
+static long page_offset_mask = 0;
+static long page_mask = 0;
 
 static gboolean cmplog_range(const GumRangeDetails *details,
                              gpointer               user_data) {
 
-  UNUSED_PARAMETER(user_data);
+  GArray *       cmplog_ranges = (GArray *)user_data;
   GumMemoryRange range = *details->range;
   g_array_append_val(cmplog_ranges, range);
   return TRUE;
@@ -35,54 +40,46 @@ static gint cmplog_sort(gconstpointer a, gconstpointer b) {
 
 }
 
-static int cmplog_create_temp(void) {
+static void cmplog_get_ranges(void) {
 
-  const char *tmpdir = g_get_tmp_dir();
-  OKF("CMPLOG Temporary directory: %s", tmpdir);
-  gchar *fname = g_strdup_printf("%s/frida-cmplog-XXXXXX", tmpdir);
-  OKF("CMPLOG Temporary file template: %s", fname);
-  int fd = mkstemp(fname);
-  OKF("CMPLOG Temporary file: %s", fname);
+  OKF("CMPLOG - Collecting ranges");
 
-  if (fd < 0) {
-
-    FATAL("Failed to create temp file: %s, errno: %d", fname, errno);
+  cmplog_ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), 100);
+  gum_process_enumerate_ranges(GUM_PAGE_READ, cmplog_range, cmplog_ranges);
+  g_array_sort(cmplog_ranges, cmplog_sort);
 
-  }
+}
 
-  if (unlink(fname) < 0) {
+void cmplog_init(void) {
 
-    FATAL("Failed to unlink temp file: %s (%d), errno: %d", fname, fd, errno);
+  if (__afl_cmp_map != NULL) { OKF("CMPLOG mode enabled"); }
 
-  }
+  cmplog_get_ranges();
 
-  if (ftruncate(fd, 0) < 0) {
+  for (guint i = 0; i < cmplog_ranges->len; i++) {
 
-    FATAL("Failed to ftruncate temp file: %s (%d), errno: %d", fname, fd,
-          errno);
+    GumMemoryRange *range = &g_array_index(cmplog_ranges, GumMemoryRange, i);
+    OKF("CMPLOG Range - %3u: 0x%016" G_GINT64_MODIFIER
+        "X - 0x%016" G_GINT64_MODIFIER "X",
+        i, range->base_address, range->base_address + range->size);
 
   }
 
-  g_free(fname);
+  page_size = sysconf(_SC_PAGE_SIZE);
+  page_offset_mask = page_size - 1;
+  page_mask = ~(page_offset_mask);
 
-  return fd;
-
-}
+  hash_yes = g_hash_table_new(g_direct_hash, g_direct_equal);
+  if (hash_yes == NULL) {
 
-void cmplog_init(void) {
-
-  if (__afl_cmp_map != NULL) { OKF("CMPLOG mode enabled"); }
+    FATAL("Failed to g_hash_table_new, errno: %d", errno);
 
-  cmplog_ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), 100);
-  gum_process_enumerate_ranges(GUM_PAGE_READ, cmplog_range, NULL);
-  g_array_sort(cmplog_ranges, cmplog_sort);
+  }
 
-  for (guint i = 0; i < cmplog_ranges->len; i++) {
+  hash_no = g_hash_table_new(g_direct_hash, g_direct_equal);
+  if (hash_no == NULL) {
 
-    GumMemoryRange *range = &g_array_index(cmplog_ranges, GumMemoryRange, i);
-    OKF("CMPLOG Range - 0x%016" G_GINT64_MODIFIER "X - 0x%016" G_GINT64_MODIFIER
-        "X",
-        range->base_address, range->base_address + range->size);
+    FATAL("Failed to g_hash_table_new, errno: %d", errno);
 
   }
 
@@ -102,6 +99,45 @@ static gboolean cmplog_contains(GumAddress inner_base, GumAddress inner_limit,
 
 }
 
+gboolean cmplog_test_addr(guint64 addr, size_t size) {
+
+  if (g_hash_table_contains(hash_yes, (gpointer)addr)) { return true; }
+  if (g_hash_table_contains(hash_no, (gpointer)addr)) { return false; }
+
+  void * page_addr = (void *)(addr & page_mask);
+  size_t page_offset = addr & page_offset_mask;
+
+  /* If it spans a page, then bail */
+  if (page_size - page_offset < size) { return false; }
+
+  /*
+   * Our address map can change (e.g. stack growth), use msync as a fallback to
+   * validate our address.
+   */
+  if (msync(page_addr, page_offset + size, MS_ASYNC) < 0) {
+
+    if (!g_hash_table_add(hash_no, (gpointer)addr)) {
+
+      FATAL("Failed - g_hash_table_add");
+
+    }
+
+    return false;
+
+  } else {
+
+    if (!g_hash_table_add(hash_yes, (gpointer)addr)) {
+
+      FATAL("Failed - g_hash_table_add");
+
+    }
+
+    return true;
+
+  }
+
+}
+
 gboolean cmplog_is_readable(guint64 addr, size_t size) {
 
   if (cmplog_ranges == NULL) FATAL("CMPLOG not initialized");
@@ -125,45 +161,16 @@ gboolean cmplog_is_readable(guint64 addr, size_t size) {
   for (guint i = 0; i < cmplog_ranges->len; i++) {
 
     GumMemoryRange *range = &g_array_index(cmplog_ranges, GumMemoryRange, i);
-    GumAddress      outer_base = range->base_address;
-    GumAddress      outer_limit = outer_base + range->size;
+
+    GumAddress outer_base = range->base_address;
+    GumAddress outer_limit = outer_base + range->size;
 
     if (cmplog_contains(inner_base, inner_limit, outer_base, outer_limit))
       return true;
 
   }
 
-  /*
-   * Our address map can change (e.g. stack growth), use write as a fallback to
-   * validate our address.
-   */
-  ssize_t written = syscall(__NR_write, fd_tmp, (void *)addr, size);
-
-  /*
-   * If the write succeeds, then the buffer must be valid otherwise it would
-   * return EFAULT
-   */
-  if (written > 0) {
-
-    fd_tmp_size += written;
-    if (fd_tmp_size > FD_TMP_MAX_SIZE) {
-
-      /*
-       * Truncate the file, we don't want our temp file to continue growing!
-       */
-      if (ftruncate(fd_tmp, 0) < 0) {
-
-        FATAL("Failed to truncate fd_tmp (%d), errno: %d", fd_tmp, errno);
-
-      }
-
-      fd_tmp_size = 0;
-
-    }
-
-    if ((size_t)written == size) { return true; }
-
-  }
+  if (cmplog_test_addr(addr, size)) { return true; }
 
   return false;
 
diff --git a/frida_mode/src/cmplog/cmplog_x64.c b/frida_mode/src/cmplog/cmplog_x64.c
index 9f56c32a..ba16445d 100644
--- a/frida_mode/src/cmplog/cmplog_x64.c
+++ b/frida_mode/src/cmplog/cmplog_x64.c
@@ -177,7 +177,7 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
   register uintptr_t k = (uintptr_t)address;
 
   k = (k >> 4) ^ (k << 8);
-  k &= CMP_MAP_W - 1;
+  k &= CMP_MAP_W - 7;
 
   __afl_cmp_map->headers[k].type = CMP_TYPE_INS;
 
@@ -198,8 +198,6 @@ static void cmplog_cmp_sub_callout(GumCpuContext *context, gpointer user_data) {
   gsize              operand1;
   gsize              operand2;
 
-  if (ctx->operand1.size != ctx->operand2.size) FATAL("Operand size mismatch");
-
   if (!cmplog_get_operand_value(context, &ctx->operand1, &operand1)) { return; }
   if (!cmplog_get_operand_value(context, &ctx->operand2, &operand2)) { return; }
 
@@ -233,6 +231,15 @@ static void cmplog_instrument_cmp_sub(const cs_insn *     instr,
 
     case X86_INS_CMP:
     case X86_INS_SUB:
+    case X86_INS_SCASB:
+    case X86_INS_SCASD:
+    case X86_INS_SCASQ:
+    case X86_INS_SCASW:
+    case X86_INS_CMPSB:
+    case X86_INS_CMPSD:
+    case X86_INS_CMPSQ:
+    case X86_INS_CMPSS:
+    case X86_INS_CMPSW:
       break;
     default:
       return;
@@ -247,13 +254,8 @@ static void cmplog_instrument_cmp_sub(const cs_insn *     instr,
   if (operand1->type == X86_OP_INVALID) return;
   if (operand2->type == X86_OP_INVALID) return;
 
-  if ((operand1->type == X86_OP_MEM) &&
-      (operand1->mem.segment != X86_REG_INVALID))
-    return;
-
-  if ((operand2->type == X86_OP_MEM) &&
-      (operand2->mem.segment != X86_REG_INVALID))
-    return;
+  /* Both operands are the same size */
+  if (operand1->size == 1) { return; }
 
   cmplog_instrument_cmp_sub_put_callout(iterator, operand1, operand2);
 
diff --git a/frida_mode/src/ctx/ctx_x64.c b/frida_mode/src/ctx/ctx_x64.c
index c5900533..1772a252 100644
--- a/frida_mode/src/ctx/ctx_x64.c
+++ b/frida_mode/src/ctx/ctx_x64.c
@@ -49,9 +49,18 @@ gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg) {
     X86_REG_8L(X86_REG_BL, ctx->rbx)
     X86_REG_8L(X86_REG_CL, ctx->rcx)
     X86_REG_8L(X86_REG_DL, ctx->rdx)
+    X86_REG_8L(X86_REG_SPL, ctx->rsp)
     X86_REG_8L(X86_REG_BPL, ctx->rbp)
     X86_REG_8L(X86_REG_SIL, ctx->rsi)
     X86_REG_8L(X86_REG_DIL, ctx->rdi)
+    X86_REG_8L(X86_REG_R8B, ctx->r8)
+    X86_REG_8L(X86_REG_R9B, ctx->r9)
+    X86_REG_8L(X86_REG_R10B, ctx->r10)
+    X86_REG_8L(X86_REG_R11B, ctx->r11)
+    X86_REG_8L(X86_REG_R12B, ctx->r12)
+    X86_REG_8L(X86_REG_R13B, ctx->r13)
+    X86_REG_8L(X86_REG_R14B, ctx->r14)
+    X86_REG_8L(X86_REG_R15B, ctx->r15)
 
     X86_REG_8H(X86_REG_AH, ctx->rax)
     X86_REG_8H(X86_REG_BH, ctx->rbx)
@@ -62,14 +71,23 @@ gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg) {
     X86_REG_16(X86_REG_BX, ctx->rbx)
     X86_REG_16(X86_REG_CX, ctx->rcx)
     X86_REG_16(X86_REG_DX, ctx->rdx)
+    X86_REG_16(X86_REG_SP, ctx->rsp)
+    X86_REG_16(X86_REG_BP, ctx->rbp)
     X86_REG_16(X86_REG_DI, ctx->rdi)
     X86_REG_16(X86_REG_SI, ctx->rsi)
-    X86_REG_16(X86_REG_BP, ctx->rbp)
+    X86_REG_16(X86_REG_R8W, ctx->r8)
+    X86_REG_16(X86_REG_R9W, ctx->r9)
+    X86_REG_16(X86_REG_R10W, ctx->r10)
+    X86_REG_16(X86_REG_R11W, ctx->r11)
+    X86_REG_16(X86_REG_R12W, ctx->r12)
+    X86_REG_16(X86_REG_R13W, ctx->r13)
+    X86_REG_16(X86_REG_R14W, ctx->r14)
+    X86_REG_16(X86_REG_R15W, ctx->r15)
 
     X86_REG_32(X86_REG_EAX, ctx->rax)
+    X86_REG_32(X86_REG_EBX, ctx->rbx)
     X86_REG_32(X86_REG_ECX, ctx->rcx)
     X86_REG_32(X86_REG_EDX, ctx->rdx)
-    X86_REG_32(X86_REG_EBX, ctx->rbx)
     X86_REG_32(X86_REG_ESP, ctx->rsp)
     X86_REG_32(X86_REG_EBP, ctx->rbp)
     X86_REG_32(X86_REG_ESI, ctx->rsi)
diff --git a/frida_mode/src/ctx/ctx_x86.c b/frida_mode/src/ctx/ctx_x86.c
index 45308272..9b50cb52 100644
--- a/frida_mode/src/ctx/ctx_x86.c
+++ b/frida_mode/src/ctx/ctx_x86.c
@@ -42,6 +42,7 @@ gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg) {
     X86_REG_8L(X86_REG_BL, ctx->ebx)
     X86_REG_8L(X86_REG_CL, ctx->ecx)
     X86_REG_8L(X86_REG_DL, ctx->edx)
+    X86_REG_8L(X86_REG_SPL, ctx->esp)
     X86_REG_8L(X86_REG_BPL, ctx->ebp)
     X86_REG_8L(X86_REG_SIL, ctx->esi)
     X86_REG_8L(X86_REG_DIL, ctx->edi)
@@ -55,14 +56,15 @@ gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg) {
     X86_REG_16(X86_REG_BX, ctx->ebx)
     X86_REG_16(X86_REG_CX, ctx->ecx)
     X86_REG_16(X86_REG_DX, ctx->edx)
+    X86_REG_16(X86_REG_SP, ctx->esp)
+    X86_REG_16(X86_REG_BP, ctx->ebp)
     X86_REG_16(X86_REG_DI, ctx->edi)
     X86_REG_16(X86_REG_SI, ctx->esi)
-    X86_REG_16(X86_REG_BP, ctx->ebp)
 
     X86_REG_32(X86_REG_EAX, ctx->eax)
+    X86_REG_32(X86_REG_EBX, ctx->ebx)
     X86_REG_32(X86_REG_ECX, ctx->ecx)
     X86_REG_32(X86_REG_EDX, ctx->edx)
-    X86_REG_32(X86_REG_EBX, ctx->ebx)
     X86_REG_32(X86_REG_ESP, ctx->esp)
     X86_REG_32(X86_REG_EBP, ctx->ebp)
     X86_REG_32(X86_REG_ESI, ctx->esi)
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index 7ff23755..b17d9f49 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -101,7 +101,8 @@ static void afl_print_cmdline(void) {
 
   if (fd < 0) {
 
-    FATAL("Failed to open /proc/self/cmdline, errno: (%d)", errno);
+    WARNF("Failed to open /proc/self/cmdline, errno: (%d)", errno);
+    return;
 
   }
 
@@ -138,7 +139,8 @@ static void afl_print_env(void) {
 
   if (fd < 0) {
 
-    FATAL("Failed to open /proc/self/cmdline, errno: (%d)", errno);
+    WARNF("Failed to open /proc/self/cmdline, errno: (%d)", errno);
+    return;
 
   }
 
diff --git a/frida_mode/test/jpeg/GNUmakefile b/frida_mode/test/jpeg/GNUmakefile
new file mode 100644
index 00000000..689fce3d
--- /dev/null
+++ b/frida_mode/test/jpeg/GNUmakefile
@@ -0,0 +1,172 @@
+PWD:=$(shell pwd)/
+ROOT:=$(shell realpath $(PWD)../../..)/
+BUILD_DIR:=$(PWD)build/
+
+AFLPP_DRIVER_HOOK_SRC=$(PWD)aflpp_qemu_driver_hook.c
+AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)aflpp_qemu_driver_hook.so
+
+LIBJPEG_BUILD_DIR:=$(BUILD_DIR)libjpeg/
+HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
+JPEGTEST_BUILD_DIR:=$(BUILD_DIR)jpegtest/
+
+LIBJPEG_URL:=https://github.com/libjpeg-turbo/libjpeg-turbo.git
+LIBJPEG_DIR:=$(LIBJPEG_BUILD_DIR)libjpeg/
+LIBJPEG_CONFIGURE:=$(LIBJPEG_DIR)configure.ac
+LIBJPEG_MAKEFILE:=$(LIBJPEG_DIR)Makefile
+LIBJPEG_LIB:=$(LIBJPEG_DIR).libs/libturbojpeg.a
+
+HARNESS_FILE:=$(HARNESS_BUILD_DIR)StandaloneFuzzTargetMain.c
+HARNESS_OBJ:=$(HARNESS_BUILD_DIR)StandaloneFuzzTargetMain.o
+HARNESS_URL:="https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/utils/aflpp_driver/aflpp_qemu_driver.c"
+
+JPEGTEST_FILE:=$(JPEGTEST_BUILD_DIR)target.cc
+JPEGTEST_OBJ:=$(JPEGTEST_BUILD_DIR)target.o
+JPEGTEST_URL:="https://raw.githubusercontent.com/google/fuzzbench/master/benchmarks/libjpeg-turbo-07-2017/libjpeg_turbo_fuzzer.cc"
+
+LDFLAGS += -lpthread
+
+TEST_BIN:=$(BUILD_DIR)test
+ifeq "$(shell uname)" "Darwin"
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup
+endif
+
+TEST_DATA_DIR:=$(BUILD_DIR)in/
+TEST_DATA_FILE:=$(TEST_DATA_DIR)default_seed
+
+FRIDA_OUT:=$(BUILD_DIR)frida-out
+
+ifndef ARCH
+
+ARCH=$(shell uname -m)
+ifeq "$(ARCH)" "aarch64"
+ ARCH:=arm64
+endif
+
+ifeq "$(ARCH)" "i686"
+ ARCH:=x86
+endif
+endif
+
+ifeq "$(ARCH)" "aarch64"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000aaaaaaaaa000)
+endif
+
+ifeq "$(ARCH)" "x86_64"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000555555554000)
+endif
+
+ifeq "$(ARCH)" "x86"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x56555000)
+endif
+
+.PHONY: all clean frida hook
+
+all: $(TEST_BIN)
+	make -C $(ROOT)frida_mode/
+
+32:
+	CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+######### HARNESS ########
+$(HARNESS_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(HARNESS_FILE): | $(HARNESS_BUILD_DIR)
+	wget -O $@ $(HARNESS_URL)
+
+$(HARNESS_OBJ): $(HARNESS_FILE)
+	$(CC) $(CXXFLAGS) $(LDFLAGS) -o $@ -c $<
+
+######### JPEGTEST ########
+
+$(JPEGTEST_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(JPEGTEST_FILE): | $(JPEGTEST_BUILD_DIR)
+	wget -O $@ $(JPEGTEST_URL)
+
+$(JPEGTEST_OBJ): $(JPEGTEST_FILE) | $(LIBJPEG_MAKEFILE)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) -std=c++11 -I $(LIBJPEG_DIR) -o $@ -c $<
+
+######### LIBJPEG ########
+
+$(LIBJPEG_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(LIBJPEG_CONFIGURE): $(LIBJPEG_BUILD_DIR)
+	git clone $(LIBJPEG_URL) $(LIBJPEG_DIR)
+	cd $(LIBJPEG_DIR) && git checkout b0971e47d76fdb81270e93bbf11ff5558073350d
+
+$(LIBJPEG_MAKEFILE): $(LIBJPEG_CONFIGURE)
+	cd $(LIBJPEG_DIR) && autoreconf -fiv
+	cd $(LIBJPEG_DIR) && ./configure
+
+$(LIBJPEG_LIB): $(LIBJPEG_MAKEFILE)
+	make -C $(LIBJPEG_DIR) -j $(shell nproc)
+
+######### TEST ########
+
+$(TEST_BIN): $(HARNESS_OBJ) $(JPEGTEST_OBJ) $(LIBJPEG_LIB)
+	$(CXX) \
+		$(CFLAGS) \
+		-o $@ \
+		$(HARNESS_OBJ) $(JPEGTEST_OBJ) $(LIBJPEG_LIB) \
+		-lz \
+		$(LDFLAGS) \
+		$(TEST_BIN_LDFLAGS) \
+
+########## HOOK ########
+
+$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
+	$(CC) -shared $(CFLAGS) $(LDFLAGS) $< -o $@
+
+########## DUMMY #######
+
+$(TEST_DATA_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(TEST_DATA_FILE): | $(TEST_DATA_DIR)
+	echo "hi" > $(TEST_DATA_FILE)
+
+###### TEST DATA #######
+
+hook: $(AFLPP_DRIVER_HOOK_OBJ)
+
+clean:
+	rm -rf $(BUILD_DIR)
+
+frida: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE)
+	AFL_DEBUG_CHILD=1 \
+	AFL_DISABLE_TRIM=1 \
+	AFL_FRIDA_PERSISTENT_CNT=1000000 \
+	AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 \
+	AFL_NO_AFFINITY=1 \
+	X__AFL_NO_UI=1 \
+	AFL_PATH=/out \
+	AFL_SHUFFLE_QUEUE=1 \
+	AFL_SKIP_CPUFREQ=1 \
+	AFL_SKIP_CRASHES=1 \
+	AFL_TESTCACHE_SIZE=2 \
+	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	$(ROOT)afl-fuzz \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-m none \
+		-t 1000+ \
+		-d \
+		-O \
+		-c 0\
+		-V 30 \
+		-- \
+			$(TEST_BIN) 2147483647
+
+debug:
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TEST_BIN) $(TEST_DATA_DIR)basn0g01.jpeg
diff --git a/frida_mode/test/jpeg/Makefile b/frida_mode/test/jpeg/Makefile
new file mode 100644
index 00000000..863438cf
--- /dev/null
+++ b/frida_mode/test/jpeg/Makefile
@@ -0,0 +1,19 @@
+all:
+	@echo trying to use GNU make...
+	@gmake all || echo please install GNUmake
+
+32:
+	@echo trying to use GNU make...
+	@gmake 32 || echo please install GNUmake
+
+clean:
+	@gmake clean
+
+frida:
+	@gmake frida
+
+debug:
+	@gmake debug
+
+hook:
+	@gmake hook
diff --git a/frida_mode/test/jpeg/aflpp_qemu_driver_hook.c b/frida_mode/test/jpeg/aflpp_qemu_driver_hook.c
new file mode 100644
index 00000000..059d438d
--- /dev/null
+++ b/frida_mode/test/jpeg/aflpp_qemu_driver_hook.c
@@ -0,0 +1,97 @@
+#include <stdint.h>
+#include <string.h>
+
+#if defined(__x86_64__)
+
+struct x86_64_regs {
+
+  uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
+      r15;
+
+  union {
+
+    uint64_t rip;
+    uint64_t pc;
+
+  };
+
+  union {
+
+    uint64_t rsp;
+    uint64_t sp;
+
+  };
+
+  union {
+
+    uint64_t rflags;
+    uint64_t flags;
+
+  };
+
+  uint8_t zmm_regs[32][64];
+
+};
+
+void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  memcpy((void *)regs->rdi, input_buf, input_buf_len);
+  regs->rsi = input_buf_len;
+
+}
+
+#elif defined(__i386__)
+
+struct x86_regs {
+
+  uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
+
+  union {
+
+    uint32_t eip;
+    uint32_t pc;
+
+  };
+
+  union {
+
+    uint32_t esp;
+    uint32_t sp;
+
+  };
+
+  union {
+
+    uint32_t eflags;
+    uint32_t flags;
+
+  };
+
+  uint8_t xmm_regs[8][16];
+
+};
+
+void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  void **esp = (void **)regs->esp;
+  void * arg1 = esp[1];
+  void **arg2 = &esp[2];
+  memcpy(arg1, input_buf, input_buf_len);
+  *arg2 = (void *)input_buf_len;
+
+}
+
+#else
+  #pragma error "Unsupported architecture"
+#endif
+
+int afl_persistent_hook_init(void) {
+
+  // 1 for shared memory input (faster), 0 for normal input (you have to use
+  // read(), input_buf will be NULL)
+  return 1;
+
+}
+
diff --git a/frida_mode/test/jpeg/get_symbol_addr.py b/frida_mode/test/jpeg/get_symbol_addr.py
new file mode 100755
index 00000000..1c46e010
--- /dev/null
+++ b/frida_mode/test/jpeg/get_symbol_addr.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python3
+import argparse
+from elftools.elf.elffile import ELFFile
+
+def process_file(file, symbol, base):
+    with open(file, 'rb') as f:
+        elf = ELFFile(f)
+        symtab = elf.get_section_by_name('.symtab')
+        mains = symtab.get_symbol_by_name(symbol)
+        if len(mains) != 1:
+            print ("Failed to find main")
+            return 1
+
+        main_addr = mains[0]['st_value']
+        main = base + main_addr
+        print ("0x%016x" % main)
+        return 0
+
+def hex_value(x):
+    return int(x, 16)
+
+def main():
+    parser = argparse.ArgumentParser(description='Process some integers.')
+    parser.add_argument('-f', '--file', dest='file', type=str,
+                    help='elf file name', required=True)
+    parser.add_argument('-s', '--symbol', dest='symbol', type=str,
+                    help='symbol name', required=True)
+    parser.add_argument('-b', '--base', dest='base', type=hex_value,
+                    help='elf base address', required=True)
+
+    args = parser.parse_args()
+    return process_file (args.file, args.symbol, args.base)
+
+if __name__ == "__main__":
+    ret = main()
+    exit(ret)
diff --git a/frida_mode/test/proj4/GNUmakefile b/frida_mode/test/proj4/GNUmakefile
new file mode 100644
index 00000000..09112cd5
--- /dev/null
+++ b/frida_mode/test/proj4/GNUmakefile
@@ -0,0 +1,172 @@
+PWD:=$(shell pwd)/
+ROOT:=$(shell realpath $(PWD)../../..)/
+BUILD_DIR:=$(PWD)build/
+
+AFLPP_DRIVER_HOOK_SRC=$(PWD)aflpp_qemu_driver_hook.c
+AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)aflpp_qemu_driver_hook.so
+
+LIBPROJ4_BUILD_DIR:=$(BUILD_DIR)libproj4/
+HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
+PROJ4TEST_BUILD_DIR:=$(BUILD_DIR)proj4test/
+
+LIBPROJ4_URL:=https://github.com/OSGeo/PROJ
+LIBPROJ4_DIR:=$(LIBPROJ4_BUILD_DIR)libproj4/
+LIBPROJ4_CONFIGURE:=$(LIBPROJ4_DIR)configure.ac
+LIBPROJ4_MAKEFILE:=$(LIBPROJ4_DIR)Makefile
+LIBPROJ4_LIB:=$(LIBPROJ4_DIR)src/.libs/libproj.a
+
+HARNESS_FILE:=$(HARNESS_BUILD_DIR)StandaloneFuzzTargetMain.c
+HARNESS_OBJ:=$(HARNESS_BUILD_DIR)StandaloneFuzzTargetMain.o
+HARNESS_URL:="https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/utils/aflpp_driver/aflpp_qemu_driver.c"
+
+PROJ4TEST_FILE:=$(PROJ4TEST_BUILD_DIR)target.cc
+PROJ4TEST_OBJ:=$(PROJ4TEST_BUILD_DIR)target.o
+PROJ4TEST_URL:="https://raw.githubusercontent.com/OSGeo/PROJ/d00501750b210a73f9fb107ac97a683d4e3d8e7a/test/fuzzers/standard_fuzzer.cpp"
+
+LDFLAGS += -lpthread
+
+TEST_BIN:=$(BUILD_DIR)test
+ifeq "$(shell uname)" "Darwin"
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup
+endif
+
+TEST_DATA_DIR:=$(BUILD_DIR)in/
+TEST_DATA_FILE:=$(TEST_DATA_DIR)default_seed
+
+FRIDA_OUT:=$(BUILD_DIR)frida-out
+
+ifndef ARCH
+
+ARCH=$(shell uname -m)
+ifeq "$(ARCH)" "aarch64"
+ ARCH:=arm64
+endif
+
+ifeq "$(ARCH)" "i686"
+ ARCH:=x86
+endif
+endif
+
+ifeq "$(ARCH)" "aarch64"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000aaaaaaaaa000)
+endif
+
+ifeq "$(ARCH)" "x86_64"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000555555554000)
+endif
+
+ifeq "$(ARCH)" "x86"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x56555000)
+endif
+
+.PHONY: all clean frida hook
+
+all: $(TEST_BIN)
+	make -C $(ROOT)frida_mode/
+
+32:
+	CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+######### HARNESS ########
+$(HARNESS_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(HARNESS_FILE): | $(HARNESS_BUILD_DIR)
+	wget -O $@ $(HARNESS_URL)
+
+$(HARNESS_OBJ): $(HARNESS_FILE)
+	$(CC) $(CXXFLAGS) $(LDFLAGS) -o $@ -c $<
+
+######### PROJ4TEST ########
+
+$(PROJ4TEST_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(PROJ4TEST_FILE): | $(PROJ4TEST_BUILD_DIR)
+	wget -O $@ $(PROJ4TEST_URL)
+
+$(PROJ4TEST_OBJ): $(PROJ4TEST_FILE) | $(LIBPROJ4_MAKEFILE)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) -std=c++11 -I $(LIBPROJ4_DIR)src/ -o $@ -c $<
+
+######### LIBPROJ4 ########
+
+$(LIBPROJ4_BUILD_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(LIBPROJ4_CONFIGURE): $(LIBPROJ4_BUILD_DIR)
+	git clone $(LIBPROJ4_URL) $(LIBPROJ4_DIR)
+	cd $(LIBPROJ4_DIR) && git checkout d00501750b210a73f9fb107ac97a683d4e3d8e7a
+
+$(LIBPROJ4_MAKEFILE): $(LIBPROJ4_CONFIGURE)
+	cd $(LIBPROJ4_DIR) && ./autogen.sh
+	cd $(LIBPROJ4_DIR) && ./configure
+
+$(LIBPROJ4_LIB): $(LIBPROJ4_MAKEFILE)
+	make -C $(LIBPROJ4_DIR) -j $(shell nproc)
+
+######### TEST ########
+
+$(TEST_BIN): $(HARNESS_OBJ) $(PROJ4TEST_OBJ) $(LIBPROJ4_LIB)
+	$(CXX) \
+		$(CFLAGS) \
+		-o $@ \
+		$(HARNESS_OBJ) $(PROJ4TEST_OBJ) $(LIBPROJ4_LIB) \
+		-lz \
+		$(LDFLAGS) \
+		$(TEST_BIN_LDFLAGS) \
+
+########## HOOK ########
+
+$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
+	$(CC) -shared $(CFLAGS) $(LDFLAGS) $< -o $@
+
+########## DUMMY #######
+
+$(TEST_DATA_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(TEST_DATA_FILE): | $(TEST_DATA_DIR)
+	echo "hi" > $(TEST_DATA_FILE)
+
+###### TEST DATA #######
+
+hook: $(AFLPP_DRIVER_HOOK_OBJ)
+
+clean:
+	rm -rf $(BUILD_DIR)
+
+frida: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE)
+	AFL_DEBUG_CHILD=1 \
+	AFL_DISABLE_TRIM=1 \
+	AFL_FRIDA_PERSISTENT_CNT=1000000 \
+	AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 \
+	AFL_NO_AFFINITY=1 \
+	X__AFL_NO_UI=1 \
+	AFL_PATH=/out \
+	AFL_SHUFFLE_QUEUE=1 \
+	AFL_SKIP_CPUFREQ=1 \
+	AFL_SKIP_CRASHES=1 \
+	AFL_TESTCACHE_SIZE=2 \
+	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	$(ROOT)afl-fuzz \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-m none \
+		-t 1000+ \
+		-d \
+		-O \
+		-c 0\
+		-V 30 \
+		-- \
+			$(TEST_BIN) 2147483647
+
+debug:
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TEST_BIN) $(TEST_DATA_DIR)basn0g01.proj4
diff --git a/frida_mode/test/proj4/Makefile b/frida_mode/test/proj4/Makefile
new file mode 100644
index 00000000..863438cf
--- /dev/null
+++ b/frida_mode/test/proj4/Makefile
@@ -0,0 +1,19 @@
+all:
+	@echo trying to use GNU make...
+	@gmake all || echo please install GNUmake
+
+32:
+	@echo trying to use GNU make...
+	@gmake 32 || echo please install GNUmake
+
+clean:
+	@gmake clean
+
+frida:
+	@gmake frida
+
+debug:
+	@gmake debug
+
+hook:
+	@gmake hook
diff --git a/frida_mode/test/proj4/aflpp_qemu_driver_hook.c b/frida_mode/test/proj4/aflpp_qemu_driver_hook.c
new file mode 100644
index 00000000..059d438d
--- /dev/null
+++ b/frida_mode/test/proj4/aflpp_qemu_driver_hook.c
@@ -0,0 +1,97 @@
+#include <stdint.h>
+#include <string.h>
+
+#if defined(__x86_64__)
+
+struct x86_64_regs {
+
+  uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
+      r15;
+
+  union {
+
+    uint64_t rip;
+    uint64_t pc;
+
+  };
+
+  union {
+
+    uint64_t rsp;
+    uint64_t sp;
+
+  };
+
+  union {
+
+    uint64_t rflags;
+    uint64_t flags;
+
+  };
+
+  uint8_t zmm_regs[32][64];
+
+};
+
+void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  memcpy((void *)regs->rdi, input_buf, input_buf_len);
+  regs->rsi = input_buf_len;
+
+}
+
+#elif defined(__i386__)
+
+struct x86_regs {
+
+  uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
+
+  union {
+
+    uint32_t eip;
+    uint32_t pc;
+
+  };
+
+  union {
+
+    uint32_t esp;
+    uint32_t sp;
+
+  };
+
+  union {
+
+    uint32_t eflags;
+    uint32_t flags;
+
+  };
+
+  uint8_t xmm_regs[8][16];
+
+};
+
+void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  void **esp = (void **)regs->esp;
+  void * arg1 = esp[1];
+  void **arg2 = &esp[2];
+  memcpy(arg1, input_buf, input_buf_len);
+  *arg2 = (void *)input_buf_len;
+
+}
+
+#else
+  #pragma error "Unsupported architecture"
+#endif
+
+int afl_persistent_hook_init(void) {
+
+  // 1 for shared memory input (faster), 0 for normal input (you have to use
+  // read(), input_buf will be NULL)
+  return 1;
+
+}
+
diff --git a/frida_mode/test/proj4/get_symbol_addr.py b/frida_mode/test/proj4/get_symbol_addr.py
new file mode 100755
index 00000000..1c46e010
--- /dev/null
+++ b/frida_mode/test/proj4/get_symbol_addr.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python3
+import argparse
+from elftools.elf.elffile import ELFFile
+
+def process_file(file, symbol, base):
+    with open(file, 'rb') as f:
+        elf = ELFFile(f)
+        symtab = elf.get_section_by_name('.symtab')
+        mains = symtab.get_symbol_by_name(symbol)
+        if len(mains) != 1:
+            print ("Failed to find main")
+            return 1
+
+        main_addr = mains[0]['st_value']
+        main = base + main_addr
+        print ("0x%016x" % main)
+        return 0
+
+def hex_value(x):
+    return int(x, 16)
+
+def main():
+    parser = argparse.ArgumentParser(description='Process some integers.')
+    parser.add_argument('-f', '--file', dest='file', type=str,
+                    help='elf file name', required=True)
+    parser.add_argument('-s', '--symbol', dest='symbol', type=str,
+                    help='symbol name', required=True)
+    parser.add_argument('-b', '--base', dest='base', type=hex_value,
+                    help='elf base address', required=True)
+
+    args = parser.parse_args()
+    return process_file (args.file, args.symbol, args.base)
+
+if __name__ == "__main__":
+    ret = main()
+    exit(ret)
diff --git a/include/forkserver.h b/include/forkserver.h
index 2baa6f0a..c6f7de00 100644
--- a/include/forkserver.h
+++ b/include/forkserver.h
@@ -54,6 +54,7 @@ typedef struct afl_forkserver {
   u32 exec_tmout;                       /* Configurable exec timeout (ms)   */
   u32 init_tmout;                       /* Configurable init timeout (ms)   */
   u32 map_size;                         /* map size used by the target      */
+  u32 real_map_size;                    /* real map size, unaligned         */
   u32 snapshot;                         /* is snapshot feature used         */
   u64 mem_limit;                        /* Memory cap for child (MB)        */
 
diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c
index 50117012..d4529e2c 100644
--- a/instrumentation/afl-compiler-rt.o.c
+++ b/instrumentation/afl-compiler-rt.o.c
@@ -271,12 +271,6 @@ static void __afl_map_shm(void) {
 
   if (__afl_final_loc) {
 
-    if (__afl_final_loc % 64) {
-
-      __afl_final_loc = (((__afl_final_loc + 63) >> 6) << 6);
-
-    }
-
     __afl_map_size = __afl_final_loc;
 
     if (__afl_final_loc > MAP_SIZE) {
@@ -623,6 +617,7 @@ static void __afl_unmap_shm(void) {
 #endif
 
     __afl_cmp_map = NULL;
+    __afl_cmp_map_backup = NULL;
 
   }
 
@@ -1019,7 +1014,7 @@ static void __afl_start_forkserver(void) {
 
       if (read(FORKSRV_FD, &was_killed, 4) != 4) {
 
-        write_error("read from afl-fuzz");
+        //write_error("read from afl-fuzz");
         _exit(1);
 
       }
@@ -1690,7 +1685,7 @@ void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t attr,
 
 void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2, uint8_t attr) {
 
-  if (unlikely(!__afl_cmp_map)) return;
+  if (likely(!__afl_cmp_map)) return;
 
   uintptr_t k = (uintptr_t)__builtin_return_address(0);
   k = (k >> 4) ^ (k << 8);
@@ -1794,7 +1789,7 @@ void __sanitizer_cov_trace_const_cmp16(uint128_t arg1, uint128_t arg2) {
 
 void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
 
-  if (unlikely(!__afl_cmp_map)) return;
+  if (likely(!__afl_cmp_map)) return;
 
   for (uint64_t i = 0; i < cases[0]; i++) {
 
@@ -1891,7 +1886,7 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
     fprintf(stderr, "\n");
   */
 
-  if (unlikely(!__afl_cmp_map)) return;
+  if (likely(!__afl_cmp_map)) return;
   // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2);
   int l1, l2;
   if ((l1 = area_is_valid(ptr1, 32)) <= 0 ||
@@ -1975,7 +1970,7 @@ static u8 *get_llvm_stdstring(u8 *string) {
 
 void __cmplog_rtn_gcc_stdstring_cstring(u8 *stdstring, u8 *cstring) {
 
-  if (unlikely(!__afl_cmp_map)) return;
+  if (likely(!__afl_cmp_map)) return;
   if (area_is_valid(stdstring, 32) <= 0 || area_is_valid(cstring, 32) <= 0)
     return;
 
@@ -1985,7 +1980,7 @@ void __cmplog_rtn_gcc_stdstring_cstring(u8 *stdstring, u8 *cstring) {
 
 void __cmplog_rtn_gcc_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
 
-  if (unlikely(!__afl_cmp_map)) return;
+  if (likely(!__afl_cmp_map)) return;
   if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0)
     return;
 
@@ -1996,7 +1991,7 @@ void __cmplog_rtn_gcc_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
 
 void __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring, u8 *cstring) {
 
-  if (unlikely(!__afl_cmp_map)) return;
+  if (likely(!__afl_cmp_map)) return;
   if (area_is_valid(stdstring, 32) <= 0 || area_is_valid(cstring, 32) <= 0)
     return;
 
@@ -2006,7 +2001,7 @@ void __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring, u8 *cstring) {
 
 void __cmplog_rtn_llvm_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
 
-  if (unlikely(!__afl_cmp_map)) return;
+  if (likely(!__afl_cmp_map)) return;
   if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0)
     return;
 
@@ -2040,7 +2035,7 @@ void __afl_coverage_on() {
   if (likely(__afl_selective_coverage && __afl_selective_coverage_temp)) {
 
     __afl_area_ptr = __afl_area_ptr_backup;
-    __afl_cmp_map = __afl_cmp_map_backup;
+    if (__afl_cmp_map_backup) { __afl_cmp_map = __afl_cmp_map_backup; }
 
   }
 
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index 3d472b36..5e8fb9b5 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -90,6 +90,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
   /* exec related stuff */
   fsrv->child_pid = -1;
   fsrv->map_size = get_map_size();
+  fsrv->real_map_size = fsrv->map_size;
   fsrv->use_fauxsrv = false;
   fsrv->last_run_timed_out = false;
   fsrv->debug = false;
@@ -110,6 +111,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) {
   fsrv_to->init_tmout = from->init_tmout;
   fsrv_to->mem_limit = from->mem_limit;
   fsrv_to->map_size = from->map_size;
+  fsrv_to->real_map_size = from->real_map_size;
   fsrv_to->support_shmem_fuzz = from->support_shmem_fuzz;
   fsrv_to->out_file = from->out_file;
   fsrv_to->dev_urandom_fd = from->dev_urandom_fd;
@@ -416,8 +418,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
     struct rlimit r;
 
-    if (!fsrv->cmplog_binary && fsrv->qemu_mode == false &&
-        fsrv->frida_mode == false) {
+    if (!fsrv->cmplog_binary) {
 
       unsetenv(CMPLOG_SHM_ENV_VAR);  // we do not want that in non-cmplog fsrv
 
@@ -691,15 +692,15 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
         if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; }
 
-        if (unlikely(tmp_map_size % 64)) {
+        fsrv->real_map_size = tmp_map_size;
+
+        if (tmp_map_size % 64) {
 
-          // should not happen
-          WARNF("Target reported non-aligned map size of %u", tmp_map_size);
           tmp_map_size = (((tmp_map_size + 63) >> 6) << 6);
 
         }
 
-        if (!be_quiet) { ACTF("Target map size: %u", tmp_map_size); }
+        if (!be_quiet) { ACTF("Target map size: %u", fsrv->real_map_size); }
         if (tmp_map_size > fsrv->map_size) {
 
           FATAL(
diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c
index c2e9c80f..c684c4b0 100644
--- a/src/afl-fuzz-cmplog.c
+++ b/src/afl-fuzz-cmplog.c
@@ -33,7 +33,7 @@ void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv) {
 
   setenv("___AFL_EINS_ZWEI_POLIZEI___", "1", 1);
 
-  if (fsrv->qemu_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); }
+  if (fsrv->qemu_mode || fsrv->frida_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); }
 
   if (!fsrv->qemu_mode && !fsrv->frida_mode && argv[0] != fsrv->cmplog_binary) {
 
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index 872e3a32..5e4f1585 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -710,96 +710,103 @@ void read_testcases(afl_state_t *afl, u8 *directory) {
 
   }
 
-  for (i = 0; i < (u32)nl_cnt; ++i) {
+  if (nl_cnt) {
 
-    struct stat st;
+    i = nl_cnt;
+    do {
 
-    u8 dfn[PATH_MAX];
-    snprintf(dfn, PATH_MAX, "%s/.state/deterministic_done/%s", afl->in_dir,
-             nl[i]->d_name);
-    u8 *fn2 = alloc_printf("%s/%s", dir, nl[i]->d_name);
+      --i;
 
-    u8 passed_det = 0;
+      struct stat st;
+      u8          dfn[PATH_MAX];
+      snprintf(dfn, PATH_MAX, "%s/.state/deterministic_done/%s", afl->in_dir,
+               nl[i]->d_name);
+      u8 *fn2 = alloc_printf("%s/%s", dir, nl[i]->d_name);
 
-    if (lstat(fn2, &st) || access(fn2, R_OK)) {
+      u8 passed_det = 0;
 
-      PFATAL("Unable to access '%s'", fn2);
+      if (lstat(fn2, &st) || access(fn2, R_OK)) {
 
-    }
+        PFATAL("Unable to access '%s'", fn2);
 
-    /* obviously we want to skip "descending" into . and .. directories,
-       however it is a good idea to skip also directories that start with
-       a dot */
-    if (subdirs && S_ISDIR(st.st_mode) && nl[i]->d_name[0] != '.') {
+      }
 
-      free(nl[i]);                                           /* not tracked */
-      read_testcases(afl, fn2);
-      ck_free(fn2);
-      continue;
+      /* obviously we want to skip "descending" into . and .. directories,
+         however it is a good idea to skip also directories that start with
+         a dot */
+      if (subdirs && S_ISDIR(st.st_mode) && nl[i]->d_name[0] != '.') {
 
-    }
+        free(nl[i]);                                         /* not tracked */
+        read_testcases(afl, fn2);
+        ck_free(fn2);
+        continue;
 
-    free(nl[i]);
+      }
 
-    if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn2, "/README.txt")) {
+      free(nl[i]);
 
-      ck_free(fn2);
-      continue;
+      if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn2, "/README.txt")) {
 
-    }
+        ck_free(fn2);
+        continue;
 
-    if (st.st_size > MAX_FILE) {
+      }
 
-      WARNF("Test case '%s' is too big (%s, limit is %s), partial reading", fn2,
-            stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size),
-            stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE));
+      if (st.st_size > MAX_FILE) {
 
-    }
+        WARNF("Test case '%s' is too big (%s, limit is %s), partial reading",
+              fn2,
+              stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size),
+              stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE));
+
+      }
 
-    /* Check for metadata that indicates that deterministic fuzzing
-       is complete for this entry. We don't want to repeat deterministic
-       fuzzing when resuming aborted scans, because it would be pointless
-       and probably very time-consuming. */
+      /* Check for metadata that indicates that deterministic fuzzing
+         is complete for this entry. We don't want to repeat deterministic
+         fuzzing when resuming aborted scans, because it would be pointless
+         and probably very time-consuming. */
 
-    if (!access(dfn, F_OK)) { passed_det = 1; }
+      if (!access(dfn, F_OK)) { passed_det = 1; }
 
-    add_to_queue(afl, fn2, st.st_size >= MAX_FILE ? MAX_FILE : st.st_size,
-                 passed_det);
+      add_to_queue(afl, fn2, st.st_size >= MAX_FILE ? MAX_FILE : st.st_size,
+                   passed_det);
 
-    if (unlikely(afl->shm.cmplog_mode)) {
+      if (unlikely(afl->shm.cmplog_mode)) {
 
-      if (afl->cmplog_lvl == 1) {
+        if (afl->cmplog_lvl == 1) {
 
-        if (!afl->cmplog_max_filesize ||
-            afl->cmplog_max_filesize < st.st_size) {
+          if (!afl->cmplog_max_filesize ||
+              afl->cmplog_max_filesize < st.st_size) {
 
-          afl->cmplog_max_filesize = st.st_size;
+            afl->cmplog_max_filesize = st.st_size;
 
-        }
+          }
 
-      } else if (afl->cmplog_lvl == 2) {
+        } else if (afl->cmplog_lvl == 2) {
 
-        if (!afl->cmplog_max_filesize ||
-            afl->cmplog_max_filesize > st.st_size) {
+          if (!afl->cmplog_max_filesize ||
+              afl->cmplog_max_filesize > st.st_size) {
 
-          afl->cmplog_max_filesize = st.st_size;
+            afl->cmplog_max_filesize = st.st_size;
+
+          }
 
         }
 
       }
 
-    }
+      /*
+          if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) {
 
-    /*
-        if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) {
+            u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size,
+         HASH_CONST); afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE;
+            afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1;
 
-          u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size,
-       HASH_CONST); afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE;
-          afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1;
+          }
 
-        }
+      */
 
-    */
+    } while (i > 0);
 
   }
 
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c
index 9648d795..e0930234 100644
--- a/src/afl-fuzz-stats.c
+++ b/src/afl-fuzz-stats.c
@@ -264,6 +264,7 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
           "peak_rss_mb       : %lu\n"
           "cpu_affinity      : %d\n"
           "edges_found       : %u\n"
+          "total_edges       : %u\n"
           "var_byte_count    : %u\n"
           "havoc_expansion   : %u\n"
           "testcache_size    : %llu\n"
@@ -303,10 +304,10 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
 #else
           -1,
 #endif
-          t_bytes, afl->var_byte_count, afl->expand_havoc,
-          afl->q_testcase_cache_size, afl->q_testcase_cache_count,
-          afl->q_testcase_evictions, afl->use_banner,
-          afl->unicorn_mode ? "unicorn" : "",
+          t_bytes, afl->fsrv.real_map_size, afl->var_byte_count,
+          afl->expand_havoc, afl->q_testcase_cache_size,
+          afl->q_testcase_cache_count, afl->q_testcase_evictions,
+          afl->use_banner, afl->unicorn_mode ? "unicorn" : "",
           afl->fsrv.qemu_mode ? "qemu " : "",
           afl->non_instrumented_mode ? " non_instrumented " : "",
           afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "",
@@ -326,7 +327,7 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
 
     u32 i = 0;
     fprintf(f, "virgin_bytes     :");
-    for (i = 0; i < afl->fsrv.map_size; i++) {
+    for (i = 0; i < afl->fsrv.real_map_size; i++) {
 
       if (afl->virgin_bits[i] != 0xff) {
 
@@ -338,7 +339,7 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
 
     fprintf(f, "\n");
     fprintf(f, "var_bytes        :");
-    for (i = 0; i < afl->fsrv.map_size; i++) {
+    for (i = 0; i < afl->fsrv.real_map_size; i++) {
 
       if (afl->var_bytes[i]) { fprintf(f, " %u", i); }
 
@@ -520,7 +521,7 @@ void show_stats(afl_state_t *afl) {
   /* Do some bitmap stats. */
 
   t_bytes = count_non_255_bytes(afl, afl->virgin_bits);
-  t_byte_ratio = ((double)t_bytes * 100) / afl->fsrv.map_size;
+  t_byte_ratio = ((double)t_bytes * 100) / afl->fsrv.real_map_size;
 
   if (likely(t_bytes) && unlikely(afl->var_byte_count)) {
 
@@ -781,7 +782,7 @@ void show_stats(afl_state_t *afl) {
   SAYF(bV bSTOP "  now processing : " cRST "%-18s " bSTG bV bSTOP, tmp);
 
   sprintf(tmp, "%0.02f%% / %0.02f%%",
-          ((double)afl->queue_cur->bitmap_size) * 100 / afl->fsrv.map_size,
+          ((double)afl->queue_cur->bitmap_size) * 100 / afl->fsrv.real_map_size,
           t_byte_ratio);
 
   SAYF("    map density : %s%-19s" bSTG bV "\n",
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index e9a67ac5..5f25f728 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -125,7 +125,7 @@ static void usage(u8 *argv0, int more_help) {
       "entering the\n"
       "                  pacemaker mode (minutes of no new paths). 0 = "
       "immediately,\n"
-      "                  -1 = immediately and together with normal mutation).\n"
+      "                  -1 = immediately and together with normal mutation.\n"
       "                  See docs/README.MOpt.md\n"
       "  -c program    - enable CmpLog by specifying a binary compiled for "
       "it.\n"
@@ -1911,7 +1911,12 @@ int main(int argc, char **argv_orig, char **envp) {
   if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl);
 
   afl->start_time = get_cur_time();
-  if (afl->in_place_resume || afl->afl_env.afl_autoresume) load_stats_file(afl);
+  if (afl->in_place_resume || afl->afl_env.afl_autoresume) {
+
+    load_stats_file(afl);
+
+  }
+
   write_stats_file(afl, 0, 0, 0, 0);
   maybe_update_plot_file(afl, 0, 0, 0);
   save_auto(afl);
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index 96b72dd9..936d3bc4 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -67,6 +67,8 @@ static char *stdin_file;               /* stdin file                        */
 static u8 *in_dir = NULL,              /* input folder                      */
     *out_file = NULL, *at_file = NULL;        /* Substitution string for @@ */
 
+static u8 outfile[PATH_MAX];
+
 static u8 *in_data,                    /* Input data                        */
     *coverage_map;                     /* Coverage map                      */
 
@@ -88,7 +90,8 @@ static bool quiet_mode,                /* Hide non-essential messages?      */
     have_coverage,                     /* have coverage?                    */
     no_classify,                       /* do not classify counts            */
     debug,                             /* debug mode                        */
-    print_filenames;                   /* print the current filename        */
+    print_filenames,                   /* print the current filename        */
+    wait_for_gdb;
 
 static volatile u8 stop_soon,          /* Ctrl-C pressed?                   */
     child_crashed;                     /* Child crashed?                    */
@@ -230,7 +233,11 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) {
   u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),
      caa = !!getenv("AFL_CMIN_ALLOW_ANY");
 
-  if (!outfile) { FATAL("Output filename not set (Bug in AFL++?)"); }
+  if (!outfile || !*outfile) {
+
+    FATAL("Output filename not set (Bug in AFL++?)");
+
+  }
 
   if (cmin_mode &&
       (fsrv->last_run_timed_out || (!caa && child_crashed != cco))) {
@@ -692,6 +699,96 @@ static void setup_signal_handlers(void) {
 
 }
 
+u32 execute_testcases(u8 *dir) {
+
+  struct dirent **nl;
+  s32             nl_cnt, subdirs = 1;
+  u32             i, done = 0;
+  u8              val_buf[2][STRINGIFY_VAL_SIZE_MAX];
+
+  if (!be_quiet) { ACTF("Scanning '%s'...", dir); }
+
+  /* We use scandir() + alphasort() rather than readdir() because otherwise,
+     the ordering of test cases would vary somewhat randomly and would be
+     difficult to control. */
+
+  nl_cnt = scandir(dir, &nl, NULL, alphasort);
+
+  if (nl_cnt < 0) { return 0; }
+
+  for (i = 0; i < (u32)nl_cnt; ++i) {
+
+    struct stat st;
+
+    u8 *fn2 = alloc_printf("%s/%s", dir, nl[i]->d_name);
+
+    if (lstat(fn2, &st) || access(fn2, R_OK)) {
+
+      PFATAL("Unable to access '%s'", fn2);
+
+    }
+
+    /* obviously we want to skip "descending" into . and .. directories,
+       however it is a good idea to skip also directories that start with
+       a dot */
+    if (subdirs && S_ISDIR(st.st_mode) && nl[i]->d_name[0] != '.') {
+
+      free(nl[i]);                                           /* not tracked */
+      done += execute_testcases(fn2);
+      ck_free(fn2);
+      continue;
+
+    }
+
+    if (!S_ISREG(st.st_mode) || !st.st_size) {
+
+      free(nl[i]);
+      ck_free(fn2);
+      continue;
+
+    }
+
+    if (st.st_size > MAX_FILE && !be_quiet) {
+
+      WARNF("Test case '%s' is too big (%s, limit is %s), partial reading", fn2,
+            stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size),
+            stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE));
+
+    }
+
+    if (!collect_coverage)
+      snprintf(outfile, sizeof(outfile), "%s/%s", out_file, nl[i]->d_name);
+
+    free(nl[i]);
+
+    if (read_file(fn2)) {
+
+      if (wait_for_gdb) {
+
+        fprintf(stderr, "exec: gdb -p %d\n", fsrv->child_pid);
+        fprintf(stderr, "exec: kill -CONT %d\n", getpid());
+        kill(0, SIGSTOP);
+
+      }
+
+      showmap_run_target_forkserver(fsrv, in_data, in_len);
+      ck_free(in_data);
+      ++done;
+
+      if (collect_coverage)
+        analyze_results(fsrv);
+      else
+        tcnt = write_results_to_file(fsrv, outfile);
+
+    }
+
+  }
+
+  free(nl);                                                  /* not tracked */
+  return done;
+
+}
+
 /* Show banner. */
 
 static void show_banner(void) {
@@ -710,31 +807,31 @@ static void usage(u8 *argv0) {
       "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
 
       "Required parameters:\n"
-      "  -o file       - file to write the trace data to\n\n"
+      "  -o file    - file to write the trace data to\n\n"
 
       "Execution control settings:\n"
-      "  -t msec       - timeout for each run (none)\n"
-      "  -m megs       - memory limit for child process (%u MB)\n"
-      "  -O            - use binary-only instrumentation (FRIDA mode)\n"
-      "  -Q            - use binary-only instrumentation (QEMU mode)\n"
-      "  -U            - use Unicorn-based instrumentation (Unicorn mode)\n"
-      "  -W            - use qemu-based instrumentation with Wine (Wine mode)\n"
-      "                  (Not necessary, here for consistency with other afl-* "
+      "  -t msec    - timeout for each run (none)\n"
+      "  -m megs    - memory limit for child process (%u MB)\n"
+      "  -O         - use binary-only instrumentation (FRIDA mode)\n"
+      "  -Q         - use binary-only instrumentation (QEMU mode)\n"
+      "  -U         - use Unicorn-based instrumentation (Unicorn mode)\n"
+      "  -W         - use qemu-based instrumentation with Wine (Wine mode)\n"
+      "               (Not necessary, here for consistency with other afl-* "
       "tools)\n\n"
       "Other settings:\n"
-      "  -i dir        - process all files in this directory, must be combined "
+      "  -i dir     - process all files below this directory, must be combined "
       "with -o.\n"
-      "                  With -C, -o is a file, without -C it must be a "
+      "               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 "
+      "               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"
-      "  -s            - do not classify the map\n"
-      "  -c            - allow core dumps\n\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"
+      "  -s         - do not classify the map\n"
+      "  -c         - allow core dumps\n\n"
 
       "This tool displays raw tuple data captured by AFL instrumentation.\n"
       "For additional help, consult %s/README.md.\n\n"
@@ -1136,15 +1233,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (in_dir) {
 
-    DIR *           dir_in, *dir_out = NULL;
-    struct dirent **file_list;
-
-    //    int            done = 0;
-    u8 infile[PATH_MAX], outfile[PATH_MAX];
-    u8 wait_for_gdb = 0;
-#if !defined(DT_REG)
-    struct stat statbuf;
-#endif
+    DIR *dir_in, *dir_out = NULL;
 
     if (getenv("AFL_DEBUG_GDB")) wait_for_gdb = true;
 
@@ -1177,7 +1266,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
     } else {
 
-      if ((coverage_map = (u8 *)malloc(map_size)) == NULL)
+      if ((coverage_map = (u8 *)malloc(map_size + 64)) == NULL)
         FATAL("coult not grab memory");
       edges_only = false;
       raw_instr_output = true;
@@ -1245,65 +1334,12 @@ int main(int argc, char **argv_orig, char **envp) {
     if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz)
       shm_fuzz = deinit_shmem(fsrv, shm_fuzz);
 
-    int file_count = scandir(in_dir, &file_list, NULL, alphasort);
-    if (file_count < 0) {
+    if (execute_testcases(in_dir) == 0) {
 
-      PFATAL("Failed to read from input dir at %s\n", in_dir);
+      FATAL("could not read input testcases from %s", in_dir);
 
     }
 
-    for (int i = 0; i < file_count; i++) {
-
-      struct dirent *dir_ent = file_list[i];
-
-      if (dir_ent->d_name[0] == '.') {
-
-        continue;  // skip anything that starts with '.'
-
-      }
-
-#if defined(DT_REG)      /* Posix and Solaris do not know d_type and DT_REG */
-      if (dir_ent->d_type != DT_REG) {
-
-        continue;  // only regular files
-
-      }
-
-#endif
-
-      snprintf(infile, sizeof(infile), "%s/%s", in_dir, dir_ent->d_name);
-
-#if !defined(DT_REG)                                          /* use stat() */
-      if (-1 == stat(infile, &statbuf) || !S_ISREG(statbuf.st_mode)) continue;
-#endif
-
-      if (!collect_coverage)
-        snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name);
-
-      if (read_file(infile)) {
-
-        if (wait_for_gdb) {
-
-          fprintf(stderr, "exec: gdb -p %d\n", fsrv->child_pid);
-          fprintf(stderr, "exec: kill -CONT %d\n", getpid());
-          kill(0, SIGSTOP);
-
-        }
-
-        showmap_run_target_forkserver(fsrv, in_data, in_len);
-        ck_free(in_data);
-        if (collect_coverage)
-          analyze_results(fsrv);
-        else
-          tcnt = write_results_to_file(fsrv, outfile);
-
-      }
-
-    }
-
-    free(file_list);
-    file_list = NULL;
-
     if (!quiet_mode) { OKF("Processed %llu input files.", fsrv->total_execs); }
 
     if (dir_out) { closedir(dir_out); }