about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/afl-common.c155
-rw-r--r--src/afl-forkserver.c17
-rw-r--r--src/afl-fuzz-bitmap.c62
-rw-r--r--src/afl-fuzz-init.c42
-rw-r--r--src/afl-fuzz-one.c284
-rw-r--r--src/afl-fuzz-queue.c177
-rw-r--r--src/afl-fuzz-run.c99
-rw-r--r--src/afl-fuzz-state.c30
-rw-r--r--src/afl-fuzz-stats.c5
-rw-r--r--src/afl-fuzz.c127
10 files changed, 806 insertions, 192 deletions
diff --git a/src/afl-common.c b/src/afl-common.c
index 367dec72..cefed8dc 100644
--- a/src/afl-common.c
+++ b/src/afl-common.c
@@ -138,62 +138,73 @@ void argv_cpy_free(char **argv) {
 
 }
 
-/* Rewrite argv for QEMU. */
-
-char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
+u8 *find_afl_binary(u8 *fname, u8 *own_loc) {
 
-  char **new_argv = ck_alloc(sizeof(char *) * (argc + 4));
-  u8 *   tmp, *cp = NULL, *rsl, *own_copy;
-
-  memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1));
-  new_argv[argc - 1] = NULL;
-
-  new_argv[2] = *target_path_p;
-  new_argv[1] = "--";
-
-  /* Now we need to actually find the QEMU binary to put in argv[0]. */
+  u8 *tmp, *rsl, *own_copy, *cp;
 
   tmp = getenv("AFL_PATH");
 
   if (tmp) {
 
-    cp = alloc_printf("%s/afl-qemu-trace", tmp);
+    cp = alloc_printf("%s/%s", tmp, fname);
 
     if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
 
-    *target_path_p = new_argv[0] = cp;
-    return new_argv;
+    return cp;
 
   }
 
-  own_copy = ck_strdup(own_loc);
-  rsl = strrchr(own_copy, '/');
+  if (own_loc) {
 
-  if (rsl) {
+    own_copy = ck_strdup(own_loc);
+    rsl = strrchr(own_copy, '/');
 
-    *rsl = 0;
+    if (rsl) {
 
-    cp = alloc_printf("%s/afl-qemu-trace", own_copy);
-    ck_free(own_copy);
+      *rsl = 0;
 
-    if (!access(cp, X_OK)) {
+      cp = alloc_printf("%s/%s", own_copy, fname);
+      ck_free(own_copy);
 
-      *target_path_p = new_argv[0] = cp;
-      return new_argv;
+      if (!access(cp, X_OK)) { return cp; }
 
-    }
+    } else {
 
-  } else {
+      ck_free(own_copy);
 
-    ck_free(own_copy);
+    }
 
   }
 
-  if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
+  cp = alloc_printf("%s/%s", BIN_PATH, fname);
+  if (!access(cp, X_OK)) { return cp; }
+
+  ck_free(cp);
+
+  return NULL;
 
-    if (cp) { ck_free(cp); }
-    *target_path_p = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace");
+}
+
+/* Rewrite argv for QEMU. */
 
+char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
+
+  char **new_argv = ck_alloc(sizeof(char *) * (argc + 4));
+  u8 *   cp = NULL;
+
+  memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1));
+  new_argv[argc - 1] = NULL;
+
+  new_argv[2] = *target_path_p;
+  new_argv[1] = "--";
+
+  /* Now we need to actually find the QEMU binary to put in argv[0]. */
+
+  cp = find_afl_binary("afl-qemu-trace", own_loc);
+
+  if (cp) {
+
+    *target_path_p = new_argv[0] = cp;
     return new_argv;
 
   }
@@ -225,7 +236,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
 char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
 
   char **new_argv = ck_alloc(sizeof(char *) * (argc + 3));
-  u8 *   tmp, *cp = NULL, *rsl, *own_copy;
+  u8 *   cp = NULL;
 
   memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1));
   new_argv[argc - 1] = NULL;
@@ -234,66 +245,16 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
 
   /* Now we need to actually find the QEMU binary to put in argv[0]. */
 
-  tmp = getenv("AFL_PATH");
-
-  if (tmp) {
-
-    cp = alloc_printf("%s/afl-qemu-trace", tmp);
+  cp = find_afl_binary("afl-qemu-trace", own_loc);
 
-    if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
+  if (cp) {
 
     ck_free(cp);
+    cp = find_afl_binary("afl-wine-trace", own_loc);
 
-    cp = alloc_printf("%s/afl-wine-trace", tmp);
-
-    if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
-
-    *target_path_p = new_argv[0] = cp;
-    return new_argv;
-
-  }
-
-  own_copy = ck_strdup(own_loc);
-  rsl = strrchr(own_copy, '/');
-
-  if (rsl) {
-
-    *rsl = 0;
-
-    cp = alloc_printf("%s/afl-qemu-trace", own_copy);
-
-    if (cp && !access(cp, X_OK)) {
-
-      ck_free(cp);
-
-      cp = alloc_printf("%s/afl-wine-trace", own_copy);
-
-      if (!access(cp, X_OK)) {
-
-        *target_path_p = new_argv[0] = cp;
-        return new_argv;
-
-      }
-
-    }
-
-    ck_free(own_copy);
-
-  } else {
-
-    ck_free(own_copy);
-
-  }
-
-  u8 *ncp = BIN_PATH "/afl-qemu-trace";
-
-  if (!access(ncp, X_OK)) {
-
-    ncp = BIN_PATH "/afl-wine-trace";
-
-    if (!access(ncp, X_OK)) {
+    if (cp) {
 
-      *target_path_p = new_argv[0] = ck_strdup(ncp);
+      *target_path_p = new_argv[0] = cp;
       return new_argv;
 
     }
@@ -301,25 +262,21 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
   }
 
   SAYF("\n" cLRD "[-] " cRST
-       "Oops, unable to find the '%s' binary. The binary must be "
-       "built\n"
-       "    separately by following the instructions in "
-       "qemu_mode/README.md. "
-       "If you\n"
-       "    already have the binary installed, you may need to specify "
-       "AFL_PATH in the\n"
-       "    environment.\n\n"
-
+       "Oops, unable to find the afl-qemu-trace and afl-wine-trace binaries.\n"
+       "The afl-qemu-trace binary must be built separately by following the "
+       "instructions\n"
+       "in qemu_mode/README.md. If you already have the binary installed, you "
+       "may need\n"
+       "to specify the location via AFL_PATH in the environment.\n\n"
        "    Of course, even without QEMU, afl-fuzz can still work with "
        "binaries that are\n"
        "    instrumented at compile time with afl-gcc. It is also possible to "
        "use it as a\n"
        "    traditional non-instrumented fuzzer by specifying '-n' in the "
        "command "
-       "line.\n",
-       ncp);
+       "line.\n");
 
-  FATAL("Failed to locate '%s'.", ncp);
+  FATAL("Failed to locate 'afl-qemu-trace' and 'afl-wine-trace'.");
 
 }
 
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index 25983f26..173cc70f 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -498,11 +498,21 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
   char pid_buf[16];
   sprintf(pid_buf, "%d", fsrv->fsrv_pid);
-  if (fsrv->cmplog_binary)
+
+  if (fsrv->taint_mode) {
+
+    setenv("__AFL_TARGET_PID3", pid_buf, 1);
+
+  } else if (fsrv->cmplog_binary) {
+
     setenv("__AFL_TARGET_PID2", pid_buf, 1);
-  else
+
+  } else {
+
     setenv("__AFL_TARGET_PID1", pid_buf, 1);
 
+  }
+
   /* Close the unneeded endpoints. */
 
   close(ctl_pipe[0]);
@@ -937,7 +947,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
 
   } else {
 
-    s32 fd = fsrv->out_fd;
+    s32 fd;
 
     if (fsrv->out_file) {
 
@@ -956,6 +966,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
 
     } else {
 
+      fd = fsrv->out_fd;
       lseek(fd, 0, SEEK_SET);
 
     }
diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c
index 1b9df624..db57061d 100644
--- a/src/afl-fuzz-bitmap.c
+++ b/src/afl-fuzz-bitmap.c
@@ -177,6 +177,40 @@ u32 count_bits(afl_state_t *afl, u8 *mem) {
 
 }
 
+u32 count_bits_len(afl_state_t *afl, u8 *mem, u32 len) {
+
+  u32 *ptr = (u32 *)mem;
+  u32  i = (len >> 2);
+  u32  ret = 0;
+
+  (void)(afl);
+
+  if (len % 4) i++;
+
+  while (i--) {
+
+    u32 v = *(ptr++);
+
+    /* This gets called on the inverse, virgin bitmap; optimize for sparse
+       data. */
+
+    if (v == 0xffffffff) {
+
+      ret += 32;
+      continue;
+
+    }
+
+    v -= ((v >> 1) & 0x55555555);
+    v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+    ret += (((v + (v >> 4)) & 0xF0F0F0F) * 0x01010101) >> 24;
+
+  }
+
+  return ret;
+
+}
+
 /* Count the number of bytes set in the bitmap. Called fairly sporadically,
    mostly to update the status screen or calibrate and examine confirmed
    new paths. */
@@ -203,6 +237,32 @@ u32 count_bytes(afl_state_t *afl, u8 *mem) {
 
 }
 
+u32 count_bytes_len(afl_state_t *afl, u8 *mem, u32 len) {
+
+  u32 *ptr = (u32 *)mem;
+  u32  i = (len >> 2);
+  u32  ret = 0;
+
+  (void)(afl);
+
+  if (len % 4) i++;
+
+  while (i--) {
+
+    u32 v = *(ptr++);
+
+    if (!v) { continue; }
+    if (v & 0x000000ff) { ++ret; }
+    if (v & 0x0000ff00) { ++ret; }
+    if (v & 0x00ff0000) { ++ret; }
+    if (v & 0xff000000) { ++ret; }
+
+  }
+
+  return ret;
+
+}
+
 /* Count the number of non-255 bytes set in the bitmap. Used strictly for the
    status screen, several calls per second or so. */
 
@@ -595,7 +655,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
 #endif                                                    /* ^!SIMPLE_FILES */
 
-    add_to_queue(afl, queue_fn, len, 0);
+    add_to_queue(afl, queue_fn, mem, len, afl->queue_top, 0);
 
     if (hnb == 2) {
 
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index 350a8599..359eef85 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -712,7 +712,7 @@ void read_testcases(afl_state_t *afl) {
 
     if (!access(dfn, F_OK)) { passed_det = 1; }
 
-    add_to_queue(afl, fn2, st.st_size, passed_det);
+    add_to_queue(afl, fn2, NULL, st.st_size, NULL, passed_det);
 
   }
 
@@ -771,9 +771,13 @@ void perform_dry_run(afl_state_t *afl) {
     close(fd);
 
     res = calibrate_case(afl, q, use_mem, 0, 1);
-    ck_free(use_mem);
 
-    if (afl->stop_soon) { return; }
+    if (afl->stop_soon) {
+
+      ck_free(use_mem);
+      return;
+
+    }
 
     if (res == afl->crash_mode || res == FSRV_RUN_NOBITS) {
 
@@ -960,6 +964,10 @@ void perform_dry_run(afl_state_t *afl) {
 
     }
 
+    /* perform taint gathering on the input seed */
+    if (afl->taint_mode) perform_taint_run(afl, q, q->fname, use_mem, q->len);
+    ck_free(use_mem);
+
     q = q->next;
 
   }
@@ -1438,6 +1446,10 @@ static void handle_existing_out_dir(afl_state_t *afl) {
 
     u8 *orig_q = alloc_printf("%s/queue", afl->out_dir);
 
+    u8 *fnt = alloc_printf("%s/taint", afl->out_dir);
+    mkdir(fnt, 0755);  // ignore errors
+    ck_free(fnt);
+
     afl->in_dir = alloc_printf("%s/_resume", afl->out_dir);
 
     rename(orig_q, afl->in_dir);                           /* Ignore errors */
@@ -1494,6 +1506,20 @@ static void handle_existing_out_dir(afl_state_t *afl) {
   if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
+  if (afl->taint_mode) {
+
+    fn = alloc_printf("%s/taint", afl->out_dir);
+    mkdir(fn, 0755);  // ignore errors
+
+    u8 *fn2 = alloc_printf("%s/taint/.input", afl->out_dir);
+    unlink(fn2);  // ignore errors
+    ck_free(fn2);
+
+    if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+    ck_free(fn);
+
+  }
+
   /* All right, let's do <afl->out_dir>/crashes/id:* and
    * <afl->out_dir>/hangs/id:*. */
 
@@ -1721,6 +1747,16 @@ void setup_dirs_fds(afl_state_t *afl) {
   if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); }
   ck_free(tmp);
 
+  /* Taint directory if taint_mode. */
+
+  if (afl->taint_mode) {
+
+    tmp = alloc_printf("%s/taint", afl->out_dir);
+    if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); }
+    ck_free(tmp);
+
+  }
+
   /* Top-level directory for queue metadata used for session
      resume and related tasks. */
 
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index 57b53c9f..6d52b2b4 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -458,27 +458,170 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
   }
 
-  /* Map the test case into memory. */
+  u32 tmp_val = 0;
 
-  fd = open(afl->queue_cur->fname, O_RDONLY);
+  if (unlikely(afl->taint_mode)) {
 
-  if (unlikely(fd < 0)) {
+    tmp_val = afl->queue_cycle % 2;  // starts with 1
+    ret_val = 0;
 
-    PFATAL("Unable to open '%s'", afl->queue_cur->fname);
+    if (unlikely(afl->queue_cur->cal_failed && !tmp_val)) goto abandon_entry;
+    if (unlikely(!afl->skip_deterministic && !afl->queue_cur->passed_det &&
+                 !tmp_val))
+      goto abandon_entry;
+    if ((!afl->queue_cur->taint_bytes_new ||
+         afl->queue_cur->taint_bytes_new == afl->queue_cur->len) &&
+        !tmp_val)
+      goto abandon_entry;
 
-  }
+    ret_val = 1;
 
-  len = afl->queue_cur->len;
+    s32 dst = 0, i;
+    temp_len = len = afl->queue_cur->len;
+    s32 j = 0;  // tmp
 
-  orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+    fd = open(afl->queue_cur->fname, O_RDONLY);
+    afl->taint_src = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+    if (fd < 0 || (ssize_t)afl->taint_src == -1)
+      FATAL("unable to open '%s'", afl->queue_cur->fname);
+    close(fd);
+    afl->taint_needs_splode = 1;
 
-  if (unlikely(orig_in == MAP_FAILED)) {
+    switch (tmp_val) {
 
-    PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len);
+      case 1:  // fuzz only tainted bytes
 
-  }
+        // special case: all or nothing tainted. in this case we act like
+        // nothing is special. this is not the taint you are looking for ...
+        if (!afl->queue_cur->taint_bytes_all ||
+            afl->queue_cur->taint_bytes_all == (u32)len) {
 
-  close(fd);
+          orig_in = in_buf = afl->taint_src;
+          afl->taint_needs_splode = 0;
+          break;
+
+        }
+
+        fd = open(afl->taint_input_file, O_RDONLY);
+        temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_all;
+        orig_in = in_buf =
+            mmap(0, len >= MAX_FILE - 65536 ? MAX_FILE : len + 65536,
+                 PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+        if (fd < 0 || (ssize_t)in_buf == -1)
+          FATAL("unable to open '%s'", afl->taint_input_file);
+        close(fd);
+
+        fd = open(afl->queue_cur->fname_taint, O_RDONLY);
+        afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE,
+                              MAP_PRIVATE, fd, 0);
+        if (fd < 0 || (ssize_t)in_buf == -1)
+          FATAL("unable to open '%s'", afl->queue_cur->fname_taint);
+        close(fd);
+
+        for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++)
+          if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i];
+
+        // FIXME DEBUG TODO XXX
+        for (i = 0; i < (s32)afl->queue_cur->len; i++) {
+
+          switch (afl->taint_map[i]) {
+
+            case 0x0:
+              break;
+            case '!':
+              j++;
+              break;
+            default:
+              FATAL(
+                  "invalid taint map entry byte 0x%02x at position %d "
+                  "(passed_det:%d)\n",
+                  afl->taint_map[i], i, afl->queue_cur->passed_det);
+
+          }
+
+        }
+
+        if (j != len)
+          FATAL("different taint values in map vs in queue (%d != %d)", j, len);
+
+        break;
+
+      case 0:  // fuzz only newly tainted bytes
+
+        fd = open(afl->taint_input_file, O_RDONLY);
+        temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_new;
+        orig_in = in_buf =
+            mmap(0, len >= MAX_FILE - 65536 ? MAX_FILE : len + 65536,
+                 PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+        if (fd < 0 || (ssize_t)in_buf == -1)
+          FATAL("unable to open '%s'", afl->taint_input_file);
+        close(fd);
+
+        u8 *fn = alloc_printf("%s.new", afl->queue_cur->fname_taint);
+        if (!fn) FATAL("OOM");
+        fd = open(fn, O_RDWR);
+        afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE,
+                              MAP_PRIVATE, fd, 0);
+        if (fd < 0 || (ssize_t)in_buf == -1)
+          FATAL("unable to open '%s' for %u bytes", fn, len);
+        close(fd);
+        ck_free(fn);
+
+        for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++)
+          if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i];
+
+        // FIXME DEBUG TODO XXX
+        for (i = 0; i < (s32)afl->queue_cur->len; i++) {
+
+          switch (afl->taint_map[i]) {
+
+            case 0x0:
+              break;
+            case '!':
+              j++;
+              break;
+            default:
+              FATAL(
+                  "invalid taint map entry byte 0x%02x at position %d "
+                  "(passed_det:%d)\n",
+                  afl->taint_map[i], i, afl->queue_cur->passed_det);
+
+          }
+
+        }
+
+        if (j != len)
+          FATAL("different taint values in map vs in queue (%d != %d)", j, len);
+
+        break;
+
+    }
+
+  } else {
+
+    /* Map the test case into memory. */
+
+    fd = open(afl->queue_cur->fname, O_RDONLY);
+
+    if (unlikely(fd < 0)) {
+
+      PFATAL("Unable to open '%s'", afl->queue_cur->fname);
+
+    }
+
+    len = afl->queue_cur->len;
+
+    orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+
+    if (unlikely(orig_in == MAP_FAILED)) {
+
+      PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len);
+
+    }
+
+    close(fd);
+
+  }
 
   /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every
      single byte anyway, so it wouldn't give us any performance or memory usage
@@ -502,8 +645,12 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
       afl->queue_cur->exec_cksum = 0;
 
-      res =
-          calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1, 0);
+      if (unlikely(afl->taint_needs_splode))
+        res = calibrate_case(afl, afl->queue_cur, afl->taint_src,
+                             afl->queue_cycle - 1, 0);
+      else
+        res = calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1,
+                             0);
 
       if (unlikely(res == FSRV_RUN_ERROR)) {
 
@@ -526,8 +673,8 @@ u8 fuzz_one_original(afl_state_t *afl) {
    * TRIMMING *
    ************/
 
-  if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done &&
-      !afl->disable_trim) {
+  if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done &&
+               !afl->disable_trim && !afl->taint_needs_splode)) {
 
     u8 res = trim_case(afl, afl->queue_cur, in_buf);
 
@@ -564,13 +711,26 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
   if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) {
 
-    if (input_to_state_stage(afl, in_buf, out_buf, len,
-                             afl->queue_cur->exec_cksum)) {
+    int res;
+    if (unlikely(afl->taint_needs_splode)) {
 
-      goto abandon_entry;
+      len = afl->queue_cur->len;
+      memcpy(out_buf, afl->taint_src, len);
+      res = input_to_state_stage(afl, afl->taint_src, out_buf, len,
+                                 afl->queue_cur->exec_cksum);
+      // just abandon as success
+      ret_val = 0;
+      res = 1;
+
+    } else {
+
+      res = input_to_state_stage(afl, in_buf, out_buf, len,
+                                 afl->queue_cur->exec_cksum);
 
     }
 
+    if (unlikely(res)) { goto abandon_entry; }
+
   }
 
   /* Skip right away if -d is given, if it has not been chosen sufficiently
@@ -2133,8 +2293,18 @@ havoc_stage:
 
             if (actually_clone) {
 
-              clone_len = choose_block_len(afl, temp_len);
-              clone_from = rand_below(afl, temp_len - clone_len + 1);
+              if (unlikely(afl->taint_needs_splode)) {
+
+                clone_len = choose_block_len(afl, afl->queue_cur->len);
+                clone_from =
+                    rand_below(afl, afl->queue_cur->len - clone_len + 1);
+
+              } else {
+
+                clone_len = choose_block_len(afl, temp_len);
+                clone_from = rand_below(afl, temp_len - clone_len + 1);
+
+              }
 
             } else {
 
@@ -2156,7 +2326,11 @@ havoc_stage:
 
             if (actually_clone) {
 
-              memcpy(new_buf + clone_to, out_buf + clone_from, clone_len);
+              if (unlikely(afl->taint_needs_splode))
+                memcpy(new_buf + clone_to, afl->taint_src + clone_from,
+                       clone_len);
+              else
+                memcpy(new_buf + clone_to, out_buf + clone_from, clone_len);
 
             } else {
 
@@ -2168,7 +2342,7 @@ havoc_stage:
             }
 
             /* Tail */
-            memcpy(new_buf + clone_to + clone_len, out_buf + clone_to,
+            memmove(new_buf + clone_to + clone_len, out_buf + clone_to,
                    temp_len - clone_to);
 
             swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
@@ -2189,16 +2363,49 @@ havoc_stage:
 
           if (temp_len < 2) { break; }
 
-          copy_len = choose_block_len(afl, temp_len - 1);
+          if (unlikely(afl->taint_needs_splode)) {
+
+            copy_len = choose_block_len(afl, afl->queue_cur->len - 1);
+            copy_from = rand_below(afl, afl->queue_cur->len - copy_len + 1);
+            copy_to = rand_below(afl, temp_len + 1);
+
+          } else {
 
-          copy_from = rand_below(afl, temp_len - copy_len + 1);
-          copy_to = rand_below(afl, temp_len - copy_len + 1);
+            copy_len = choose_block_len(afl, temp_len - 1);
+            copy_from = rand_below(afl, temp_len - copy_len + 1);
+            copy_to = rand_below(afl, temp_len - copy_len + 1);
+
+          }
 
           if (rand_below(afl, 4)) {
 
             if (copy_from != copy_to) {
 
-              memmove(out_buf + copy_to, out_buf + copy_from, copy_len);
+              if (unlikely(afl->taint_needs_splode)) {
+
+                if (temp_len >= (s32)(copy_to + copy_len)) {
+
+                  memcpy(out_buf + copy_to, afl->taint_src + copy_from,
+                         copy_len);
+
+                } else {
+
+                  u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch),
+                                              copy_to + copy_len);
+                  memcpy(new_buf, in_buf, copy_to);
+                  memcpy(new_buf + copy_to, afl->taint_src + copy_from,
+                         copy_len);
+                  swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
+                  out_buf = new_buf;
+                  temp_len = copy_to + copy_len;
+
+                }
+
+              } else {
+
+                memmove(out_buf + copy_to, out_buf + copy_from, copy_len);
+
+              }
 
             }
 
@@ -2464,10 +2671,17 @@ havoc_stage:
      splices them together at some offset, then relies on the havoc
      code to mutate that blob. */
 
+  u32 saved_len;
+
+  if (unlikely(afl->taint_needs_splode))
+    saved_len = afl->taint_len;
+  else
+    saved_len = afl->queue_cur->len;
+
 retry_splicing:
 
   if (afl->use_splicing && splice_cycle++ < SPLICE_CYCLES &&
-      afl->queued_paths > 1 && afl->queue_cur->len > 1) {
+      afl->queued_paths > 1 && saved_len > 1) {
 
     struct queue_entry *target;
     u32                 tid, split_at;
@@ -2480,7 +2694,7 @@ retry_splicing:
     if (in_buf != orig_in) {
 
       in_buf = orig_in;
-      len = afl->queue_cur->len;
+      len = saved_len;
 
     }
 
@@ -2551,6 +2765,8 @@ retry_splicing:
 
   ret_val = 0;
 
+  goto abandon_entry;
+
 /* we are through with this queue entry - for this iteration */
 abandon_entry:
 
@@ -2570,7 +2786,17 @@ abandon_entry:
 
   ++afl->queue_cur->fuzz_level;
 
-  munmap(orig_in, afl->queue_cur->len);
+  if (unlikely(afl->taint_needs_splode)) {
+
+    munmap(afl->taint_src, afl->queue_cur->len);
+    munmap(orig_in, afl->taint_len);
+    munmap(afl->taint_map, afl->queue_cur->len);
+
+  } else {
+
+    munmap(orig_in, afl->queue_cur->len);
+
+  }
 
   return ret_val;
 
diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c
index f35df914..43794018 100644
--- a/src/afl-fuzz-queue.c
+++ b/src/afl-fuzz-queue.c
@@ -103,6 +103,169 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) {
 
 }
 
+void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname,
+                       u8 *mem, u32 len) {
+
+  u8 *                ptr, *fn = fname;
+  u32                 bytes = 0, plen = len;
+  struct queue_entry *prev = q->prev;
+
+  if (plen % 4) plen = plen + 4 - (len % 4);
+
+  if ((ptr = strrchr(fname, '/')) != NULL) fn = ptr + 1;
+  q->fname_taint = alloc_printf("%s/taint/%s", afl->out_dir, fn);
+
+  if (q->fname_taint) {
+
+    u8 *save = ck_maybe_grow(BUF_PARAMS(out_scratch), afl->fsrv.map_size);
+    memcpy(save, afl->taint_fsrv.trace_bits, afl->fsrv.map_size);
+
+    afl->taint_fsrv.map_size = plen;  // speed :)
+    write_to_testcase(afl, mem, len);
+    if (afl_fsrv_run_target(&afl->taint_fsrv, afl->fsrv.exec_tmout * 4,
+                            &afl->stop_soon) == 0) {
+
+      bytes = q->taint_bytes_all =
+          count_bytes_len(afl, afl->taint_fsrv.trace_bits, plen);
+      if (afl->debug)
+        fprintf(stderr, "Debug: tainted %u out of %u bytes\n", bytes, len);
+
+      /* DEBUG FIXME TODO XXX */
+      u32 i;
+      for (i = 0; i < len; i++) {
+
+        if (afl->taint_fsrv.trace_bits[i] &&
+            afl->taint_fsrv.trace_bits[i] != '!')
+          FATAL("invalid taint map value %02x at pos %d",
+                afl->taint_fsrv.trace_bits[i], i);
+
+      }
+
+      if (len < plen)
+        for (i = len; i < plen; i++) {
+
+          if (afl->taint_fsrv.trace_bits[i])
+            FATAL("invalid taint map value %02x in padding at pos %d",
+                  afl->taint_fsrv.trace_bits[i], i);
+
+        }
+
+    }
+
+    // if all is tainted we do not need to write taint data away
+    if (bytes && bytes < len) {
+
+      // save the bytes away
+      int w = open(q->fname_taint, O_CREAT | O_WRONLY, 0644);
+      if (w >= 0) {
+
+        ck_write(w, afl->taint_fsrv.trace_bits, len, q->fname_taint);
+        close(w);
+
+        // find the highest tainted offset in the input (for trim opt)
+        s32 i = len;
+        while (i > 0 && !afl->taint_fsrv.trace_bits[i - 1])
+          i--;
+        q->taint_bytes_highest = i;
+
+        afl->taint_count++;
+
+      } else {
+
+        FATAL("could not create %s", q->fname_taint);
+        q->taint_bytes_all = bytes = 0;
+
+      }
+
+      // it is possible that there is no main taint file - if the whole file
+      // is tainted - but a .new taint file if it had new tainted bytes
+
+      // check if there is a previous queue entry and if it had taint
+      if (bytes && prev && prev->taint_bytes_all &&
+          prev->taint_bytes_all < prev->len) {
+
+        // check if there are new bytes in the taint vs the previous
+        int r = open(prev->fname_taint, O_RDONLY);
+
+        if (r >= 0) {
+
+          u8 *bufr = mmap(0, prev->len, PROT_READ, MAP_PRIVATE, r, 0);
+
+          if ((ssize_t)bufr != -1) {
+
+            u32 i;
+            u8 *tmp = ck_maybe_grow(BUF_PARAMS(in_scratch), plen);
+            memset(tmp, 0, plen);
+
+            for (i = 0; i < len; i++)
+              if (afl->taint_fsrv.trace_bits[i] && (i >= prev->len || !bufr[i]))
+                tmp[i] = '!';
+
+            q->taint_bytes_new = count_bytes_len(afl, tmp, plen);
+
+            if (afl->debug)
+              fprintf(stderr, "Debug: %u new taint out of %u bytes\n", bytes,
+                      len);
+
+            if (q->taint_bytes_new) {
+
+              u8 *fnw = alloc_printf("%s.new", q->fname_taint);
+              if (fnw) {
+
+                int w = open(fnw, O_CREAT | O_WRONLY, 0644);
+                if (w >= 0) {
+
+                  ck_write(w, tmp, plen, fnw);
+                  close(w);
+
+                } else {
+
+                  FATAL("count not create '%s'", fnw);
+                  q->taint_bytes_new = 0;
+
+                }
+
+                ck_free(fnw);
+
+              } else {
+
+                q->taint_bytes_new = 0;
+
+              }
+
+            }
+
+            munmap(bufr, prev->len);
+
+          }
+
+          close(r);
+
+        }
+
+      }
+
+    }
+
+    memcpy(afl->taint_fsrv.trace_bits, save, afl->fsrv.map_size);
+
+  }
+
+  if (!bytes) {
+
+    q->taint_bytes_highest = q->taint_bytes_all = q->taint_bytes_new = 0;
+
+    if (q->fname_taint) {
+
+      ck_free(q->fname_taint);
+      q->fname_taint = NULL;
+
+    }
+
+  }
+
+}
+
 /* check if ascii or UTF-8 */
 
 static u8 check_if_text(struct queue_entry *q) {
@@ -212,10 +375,12 @@ static u8 check_if_text(struct queue_entry *q) {
 
 /* Append new test case to the queue. */
 
-void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
+void add_to_queue(afl_state_t *afl, u8 *fname, u8 *mem, u32 len,
+                  struct queue_entry *prev_q, u8 passed_det) {
 
   struct queue_entry *q = ck_alloc(sizeof(struct queue_entry));
 
+  q->prev = prev_q;
   q->fname = fname;
   q->len = len;
   q->depth = afl->cur_depth + 1;
@@ -254,6 +419,13 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
 
   afl->last_path_time = get_cur_time();
 
+  /* trigger the tain gathering if this is not a dry run */
+  if (afl->taint_mode && mem) { perform_taint_run(afl, q, fname, mem, len); }
+
+  /* only redqueen currently uses is_ascii */
+  if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q);
+
+  /* run custom mutators afl_custom_queue_new_entry() */
   if (afl->custom_mutators_count) {
 
     LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
@@ -273,9 +445,6 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
 
   }
 
-  /* only redqueen currently uses is_ascii */
-  if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q);
-
 }
 
 /* Destroy the entire queue. */
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index d3f823c9..5381723d 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -350,7 +350,9 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
 
   }
 
-  if (q->exec_cksum) {
+  if (unlikely(afl->taint_mode))
+    q->exec_cksum = 0;
+  else if (q->exec_cksum) {
 
     memcpy(afl->first_trace, afl->fsrv.trace_bits, afl->fsrv.map_size);
     hnb = has_new_bits(afl, afl->virgin_bits);
@@ -753,56 +755,65 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
     while (remove_pos < q->len) {
 
       u32 trim_avail = MIN(remove_len, q->len - remove_pos);
-      u64 cksum;
 
-      write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail);
+      if (likely((!q->taint_bytes_highest) ||
+                 (q->len - trim_avail > q->taint_bytes_highest))) {
 
-      fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
-      ++afl->trim_execs;
+        u64 cksum;
 
-      if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; }
+        write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail);
 
-      /* Note that we don't keep track of crashes or hangs here; maybe TODO?
-       */
+        fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
+        ++afl->trim_execs;
+
+        if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; }
+
+        /* Note that we don't keep track of crashes or hangs here; maybe TODO?
+         */
+
+        cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
+
+        /* If the deletion had no impact on the trace, make it permanent. This
+           isn't perfect for variable-path inputs, but we're just making a
+           best-effort pass, so it's not a big deal if we end up with false
+           negatives every now and then. */
+
+        if (cksum == q->exec_cksum) {
 
-      cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
+          u32 move_tail = q->len - remove_pos - trim_avail;
 
-      /* If the deletion had no impact on the trace, make it permanent. This
-         isn't perfect for variable-path inputs, but we're just making a
-         best-effort pass, so it's not a big deal if we end up with false
-         negatives every now and then. */
+          q->len -= trim_avail;
+          len_p2 = next_pow2(q->len);
 
-      if (cksum == q->exec_cksum) {
+          memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail,
+                  move_tail);
 
-        u32 move_tail = q->len - remove_pos - trim_avail;
+          /* Let's save a clean trace, which will be needed by
+             update_bitmap_score once we're done with the trimming stuff. */
 
-        q->len -= trim_avail;
-        len_p2 = next_pow2(q->len);
+          if (!needs_write) {
 
-        memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail,
-                move_tail);
+            needs_write = 1;
+            memcpy(afl->clean_trace, afl->fsrv.trace_bits, afl->fsrv.map_size);
 
-        /* Let's save a clean trace, which will be needed by
-           update_bitmap_score once we're done with the trimming stuff. */
+          }
 
-        if (!needs_write) {
+        } else {
 
-          needs_write = 1;
-          memcpy(afl->clean_trace, afl->fsrv.trace_bits, afl->fsrv.map_size);
+          remove_pos += remove_len;
 
         }
 
+        /* Since this can be slow, update the screen every now and then. */
+        if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); }
+        ++afl->stage_cur;
+
       } else {
 
         remove_pos += remove_len;
 
       }
 
-      /* Since this can be slow, update the screen every now and then. */
-
-      if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); }
-      ++afl->stage_cur;
-
     }
 
     remove_len >>= 1;
@@ -855,6 +866,8 @@ abort_trimming:
 
 }
 
+#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size
+
 /* Write a modified test case, run program, process results. Handle
    error conditions, returning 1 if it's time to bail out. This is
    a helper function for fuzz_one(). */
@@ -864,6 +877,32 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
 
   u8 fault;
 
+  if (unlikely(afl->taint_needs_splode)) {
+
+    s32 new_len = afl->queue_cur->len + len - afl->taint_len;
+    if (new_len < 4)
+      new_len = 4;
+    else if (new_len > MAX_FILE)
+      new_len = MAX_FILE;
+    u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), new_len);
+
+    u32 i, taint = 0;
+    for (i = 0; i < (u32)new_len; i++) {
+
+      if (i >= afl->taint_len || i >= afl->queue_cur->len || afl->taint_map[i])
+        new_buf[i] = out_buf[taint++];
+      else
+        new_buf[i] = afl->taint_src[i];
+
+    }
+
+    swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
+
+    out_buf = new_buf;
+    len = new_len;
+
+  }
+
   write_to_testcase(afl, out_buf, len);
 
   fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
@@ -911,3 +950,5 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
 
 }
 
+#undef BUF_PARAMS
+
diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c
index d4de91a4..a8416eb1 100644
--- a/src/afl-fuzz-state.c
+++ b/src/afl-fuzz-state.c
@@ -75,7 +75,7 @@ static list_t afl_states = {.element_prealloc_count = 0};
 
 /* Initializes an afl_state_t. */
 
-void afl_state_init(afl_state_t *afl, uint32_t map_size) {
+void afl_state_init_1(afl_state_t *afl, uint32_t map_size) {
 
   /* thanks to this memset, growing vars like out_buf
   and out_size are NULL/0 by default. */
@@ -100,16 +100,6 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) {
   afl->cpu_aff = -1;                    /* Selected CPU core                */
 #endif                                                     /* HAVE_AFFINITY */
 
-  afl->virgin_bits = ck_alloc(map_size);
-  afl->virgin_tmout = ck_alloc(map_size);
-  afl->virgin_crash = ck_alloc(map_size);
-  afl->var_bytes = ck_alloc(map_size);
-  afl->top_rated = ck_alloc(map_size * sizeof(void *));
-  afl->clean_trace = ck_alloc(map_size);
-  afl->clean_trace_custom = ck_alloc(map_size);
-  afl->first_trace = ck_alloc(map_size);
-  afl->map_tmp_buf = ck_alloc(map_size);
-
   afl->fsrv.use_stdin = 1;
   afl->fsrv.map_size = map_size;
   // afl_state_t is not available in forkserver.c
@@ -161,6 +151,24 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) {
 
 }
 
+void afl_state_init_2(afl_state_t *afl, uint32_t map_size) {
+
+  afl->shm.map_size = map_size ? map_size : MAP_SIZE;
+
+  afl->virgin_bits = ck_alloc(map_size);
+  afl->virgin_tmout = ck_alloc(map_size);
+  afl->virgin_crash = ck_alloc(map_size);
+  afl->var_bytes = ck_alloc(map_size);
+  afl->top_rated = ck_alloc(map_size * sizeof(void *));
+  afl->clean_trace = ck_alloc(map_size);
+  afl->clean_trace_custom = ck_alloc(map_size);
+  afl->first_trace = ck_alloc(map_size);
+  afl->map_tmp_buf = ck_alloc(map_size);
+
+  afl->fsrv.map_size = map_size;
+
+}
+
 /*This sets up the environment variables for afl-fuzz into the afl_state
  * struct*/
 
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c
index aeb290bd..0cc06e12 100644
--- a/src/afl-fuzz-stats.c
+++ b/src/afl-fuzz-stats.c
@@ -116,6 +116,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability,
           "edges_found       : %u\n"
           "var_byte_count    : %u\n"
           "havoc_expansion   : %u\n"
+          "tainted_inputs    : %u\n"
           "afl_banner        : %s\n"
           "afl_version       : " VERSION
           "\n"
@@ -149,8 +150,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability,
 #else
           -1,
 #endif
-          t_bytes, afl->var_byte_count, afl->expand_havoc, afl->use_banner,
-          afl->unicorn_mode ? "unicorn" : "",
+          t_bytes, afl->var_byte_count, afl->expand_havoc, afl->taint_count,
+          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 " : "",
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 5dd092f2..106aa550 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -53,6 +53,9 @@ static void at_exit() {
   ptr = getenv("__AFL_TARGET_PID2");
   if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL);
 
+  ptr = getenv("__AFL_TARGET_PID3");
+  if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL);
+
   i = 0;
   while (list[i] != NULL) {
 
@@ -89,6 +92,8 @@ static void usage(u8 *argv0, int more_help) {
       "  -o dir        - output directory for fuzzer findings\n\n"
 
       "Execution control settings:\n"
+      "  -A            - use first level taint analysis (see "
+      "qemu_taint/README.md)\n"
       "  -p schedule   - power schedules compute a seed's performance score. "
       "<explore\n"
       "                  (default), fast, coe, lin, quad, exploit, mmopt, "
@@ -239,9 +244,10 @@ static int stricmp(char const *a, char const *b) {
 
 int main(int argc, char **argv_orig, char **envp) {
 
-  s32    opt;
-  u64    prev_queued = 0;
-  u32    sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE;
+  s32 opt;
+  u64 prev_queued = 0;
+  u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE,
+      real_map_size = 0;
   u8 *   extras_dir = 0;
   u8     mem_limit_given = 0, exit_1 = 0, debug = 0;
   char **use_argv;
@@ -257,7 +263,7 @@ int main(int argc, char **argv_orig, char **envp) {
   if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; }
 
   map_size = get_map_size();
-  afl_state_init(afl, map_size);
+  afl_state_init_1(afl, map_size);
   afl->debug = debug;
   afl_fsrv_init(&afl->fsrv);
 
@@ -277,10 +283,15 @@ int main(int argc, char **argv_orig, char **envp) {
 
   while ((opt = getopt(
               argc, argv,
-              "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > 0) {
+              "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QANUWe:p:s:V:E:L:hRP:")) > 0) {
 
     switch (opt) {
 
+      case 'A':
+        afl->taint_mode = 1;
+        if (!mem_limit_given) { afl->fsrv.mem_limit = MEM_LIMIT_QEMU; }
+        break;
+
       case 'I':
         afl->infoexec = optarg;
         break;
@@ -488,7 +499,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
         if (!optarg) { FATAL("Wrong usage of -m"); }
 
-        if (!strcmp(optarg, "none")) {
+        if (!strcmp(optarg, "none") || !strcmp(optarg, "0")) {
 
           afl->fsrv.mem_limit = 0;
           break;
@@ -818,6 +829,15 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  if (afl->taint_mode && afl->fsrv.map_size < MAX_FILE) {
+
+    real_map_size = map_size;
+    map_size = MAX_FILE;
+
+  }
+
+  afl_state_init_2(afl, map_size);
+
   if (!mem_limit_given && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260;
 
   OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" "
@@ -825,8 +845,7 @@ int main(int argc, char **argv_orig, char **envp) {
   OKF("afl++ is open source, get it at "
       "https://github.com/AFLplusplus/AFLplusplus");
   OKF("Power schedules from github.com/mboehme/aflfast");
-  OKF("Python Mutator and llvm_mode instrument file list from "
-      "github.com/choller/afl");
+  OKF("Python Mutator from github.com/choller/afl");
   OKF("MOpt Mutator from github.com/puppet-meteor/MOpt-AFL");
 
   if (afl->sync_id && afl->is_main_node &&
@@ -872,6 +891,19 @@ int main(int argc, char **argv_orig, char **envp) {
     if (afl->crash_mode) { FATAL("-C and -n are mutually exclusive"); }
     if (afl->fsrv.qemu_mode) { FATAL("-Q and -n are mutually exclusive"); }
     if (afl->unicorn_mode) { FATAL("-U and -n are mutually exclusive"); }
+    if (afl->taint_mode) { FATAL("-A and -n are mutually exclusive"); }
+
+  }
+
+  if (afl->limit_time_sig != 0 && afl->taint_mode) {
+
+    FATAL("-A and -L are mutually exclusive");
+
+  }
+
+  if (afl->unicorn_mode != 0 && afl->taint_mode) {
+
+    FATAL("-A and -U are mutually exclusive");
 
   }
 
@@ -972,7 +1004,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (afl->afl_env.afl_preload) {
 
-    if (afl->fsrv.qemu_mode) {
+    if (afl->fsrv.qemu_mode || afl->taint_mode) {
 
       u8 *qemu_preload = getenv("QEMU_SET_ENV");
       u8 *afl_preload = getenv("AFL_PRELOAD");
@@ -1068,6 +1100,13 @@ int main(int argc, char **argv_orig, char **envp) {
   afl->fsrv.trace_bits =
       afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode);
 
+  if (real_map_size && map_size != real_map_size) {
+
+    afl->fsrv.map_size = real_map_size;
+    if (afl->cmplog_binary) afl->cmplog_fsrv.map_size = real_map_size;
+
+  }
+
   if (!afl->in_bitmap) { memset(afl->virgin_bits, 255, afl->fsrv.map_size); }
   memset(afl->virgin_tmout, 255, afl->fsrv.map_size);
   memset(afl->virgin_crash, 255, afl->fsrv.map_size);
@@ -1223,7 +1262,6 @@ int main(int argc, char **argv_orig, char **envp) {
 
     ACTF("Spawning cmplog forkserver");
     afl_fsrv_init_dup(&afl->cmplog_fsrv, &afl->fsrv);
-    // TODO: this is semi-nice
     afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits;
     afl->cmplog_fsrv.qemu_mode = afl->fsrv.qemu_mode;
     afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary;
@@ -1234,6 +1272,70 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  if (afl->taint_mode) {
+
+    ACTF("Spawning qemu_taint forkserver");
+
+    u8 *disable = getenv("AFL_DISABLE_LLVM_INSTRUMENTATION");
+    setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0);
+
+    afl_fsrv_init_dup(&afl->taint_fsrv, &afl->fsrv);
+    afl->taint_fsrv.taint_mode = 1;
+    afl->taint_fsrv.trace_bits = afl->fsrv.trace_bits;
+
+    ck_free(afl->taint_fsrv.target_path);
+    afl->argv_taint = ck_alloc(sizeof(char *) * (argc + 4 - optind));
+    afl->taint_fsrv.target_path = find_afl_binary("afl-qemu-taint", argv[0]);
+    afl->argv_taint[0] = find_afl_binary("afl-qemu-taint", argv[0]);
+    if (!afl->argv_taint[0])
+      FATAL(
+          "Cannot find 'afl-qemu-taint', read qemu_taint/README.md on how to "
+          "build it.");
+    u32 idx = optind - 1, offset = 0;
+    do {
+
+      idx++;
+      offset++;
+      afl->argv_taint[offset] = argv[idx];
+
+    } while (argv[idx] != NULL);
+
+    if (afl->fsrv.use_stdin)
+      unsetenv("AFL_TAINT_INPUT");
+    else
+      setenv("AFL_TAINT_INPUT", afl->fsrv.out_file, 1);
+    afl_fsrv_start(&afl->taint_fsrv, afl->argv_taint, &afl->stop_soon,
+                   afl->afl_env.afl_debug_child_output);
+
+    afl->taint_input_file = alloc_printf("%s/taint/.input", afl->out_dir);
+    int fd = open(afl->taint_input_file, O_CREAT | O_TRUNC | O_RDWR, 0644);
+    if (fd < 0)
+      FATAL("Cannot create taint inpu file '%s'", afl->taint_input_file);
+    lseek(fd, MAX_FILE, SEEK_SET);
+    ck_write(fd, "\0", 1, afl->taint_input_file);
+
+    if (!disable) unsetenv("AFL_DISABLE_LLVM_INSTRUMENTATION");
+
+    OKF("Taint forkserver successfully started");
+
+    const rlim_t  kStackSize = 128L * 1024L * 1024L;  // min stack size = 128 Mb
+    struct rlimit rl;
+    rl.rlim_cur = kStackSize;
+    if (getrlimit(RLIMIT_STACK, &rl) != 0)
+      WARNF("Setting a higher stack size failed!");
+
+  #define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size
+    u8 *tmp1 = ck_maybe_grow(BUF_PARAMS(eff), MAX_FILE + 4096);
+    u8 *tmp2 = ck_maybe_grow(BUF_PARAMS(ex), MAX_FILE + 4096);
+    u8 *tmp3 = ck_maybe_grow(BUF_PARAMS(in_scratch), MAX_FILE + 4096);
+    u8 *tmp4 = ck_maybe_grow(BUF_PARAMS(out), MAX_FILE + 4096);
+    u8 *tmp5 = ck_maybe_grow(BUF_PARAMS(out_scratch), MAX_FILE + 4096);
+  #undef BUF_PARAMS
+    if (!tmp1 || !tmp2 || !tmp3 || !tmp4 || !tmp5)
+      FATAL("memory issues. me hungry, feed me!");
+
+  }
+
   perform_dry_run(afl);
 
   cull_queue(afl);
@@ -1308,7 +1410,7 @@ int main(int argc, char **argv_orig, char **envp) {
               break;
             case 1:
               if (afl->limit_time_sig == 0 && !afl->custom_only &&
-                  !afl->python_only) {
+                  !afl->python_only && !afl->taint_mode) {
 
                 afl->limit_time_sig = -1;
                 afl->limit_time_puppet = 0;
@@ -1496,8 +1598,11 @@ stop_fuzzing:
 
   }
 
+  if (afl->cmplog_binary) afl_fsrv_deinit(&afl->cmplog_fsrv);
+  if (afl->taint_mode) afl_fsrv_deinit(&afl->taint_fsrv);
   afl_fsrv_deinit(&afl->fsrv);
   if (afl->orig_cmdline) { ck_free(afl->orig_cmdline); }
+  if (afl->argv_taint) { ck_free(afl->argv_taint); }
   ck_free(afl->fsrv.target_path);
   ck_free(afl->fsrv.out_file);
   ck_free(afl->sync_id);