about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/afl-analyze.c4
-rw-r--r--src/afl-as.c4
-rw-r--r--src/afl-cc.c3712
-rw-r--r--src/afl-common.c117
-rw-r--r--src/afl-forkserver.c601
-rw-r--r--src/afl-fuzz-bitmap.c135
-rw-r--r--src/afl-fuzz-cmplog.c4
-rw-r--r--src/afl-fuzz-extras.c16
-rw-r--r--src/afl-fuzz-init.c321
-rw-r--r--src/afl-fuzz-mutators.c16
-rw-r--r--src/afl-fuzz-one.c1230
-rw-r--r--src/afl-fuzz-python.c34
-rw-r--r--src/afl-fuzz-queue.c89
-rw-r--r--src/afl-fuzz-redqueen.c697
-rw-r--r--src/afl-fuzz-run.c174
-rw-r--r--src/afl-fuzz-skipdet.c403
-rw-r--r--src/afl-fuzz-state.c60
-rw-r--r--src/afl-fuzz-stats.c354
-rw-r--r--src/afl-fuzz-statsd.c2
-rw-r--r--src/afl-fuzz.c444
-rw-r--r--src/afl-gotcpu.c4
-rw-r--r--src/afl-ld-lto.c16
-rw-r--r--src/afl-performance.c345
-rw-r--r--src/afl-sharedmem.c4
-rw-r--r--src/afl-showmap.c34
-rw-r--r--src/afl-tmin.c38
-rw-r--r--src/hashmap.c149
27 files changed, 6433 insertions, 2574 deletions
diff --git a/src/afl-analyze.c b/src/afl-analyze.c
index 5b122741..d089cd08 100644
--- a/src/afl-analyze.c
+++ b/src/afl-analyze.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/src/afl-as.c b/src/afl-as.c
index 772e31b3..d4ddb94d 100644
--- a/src/afl-as.c
+++ b/src/afl-as.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/src/afl-cc.c b/src/afl-cc.c
index 972ac8cd..c872b2eb 100644
--- a/src/afl-cc.c
+++ b/src/afl-cc.c
@@ -5,7 +5,7 @@
    Written by Michal Zalewski, Laszlo Szekeres and Marc Heuse
 
    Copyright 2015, 2016 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -17,6 +17,10 @@
 
 #define AFL_MAIN
 
+#ifndef _GNU_SOURCE
+  #define _GNU_SOURCE 1
+#endif
+
 #include "common.h"
 #include "config.h"
 #include "types.h"
@@ -32,7 +36,9 @@
 #include <limits.h>
 #include <assert.h>
 #include <ctype.h>
+#include <fcntl.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 
 #if (LLVM_MAJOR - 0 == 0)
   #undef LLVM_MAJOR
@@ -47,23 +53,22 @@
   #define LLVM_MINOR 0
 #endif
 
-static u8  *obj_path;                  /* Path to runtime libraries         */
-static u8 **cc_params;                 /* Parameters passed to the real CC  */
-static u32  cc_par_cnt = 1;            /* Param count, including argv0      */
-static u8   clang_mode;                /* Invoked as afl-clang*?            */
-static u8   llvm_fullpath[PATH_MAX];
-static u8   instrument_mode, instrument_opt_mode, ngram_size, ctx_k, lto_mode;
-static u8   compiler_mode, plusplus_mode, have_instr_env = 0, need_aflpplib = 0;
-static u8   have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0;
-static u8  *lto_flag = AFL_CLANG_FLTO, *argvnull;
-static u8   debug;
-static u8   cwd[4096];
-static u8   cmplog_mode;
-u8          use_stdin;                                             /* dummy */
-static int  passthrough;
-// static u8 *march_opt = CFLAGS_OPT;
-
-enum {
+#ifndef MAX_PARAMS_NUM
+  #define MAX_PARAMS_NUM 2048
+#endif
+
+/** Global declarations -----BEGIN----- **/
+
+typedef enum {
+
+  PARAM_MISS,  // not matched
+  PARAM_SCAN,  // scan only
+  PARAM_KEEP,  // kept as-is
+  PARAM_DROP,  // ignored
+
+} param_st;
+
+typedef enum {
 
   INSTRUMENT_DEFAULT = 0,
   INSTRUMENT_CLASSIC = 1,
@@ -80,7 +85,20 @@ enum {
   INSTRUMENT_OPT_CTX_K = 64,
   INSTRUMENT_OPT_CODECOV = 128,
 
-};
+} instrument_mode_id;
+
+typedef enum {
+
+  UNSET = 0,
+  LTO = 1,
+  LLVM = 2,
+  GCC_PLUGIN = 3,
+  GCC = 4,
+  CLANG = 5
+
+} compiler_mode_id;
+
+static u8 cwd[4096];
 
 char instrument_mode_string[18][18] = {
 
@@ -105,17 +123,6 @@ char instrument_mode_string[18][18] = {
 
 };
 
-enum {
-
-  UNSET = 0,
-  LTO = 1,
-  LLVM = 2,
-  GCC_PLUGIN = 3,
-  GCC = 4,
-  CLANG = 5
-
-};
-
 char compiler_mode_string[7][12] = {
 
     "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN",
@@ -123,6 +130,18 @@ char compiler_mode_string[7][12] = {
 
 };
 
+u8 *instrument_mode_2str(instrument_mode_id i) {
+
+  return instrument_mode_string[i];
+
+}
+
+u8 *compiler_mode_2str(compiler_mode_id i) {
+
+  return compiler_mode_string[i];
+
+}
+
 u8 *getthecwd() {
 
   if (getcwd(cwd, sizeof(cwd)) == NULL) {
@@ -136,26 +155,237 @@ u8 *getthecwd() {
 
 }
 
-/* Try to find a specific runtime we need, returns NULL on fail. */
+typedef struct aflcc_state {
+
+  u8 **cc_params;                      /* Parameters passed to the real CC  */
+  u32  cc_par_cnt;                     /* Param count, including argv0      */
+
+  u8 *argv0;                           /* Original argv0 (by strdup)        */
+  u8 *callname;                        /* Executable file argv0 indicated   */
+
+  u8 debug;
+
+  u8 compiler_mode, plusplus_mode, lto_mode;
+
+  u8 *lto_flag;
+
+  u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k;
+
+  u8 cmplog_mode;
+
+  u8 have_instr_env, have_gcc, have_clang, have_llvm, have_gcc_plugin, have_lto,
+      have_optimized_pcguard, have_instr_list;
+
+  u8 fortify_set, x_set, bit_mode, preprocessor_only, have_unroll, have_o,
+      have_pic, have_c, shared_linking, partial_linking, non_dash, have_fp,
+      have_flto, have_hidden, have_fortify, have_fcf, have_staticasan,
+      have_rust_asanrt, have_asan, have_msan, have_ubsan, have_lsan, have_tsan,
+      have_cfisan;
+
+  // u8 *march_opt;
+  u8  need_aflpplib;
+  int passthrough;
+
+  u8  use_stdin;                                                   /* dummy */
+  u8 *argvnull;                                                    /* dummy */
+
+} aflcc_state_t;
+
+void aflcc_state_init(aflcc_state_t *, u8 *argv0);
+
+u8 *find_object(aflcc_state_t *, u8 *obj);
+
+void find_built_deps(aflcc_state_t *);
+
+/* Insert param into the new argv, raise error if MAX_PARAMS_NUM exceeded. */
+static inline void insert_param(aflcc_state_t *aflcc, u8 *param) {
+
+  if (unlikely(aflcc->cc_par_cnt + 1 >= MAX_PARAMS_NUM))
+    FATAL("Too many command line parameters, please increase MAX_PARAMS_NUM.");
+
+  aflcc->cc_params[aflcc->cc_par_cnt++] = param;
+
+}
 
 /*
-  in find_object() we look here:
+  Insert a param which contains path to the object file. It uses find_object to
+  get the path based on the name `obj`, and then uses a sprintf like method to
+  format it with `fmt`. If `fmt` is NULL, the inserted arg is same as the path.
+  If `msg` provided, it should be an error msg raised if the path can't be
+  found. `obj` must not be NULL.
+*/
+static inline void insert_object(aflcc_state_t *aflcc, u8 *obj, u8 *fmt,
+                                 u8 *msg) {
+
+  u8 *_obj_path = find_object(aflcc, obj);
+  if (!_obj_path) {
+
+    if (msg)
+      FATAL("%s", msg);
+    else
+      FATAL("Unable to find '%s'", obj);
+
+  } else {
+
+    if (fmt) {
+
+      u8 *_obj_path_fmt = alloc_printf(fmt, _obj_path);
+      ck_free(_obj_path);
+      aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path_fmt;
+
+    } else {
+
+      aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path;
+
+    }
+
+  }
+
+}
+
+/* Insert params into the new argv, make clang load the pass. */
+static inline void load_llvm_pass(aflcc_state_t *aflcc, u8 *pass) {
+
+#if LLVM_MAJOR >= 11                                /* use new pass manager */
+  #if LLVM_MAJOR < 16
+  insert_param(aflcc, "-fexperimental-new-pass-manager");
+  #endif
+  insert_object(aflcc, pass, "-fpass-plugin=%s", 0);
+#else
+  insert_param(aflcc, "-Xclang");
+  insert_param(aflcc, "-load");
+  insert_param(aflcc, "-Xclang");
+  insert_object(aflcc, pass, 0, 0);
+#endif
+
+}
+
+static inline void debugf_args(int argc, char **argv) {
+
+  DEBUGF("cd '%s';", getthecwd());
+  for (int i = 0; i < argc; i++)
+    SAYF(" '%s'", argv[i]);
+  SAYF("\n");
+  fflush(stdout);
+  fflush(stderr);
+
+}
 
-  1. if obj_path is already set we look there first
-  2. then we check the $AFL_PATH environment variable location if set
-  3. next we check argv[0] if it has path information and use it
+void compiler_mode_by_callname(aflcc_state_t *);
+void compiler_mode_by_environ(aflcc_state_t *);
+void compiler_mode_by_cmdline(aflcc_state_t *, int argc, char **argv);
+void instrument_mode_by_environ(aflcc_state_t *);
+void mode_final_checkout(aflcc_state_t *, int argc, char **argv);
+void mode_notification(aflcc_state_t *);
+
+void add_real_argv0(aflcc_state_t *);
+
+void add_defs_common(aflcc_state_t *);
+void add_defs_selective_instr(aflcc_state_t *);
+void add_defs_persistent_mode(aflcc_state_t *);
+void add_defs_fortify(aflcc_state_t *, u8);
+void add_defs_lsan_ctrl(aflcc_state_t *);
+
+param_st parse_fsanitize(aflcc_state_t *, u8 *, u8);
+void     add_sanitizers(aflcc_state_t *, char **envp);
+void     add_optimized_pcguard(aflcc_state_t *);
+void     add_native_pcguard(aflcc_state_t *);
+
+void add_assembler(aflcc_state_t *);
+void add_gcc_plugin(aflcc_state_t *);
+
+param_st parse_misc_params(aflcc_state_t *, u8 *, u8);
+void     add_misc_params(aflcc_state_t *);
+
+param_st parse_linking_params(aflcc_state_t *, u8 *, u8, u8 *skip_next,
+                              char **argv);
+
+void add_lto_linker(aflcc_state_t *);
+void add_lto_passes(aflcc_state_t *);
+void add_runtime(aflcc_state_t *);
+
+/** Global declarations -----END----- **/
+
+/*
+  Init global state struct. We also extract the callname,
+  check debug options and if in C++ mode here.
+*/
+void aflcc_state_init(aflcc_state_t *aflcc, u8 *argv0) {
+
+  // Default NULL/0 is a good start
+  memset(aflcc, 0, sizeof(aflcc_state_t));
+
+  aflcc->cc_params = ck_alloc(MAX_PARAMS_NUM * sizeof(u8 *));
+  aflcc->cc_par_cnt = 1;
+
+  aflcc->lto_flag = AFL_CLANG_FLTO;
+
+  // aflcc->march_opt = CFLAGS_OPT;
+
+  /* callname & if C++ mode */
+
+  aflcc->argv0 = ck_strdup(argv0);
+
+  char *cname = NULL;
+
+  if ((cname = strrchr(aflcc->argv0, '/')) != NULL) {
+
+    cname++;
+
+  } else {
+
+    cname = aflcc->argv0;
+
+  }
+
+  aflcc->callname = cname;
+
+  if (strlen(cname) > 2 && (strncmp(cname + strlen(cname) - 2, "++", 2) == 0 ||
+                            strstr(cname, "-g++") != NULL)) {
+
+    aflcc->plusplus_mode = 1;
+
+  }
+
+  /* debug */
+
+  if (getenv("AFL_DEBUG")) {
+
+    aflcc->debug = 1;
+    if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG");
+
+  } else if (getenv("AFL_QUIET")) {
+
+    be_quiet = 1;
+
+  }
+
+  if ((getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) && (!aflcc->debug)) {
+
+    be_quiet = 1;
+
+  }
+
+}
+
+/*
+  Try to find a specific runtime we need, in here:
+
+  1. firstly we check the $AFL_PATH environment variable location if set
+  2. next we check argv[0] if it has path information and use it
     a) we also check ../lib/afl
-  4. if 3. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and
+  3. if 2. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and
      FreeBSD with procfs)
     a) and check here in ../lib/afl too
-  5. we look into the AFL_PATH define (usually /usr/local/lib/afl)
-  6. we finally try the current directory
+  4. we look into the AFL_PATH define (usually /usr/local/lib/afl)
+  5. we finally try the current directory
 
   if all these attempts fail - we return NULL and the caller has to decide
-  what to do.
+  what to do. Otherwise the path to obj would be allocated and returned.
 */
+u8 *find_object(aflcc_state_t *aflcc, u8 *obj) {
 
-static u8 *find_object(u8 *obj, u8 *argv0) {
+  u8 *argv0 = aflcc->argv0;
 
   u8 *afl_path = getenv("AFL_PATH");
   u8 *slash = NULL, *tmp;
@@ -164,14 +394,9 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
 
     tmp = alloc_printf("%s/%s", afl_path, obj);
 
-    if (debug) DEBUGF("Trying %s\n", tmp);
-
-    if (!access(tmp, R_OK)) {
+    if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
-      obj_path = afl_path;
-      return tmp;
-
-    }
+    if (!access(tmp, R_OK)) { return tmp; }
 
     ck_free(tmp);
 
@@ -190,11 +415,11 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
 
       tmp = alloc_printf("%s/%s", dir, obj);
 
-      if (debug) DEBUGF("Trying %s\n", tmp);
+      if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
       if (!access(tmp, R_OK)) {
 
-        obj_path = dir;
+        ck_free(dir);
         return tmp;
 
       }
@@ -202,12 +427,10 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
       ck_free(tmp);
       tmp = alloc_printf("%s/../lib/afl/%s", dir, obj);
 
-      if (debug) DEBUGF("Trying %s\n", tmp);
+      if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
       if (!access(tmp, R_OK)) {
 
-        u8 *dir2 = alloc_printf("%s/../lib/afl", dir);
-        obj_path = dir2;
         ck_free(dir);
         return tmp;
 
@@ -247,26 +470,18 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
             *slash = 0;
             tmp = alloc_printf("%s/%s", exepath, obj);
 
-            if (!access(tmp, R_OK)) {
+            if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
-              u8 *dir = alloc_printf("%s", exepath);
-              obj_path = dir;
-              return tmp;
-
-            }
+            if (!access(tmp, R_OK)) { return tmp; }
 
             ck_free(tmp);
             tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj);
 
-            if (debug) DEBUGF("Trying %s\n", tmp);
-
-            if (!access(tmp, R_OK)) {
+            if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
-              u8 *dir = alloc_printf("%s/../lib/afl/", exepath);
-              obj_path = dir;
-              return tmp;
+            if (!access(tmp, R_OK)) { return tmp; }
 
-            }
+            ck_free(tmp);
 
           }
 
@@ -283,432 +498,962 @@ static u8 *find_object(u8 *obj, u8 *argv0) {
 
   tmp = alloc_printf("%s/%s", AFL_PATH, obj);
 
-  if (debug) DEBUGF("Trying %s\n", tmp);
+  if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
 
-  if (!access(tmp, R_OK)) {
+  if (!access(tmp, R_OK)) { return tmp; }
 
-    obj_path = AFL_PATH;
-    return tmp;
+  ck_free(tmp);
+  tmp = alloc_printf("./%s", obj);
 
-  }
+  if (aflcc->debug) DEBUGF("Trying %s\n", tmp);
+
+  if (!access(tmp, R_OK)) { return tmp; }
 
   ck_free(tmp);
 
-  tmp = alloc_printf("./%s", obj);
+  if (aflcc->debug) DEBUGF("Trying ... giving up\n");
 
-  if (debug) DEBUGF("Trying %s\n", tmp);
+  return NULL;
 
-  if (!access(tmp, R_OK)) {
+}
+
+/*
+  Deduce some info about compiler toolchains in current system,
+  from the building results of AFL++
+*/
+void find_built_deps(aflcc_state_t *aflcc) {
+
+  char *ptr = NULL;
 
-    obj_path = ".";
-    return tmp;
+#if defined(__x86_64__) || defined(__i386__)
+  if ((ptr = find_object(aflcc, "afl-as")) != NULL) {
+
+  #ifndef __APPLE__
+    // on OSX clang masquerades as GCC
+    aflcc->have_gcc = 1;
+  #endif
+    aflcc->have_clang = 1;
+    ck_free(ptr);
 
   }
 
-  ck_free(tmp);
+#endif
 
-  if (debug) DEBUGF("Trying ... giving up\n");
+  if ((ptr = find_object(aflcc, "SanitizerCoveragePCGUARD.so")) != NULL) {
 
-  return NULL;
+    aflcc->have_optimized_pcguard = 1;
+    ck_free(ptr);
+
+  }
+
+#if (LLVM_MAJOR >= 3)
+
+  if ((ptr = find_object(aflcc, "SanitizerCoverageLTO.so")) != NULL) {
+
+    aflcc->have_lto = 1;
+    ck_free(ptr);
+
+  }
+
+  if ((ptr = find_object(aflcc, "cmplog-routines-pass.so")) != NULL) {
+
+    aflcc->have_llvm = 1;
+    ck_free(ptr);
+
+  }
+
+#endif
+
+#ifdef __ANDROID__
+  aflcc->have_llvm = 1;
+#endif
+
+  if ((ptr = find_object(aflcc, "afl-gcc-pass.so")) != NULL) {
+
+    aflcc->have_gcc_plugin = 1;
+    ck_free(ptr);
+
+  }
+
+#if !defined(__ANDROID__) && !defined(ANDROID)
+  ptr = find_object(aflcc, "afl-compiler-rt.o");
+
+  if (!ptr) {
+
+    FATAL(
+        "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH "
+        "environment variable.");
+
+  }
+
+  if (aflcc->debug) { DEBUGF("rt=%s\n", ptr); }
+
+  ck_free(ptr);
+#endif
 
 }
 
-void parse_fsanitize(char *string) {
+/** compiler_mode & instrument_mode selecting -----BEGIN----- **/
 
-  char *p, *ptr = string + strlen("-fsanitize=");
-  char *new = malloc(strlen(string) + 1);
-  char *tmp = malloc(strlen(ptr));
-  u32   count = 0, len, ende = 0;
+/* Select compiler_mode by callname, such as "afl-clang-fast", etc. */
+void compiler_mode_by_callname(aflcc_state_t *aflcc) {
 
-  if (!new || !tmp) { FATAL("could not acquire memory"); }
-  strcpy(new, "-fsanitize=");
+  if (strncmp(aflcc->callname, "afl-clang-fast", 14) == 0) {
 
-  do {
+    /* afl-clang-fast is always created there by makefile
+      just like afl-clang, burdened with special purposes:
+      - If llvm-config is not available (i.e. LLVM_MAJOR is 0),
+        or too old, it falls back to LLVM-NATIVE mode and let
+        the actual compiler complain if doesn't work.
+      - Otherwise try default llvm instruments except LTO.
+    */
+#if (LLVM_MAJOR >= 3)
+    aflcc->compiler_mode = LLVM;
+#else
+    aflcc->compiler_mode = CLANG;
+#endif
 
-    p = strchr(ptr, ',');
-    if (!p) {
+  } else
 
-      p = ptr + strlen(ptr) + 1;
-      ende = 1;
+#if (LLVM_MAJOR >= 3)
+
+      if (strncmp(aflcc->callname, "afl-clang-lto", 13) == 0 ||
+
+          strncmp(aflcc->callname, "afl-lto", 7) == 0) {
+
+    aflcc->compiler_mode = LTO;
+
+  } else
+
+#endif
+
+      if (strncmp(aflcc->callname, "afl-gcc-fast", 12) == 0 ||
+
+          strncmp(aflcc->callname, "afl-g++-fast", 12) == 0) {
+
+    aflcc->compiler_mode = GCC_PLUGIN;
+
+  } else if (strncmp(aflcc->callname, "afl-gcc", 7) == 0 ||
+
+             strncmp(aflcc->callname, "afl-g++", 7) == 0) {
+
+    aflcc->compiler_mode = GCC;
+
+  } else if (strcmp(aflcc->callname, "afl-clang") == 0 ||
+
+             strcmp(aflcc->callname, "afl-clang++") == 0) {
+
+    aflcc->compiler_mode = CLANG;
+
+  }
+
+}
+
+/*
+  Select compiler_mode by env AFL_CC_COMPILER. And passthrough mode can be
+  regarded as a special compiler_mode, so we check for it here, too.
+*/
+void compiler_mode_by_environ(aflcc_state_t *aflcc) {
+
+  if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) {
+
+    aflcc->passthrough = 1;
+
+  }
+
+  char *ptr = getenv("AFL_CC_COMPILER");
+
+  if (!ptr) { return; }
+
+  if (aflcc->compiler_mode) {
+
+    if (!be_quiet) {
+
+      WARNF(
+          "\"AFL_CC_COMPILER\" is set but a specific compiler was already "
+          "selected by command line parameter or symlink, ignoring the "
+          "environment variable!");
 
     }
 
-    len = p - ptr;
-    if (len) {
+  } else {
 
-      strncpy(tmp, ptr, len);
-      tmp[len] = 0;
-      // fprintf(stderr, "Found: %s\n", tmp);
-      ptr += len + 1;
-      if (*tmp) {
+    if (strncasecmp(ptr, "LTO", 3) == 0) {
 
-        u32 copy = 1;
-        if (!strcmp(tmp, "fuzzer")) {
+      aflcc->compiler_mode = LTO;
 
-          need_aflpplib = 1;
-          copy = 0;
+    } else if (strncasecmp(ptr, "LLVM", 4) == 0) {
 
-        } else if (!strncmp(tmp, "fuzzer", 6)) {
+      aflcc->compiler_mode = LLVM;
 
-          copy = 0;
+    } else if (strncasecmp(ptr, "GCC_P", 5) == 0 ||
 
-        }
+               strncasecmp(ptr, "GCC-P", 5) == 0 ||
+               strncasecmp(ptr, "GCCP", 4) == 0) {
 
-        if (copy) {
+      aflcc->compiler_mode = GCC_PLUGIN;
 
-          if (count) { strcat(new, ","); }
-          strcat(new, tmp);
-          ++count;
+    } else if (strcasecmp(ptr, "GCC") == 0) {
 
-        }
+      aflcc->compiler_mode = GCC;
+
+    } else if (strcasecmp(ptr, "CLANG") == 0) {
+
+      aflcc->compiler_mode = CLANG;
+
+    } else
+
+      FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr);
+
+  }
+
+}
+
+/*
+  Select compiler_mode by command line options --afl-...
+  If it can be inferred, instrument_mode would also be set.
+  This can supersedes previous result based on callname
+  or AFL_CC_COMPILER. And "--afl_noopt"/"--afl-noopt" will
+  be overwritten by "-g".
+*/
+void compiler_mode_by_cmdline(aflcc_state_t *aflcc, int argc, char **argv) {
+
+  char *ptr = NULL;
+
+  for (int i = 1; i < argc; i++) {
+
+    if (strncmp(argv[i], "--afl", 5) == 0) {
+
+      if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) {
+
+        aflcc->passthrough = 1;
+        argv[i] = "-g";  // we have to overwrite it, -g is always good
+        continue;
 
       }
 
-    } else {
+      if (aflcc->compiler_mode && !be_quiet) {
 
-      ptr++;                                    /*fprintf(stderr, "NO!\n"); */
+        WARNF(
+            "--afl-... compiler mode supersedes the AFL_CC_COMPILER and "
+            "symlink compiler selection!");
 
-    }
+      }
 
-  } while (!ende);
+      ptr = argv[i];
+      ptr += 5;
+      while (*ptr == '-')
+        ptr++;
 
-  strcpy(string, new);
-  // fprintf(stderr, "string: %s\n", string);
-  // fprintf(stderr, "new: %s\n", new);
+      if (strncasecmp(ptr, "LTO", 3) == 0) {
+
+        aflcc->compiler_mode = LTO;
+
+      } else if (strncasecmp(ptr, "LLVM", 4) == 0) {
+
+        aflcc->compiler_mode = LLVM;
+
+      } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 ||
+
+                 strncasecmp(ptr, "PC-GUARD", 8) == 0) {
+
+        aflcc->compiler_mode = LLVM;
+        aflcc->instrument_mode = INSTRUMENT_PCGUARD;
+
+      } else if (strcasecmp(ptr, "INSTRIM") == 0 ||
+
+                 strcasecmp(ptr, "CFG") == 0) {
+
+        FATAL(
+            "InsTrim instrumentation was removed. Use a modern LLVM and "
+            "PCGUARD (default in afl-cc).\n");
+
+      } else if (strcasecmp(ptr, "AFL") == 0 ||
+
+                 strcasecmp(ptr, "CLASSIC") == 0) {
+
+        aflcc->compiler_mode = LLVM;
+        aflcc->instrument_mode = INSTRUMENT_CLASSIC;
+
+      } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 ||
+
+                 strcasecmp(ptr, "NATIVE") == 0 ||
+                 strcasecmp(ptr, "LLVM-NATIVE") == 0) {
+
+        aflcc->compiler_mode = LLVM;
+        aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
+
+      } else if (strncasecmp(ptr, "GCC_P", 5) == 0 ||
+
+                 strncasecmp(ptr, "GCC-P", 5) == 0 ||
+                 strncasecmp(ptr, "GCCP", 4) == 0) {
+
+        aflcc->compiler_mode = GCC_PLUGIN;
+
+      } else if (strcasecmp(ptr, "GCC") == 0) {
+
+        aflcc->compiler_mode = GCC;
+
+      } else if (strncasecmp(ptr, "CLANG", 5) == 0) {
+
+        aflcc->compiler_mode = CLANG;
+
+      } else
+
+        FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]);
+
+    }
+
+  }
 
 }
 
-static u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0,
-          shared_linking = 0, preprocessor_only = 0, have_unroll = 0,
-          have_o = 0, have_pic = 0, have_c = 0, partial_linking = 0,
-          non_dash = 0;
+/*
+  Select instrument_mode by those envs in old style:
+  - USE_TRACE_PC, AFL_USE_TRACE_PC, AFL_LLVM_USE_TRACE_PC, AFL_TRACE_PC
+  - AFL_LLVM_CALLER, AFL_LLVM_CTX, AFL_LLVM_CTX_K
+  - AFL_LLVM_NGRAM_SIZE
+*/
+static void instrument_mode_old_environ(aflcc_state_t *aflcc) {
+
+  if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") ||
+      getenv("INSTRIM_LIB")) {
+
+    FATAL(
+        "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD "
+        "(default in afl-cc).\n");
+
+  }
+
+  if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
+      getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) {
+
+    if (aflcc->instrument_mode == 0)
+      aflcc->instrument_mode = INSTRUMENT_PCGUARD;
+    else if (aflcc->instrument_mode != INSTRUMENT_PCGUARD)
+      FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together");
+
+  }
+
+  if (getenv("AFL_LLVM_CTX")) aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX;
+  if (getenv("AFL_LLVM_CALLER") || getenv("AFL_LLVM_LTO_CALLER") ||
+      getenv("AFL_LLVM_LTO_CTX"))
+    aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
 
-static void process_params(u32 argc, char **argv) {
+  if (getenv("AFL_LLVM_NGRAM_SIZE")) {
+
+    aflcc->instrument_opt_mode |= INSTRUMENT_OPT_NGRAM;
+    aflcc->ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE"));
+    if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX)
+      FATAL(
+          "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX "
+          "(%u)",
+          NGRAM_SIZE_MAX);
 
-  if (cc_par_cnt + argc >= 1024) { FATAL("Too many command line parameters"); }
+  }
+
+  if (getenv("AFL_LLVM_CTX_K")) {
+
+    aflcc->ctx_k = atoi(getenv("AFL_LLVM_CTX_K"));
+    if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K)
+      FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)",
+            CTX_MAX_K);
+    if (aflcc->ctx_k == 1) {
 
-  if (lto_mode && argc > 1) {
+      setenv("AFL_LLVM_CALLER", "1", 1);
+      unsetenv("AFL_LLVM_CTX_K");
+      aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
 
-    u32 idx;
-    for (idx = 1; idx < argc; idx++) {
+    } else {
 
-      if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1;
+      aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX_K;
 
     }
 
   }
 
-  // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]);
+}
 
-  /* Process the argument list. */
+/*
+  Select instrument_mode by env 'AFL_LLVM_INSTRUMENT'.
+  Previous compiler_mode will be superseded, if required by some
+  values of instrument_mode.
+*/
+static void instrument_mode_new_environ(aflcc_state_t *aflcc) {
 
-  u8 skip_next = 0;
-  while (--argc) {
+  if (!getenv("AFL_LLVM_INSTRUMENT")) { return; }
 
-    u8 *cur = *(++argv);
+  u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;");
 
-    if (skip_next) {
+  while (ptr2) {
 
-      skip_next = 0;
-      continue;
+    if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 ||
+        strncasecmp(ptr2, "classic", strlen("classic")) == 0) {
+
+      if (aflcc->instrument_mode == INSTRUMENT_LTO) {
+
+        aflcc->instrument_mode = INSTRUMENT_CLASSIC;
+        aflcc->lto_mode = 1;
+
+      } else if (!aflcc->instrument_mode ||
+
+                 aflcc->instrument_mode == INSTRUMENT_AFL) {
+
+        aflcc->instrument_mode = INSTRUMENT_AFL;
+
+      } else {
+
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
+
+      }
 
     }
 
-    if (cur[0] != '-') { non_dash = 1; }
-    if (!strncmp(cur, "--afl", 5)) continue;
+    if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 ||
+        strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) {
 
-    if (lto_mode && !strncmp(cur, "-flto=thin", 10)) {
+      if (!aflcc->instrument_mode ||
+          aflcc->instrument_mode == INSTRUMENT_PCGUARD)
 
-      FATAL(
-          "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or "
-          "use afl-clang-fast!");
+        aflcc->instrument_mode = INSTRUMENT_PCGUARD;
+
+      else
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
     }
 
-    if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue;
-    if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue;
-    if (!strncmp(cur, "-fno-unroll", 11)) continue;
-    if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue;
-    if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") ||
-        !strcmp(cur, "--no-undefined")) {
+    if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 ||
+        strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 ||
+        strncasecmp(ptr2, "native", strlen("native")) == 0) {
 
-      continue;
+      if (!aflcc->instrument_mode ||
+          aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE)
+
+        aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
+
+      else
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
     }
 
-    if (compiler_mode == GCC_PLUGIN && !strcmp(cur, "-pipe")) { continue; }
+    if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 ||
+        strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) {
 
-    if (!strcmp(cur, "-z") || !strcmp(cur, "-Wl,-z")) {
+      if (!aflcc->instrument_mode ||
+          aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) {
 
-      u8 *param = *(argv + 1);
-      if (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs")) {
+        aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
+        aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CODECOV;
 
-        skip_next = 1;
-        continue;
+      } else {
+
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
       }
 
     }
 
-    if ((compiler_mode == GCC || compiler_mode == GCC_PLUGIN) &&
-        !strncmp(cur, "-stdlib=", 8)) {
+    if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 ||
+        strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) {
 
-      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
-      continue;
+      FATAL(
+          "InsTrim instrumentation was removed. Use a modern LLVM and "
+          "PCGUARD (default in afl-cc).\n");
 
     }
 
-    if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) {
+    if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) {
 
-      have_instr_list = 1;
+      aflcc->lto_mode = 1;
+      if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_LTO)
+
+        aflcc->instrument_mode = INSTRUMENT_LTO;
+
+      else
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
     }
 
-    if (!strncmp(cur, "-fsanitize=", strlen("-fsanitize=")) &&
-        strchr(cur, ',')) {
+    if (strcasecmp(ptr2, "gcc") == 0) {
 
-      parse_fsanitize(cur);
-      if (!cur || strlen(cur) <= strlen("-fsanitize=")) { continue; }
+      if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_GCC)
 
-    } else if ((!strncmp(cur, "-fsanitize=fuzzer-",
+        aflcc->instrument_mode = INSTRUMENT_GCC;
 
-                         strlen("-fsanitize=fuzzer-")) ||
-                !strncmp(cur, "-fsanitize-coverage",
-                         strlen("-fsanitize-coverage"))) &&
-               (strncmp(cur, "sanitize-coverage-allow",
-                        strlen("sanitize-coverage-allow")) &&
-                strncmp(cur, "sanitize-coverage-deny",
-                        strlen("sanitize-coverage-deny")) &&
-                instrument_mode != INSTRUMENT_LLVMNATIVE)) {
+      else if (aflcc->instrument_mode != INSTRUMENT_GCC)
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
-      if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
-      continue;
+      aflcc->compiler_mode = GCC;
 
     }
 
-    if (need_aflpplib || !strcmp(cur, "-fsanitize=fuzzer")) {
+    if (strcasecmp(ptr2, "clang") == 0) {
 
-      u8 *afllib = find_object("libAFLDriver.a", argv[0]);
+      if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_CLANG)
 
-      if (!be_quiet) {
+        aflcc->instrument_mode = INSTRUMENT_CLANG;
 
-        OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a");
+      else if (aflcc->instrument_mode != INSTRUMENT_CLANG)
+        FATAL("main instrumentation mode already set with %s",
+              instrument_mode_2str(aflcc->instrument_mode));
 
-      }
+      aflcc->compiler_mode = CLANG;
 
-      if (!afllib) {
+    }
 
-        if (!be_quiet) {
+    if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0 ||
+        strncasecmp(ptr2, "kctx-", strlen("c-ctx-")) == 0 ||
+        strncasecmp(ptr2, "k-ctx-", strlen("k-ctx-")) == 0) {
 
-          WARNF(
-              "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in "
-              "the flags - this will fail!");
+      u8 *ptr3 = ptr2;
+      while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9'))
+        ptr3++;
 
-        }
+      if (!*ptr3) {
+
+        if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL)
+          FATAL(
+              "you must set the K-CTX K with (e.g. for value 2) "
+              "AFL_LLVM_INSTRUMENT=ctx-2");
+
+      }
+
+      aflcc->ctx_k = atoi(ptr3);
+      if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K)
+        FATAL(
+            "K-CTX instrumentation option must be between 1 and CTX_MAX_K "
+            "(%u)",
+            CTX_MAX_K);
+
+      if (aflcc->ctx_k == 1) {
+
+        aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+        setenv("AFL_LLVM_CALLER", "1", 1);
+        unsetenv("AFL_LLVM_CTX_K");
 
       } else {
 
-        cc_params[cc_par_cnt++] = afllib;
+        aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K);
+        u8 *ptr4 = alloc_printf("%u", aflcc->ctx_k);
+        setenv("AFL_LLVM_CTX_K", ptr4, 1);
 
-#ifdef __APPLE__
-        cc_params[cc_par_cnt++] = "-undefined";
-        cc_params[cc_par_cnt++] = "dynamic_lookup";
-#endif
+      }
+
+    }
+
+    if (strcasecmp(ptr2, "ctx") == 0) {
+
+      aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX;
+      setenv("AFL_LLVM_CTX", "1", 1);
+
+    }
+
+    if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) {
+
+      aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+      setenv("AFL_LLVM_CALLER", "1", 1);
+
+    }
+
+    if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) {
+
+      u8 *ptr3 = ptr2 + strlen("ngram");
+      while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) {
+
+        ptr3++;
 
       }
 
-      if (need_aflpplib) {
+      if (!*ptr3) {
 
-        need_aflpplib = 0;
+        if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL)
+          FATAL(
+              "you must set the NGRAM size with (e.g. for value 2) "
+              "AFL_LLVM_INSTRUMENT=ngram-2");
 
-      } else {
+      }
 
-        continue;
+      aflcc->ngram_size = atoi(ptr3);
+
+      if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX) {
+
+        FATAL(
+            "NGRAM instrumentation option must be between 2 and "
+            "NGRAM_SIZE_MAX (%u)",
+            NGRAM_SIZE_MAX);
 
       }
 
+      aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM);
+      u8 *ptr4 = alloc_printf("%u", aflcc->ngram_size);
+      setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1);
+
     }
 
-    if (!strcmp(cur, "-m32")) bit_mode = 32;
-    if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;
-    if (!strcmp(cur, "-m64")) bit_mode = 64;
+    ptr2 = strtok(NULL, ":,;");
 
-    if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
-      asan_set = 1;
+  }
 
-    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
+}
 
-    if (!strcmp(cur, "-x")) x_set = 1;
-    if (!strcmp(cur, "-E")) preprocessor_only = 1;
-    if (!strcmp(cur, "-shared")) shared_linking = 1;
-    if (!strcmp(cur, "-dynamiclib")) shared_linking = 1;
-    if (!strcmp(cur, "--target=wasm32-wasi")) passthrough = 1;
-    if (!strcmp(cur, "-Wl,-r")) partial_linking = 1;
-    if (!strcmp(cur, "-Wl,-i")) partial_linking = 1;
-    if (!strcmp(cur, "-Wl,--relocatable")) partial_linking = 1;
-    if (!strcmp(cur, "-r")) partial_linking = 1;
-    if (!strcmp(cur, "--relocatable")) partial_linking = 1;
-    if (!strcmp(cur, "-c")) have_c = 1;
+/*
+  Select instrument_mode by envs, the top wrapper. We check
+  have_instr_env firstly, then call instrument_mode_old_environ
+  and instrument_mode_new_environ sequentially.
+*/
+void instrument_mode_by_environ(aflcc_state_t *aflcc) {
 
-    if (!strncmp(cur, "-O", 2)) have_o = 1;
-    if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1;
+  if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") ||
+      getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") ||
+      getenv("AFL_LLVM_BLOCKLIST")) {
 
-    if (*cur == '@') {
+    aflcc->have_instr_env = 1;
 
-      // response file support.
-      // we have two choices - move everything to the command line or
-      // rewrite the response files to temporary files and delete them
-      // afterwards. We choose the first for easiness.
-      // We do *not* support quotes in the rsp files to cope with spaces in
-      // filenames etc! If you need that then send a patch!
-      u8 *filename = cur + 1;
-      if (debug) { DEBUGF("response file=%s\n", filename); }
-      FILE       *f = fopen(filename, "r");
-      struct stat st;
+  }
 
-      // Check not found or empty? let the compiler complain if so.
-      if (!f || fstat(fileno(f), &st) < 0 || st.st_size < 1) {
+  if (aflcc->have_instr_env && getenv("AFL_DONT_OPTIMIZE") && !be_quiet) {
 
-        cc_params[cc_par_cnt++] = cur;
-        continue;
+    WARNF(
+        "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined "
+        "for file matching, only function matching!");
 
-      }
+  }
 
-      u8    *tmpbuf = malloc(st.st_size + 1), *ptr;
-      char **args = malloc(sizeof(char *) * (st.st_size >> 1));
-      int    count = 1, cont = 0, cont_act = 0;
+  instrument_mode_old_environ(aflcc);
+  instrument_mode_new_environ(aflcc);
 
-      while (fgets(tmpbuf, st.st_size, f)) {
+}
 
-        ptr = tmpbuf;
-        // no leading whitespace
-        while (isspace(*ptr)) {
+/*
+  Workaround to ensure CALLER, CTX, K-CTX and NGRAM
+  instrumentation were used correctly.
+*/
+static void instrument_opt_mode_exclude(aflcc_state_t *aflcc) {
 
-          ++ptr;
-          cont_act = 0;
+  if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER)) {
 
-        }
+    FATAL("you cannot set CTX and CALLER together");
 
-        // no comments, no empty lines
-        if (*ptr == '#' || *ptr == '\n' || !*ptr) { continue; }
-        // remove LF
-        if (ptr[strlen(ptr) - 1] == '\n') { ptr[strlen(ptr) - 1] = 0; }
-        // remove CR
-        if (*ptr && ptr[strlen(ptr) - 1] == '\r') { ptr[strlen(ptr) - 1] = 0; }
-        // handle \ at end of line
-        if (*ptr && ptr[strlen(ptr) - 1] == '\\') {
+  }
 
-          cont = 1;
-          ptr[strlen(ptr) - 1] = 0;
+  if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
 
-        }
+    FATAL("you cannot set CTX and K-CTX together");
 
-        // remove whitespace at end
-        while (*ptr && isspace(ptr[strlen(ptr) - 1])) {
+  }
 
-          ptr[strlen(ptr) - 1] = 0;
-          cont = 0;
+  if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) &&
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
 
-        }
+    FATAL("you cannot set CALLER and K-CTX together");
 
-        if (*ptr) {
+  }
 
-          do {
+  if (aflcc->instrument_opt_mode && aflcc->compiler_mode != LLVM &&
+      !((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) &&
+        aflcc->compiler_mode == LTO))
+    FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode");
 
-            u8 *value = ptr;
-            while (*ptr && !isspace(*ptr)) {
+  if (aflcc->instrument_opt_mode &&
+      aflcc->instrument_opt_mode != INSTRUMENT_OPT_CODECOV &&
+      aflcc->instrument_mode != INSTRUMENT_CLASSIC &&
+      !(aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER &&
+        aflcc->compiler_mode == LTO))
+    FATAL(
+        "CALLER, CTX and NGRAM instrumentation options can only be used with "
+        "the LLVM CLASSIC instrumentation mode.");
 
-              ++ptr;
+}
 
-            }
+/*
+  Last step of compiler_mode & instrument_mode selecting.
+  We have a few of workarounds here, to check any corner cases,
+  prepare for a series of fallbacks, and raise warnings or errors.
+*/
+void mode_final_checkout(aflcc_state_t *aflcc, int argc, char **argv) {
 
-            while (*ptr && isspace(*ptr)) {
+  if (aflcc->instrument_opt_mode &&
+      aflcc->instrument_mode == INSTRUMENT_DEFAULT &&
+      (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == UNSET)) {
 
-              *ptr++ = 0;
+    aflcc->instrument_mode = INSTRUMENT_CLASSIC;
+    aflcc->compiler_mode = LLVM;
 
-            }
+  }
 
-            if (cont_act) {
+  if (!aflcc->compiler_mode) {
 
-              u32 len = strlen(args[count - 1]) + strlen(value) + 1;
-              u8 *tmp = malloc(len);
-              snprintf(tmp, len, "%s%s", args[count - 1], value);
-              free(args[count - 1]);
-              args[count - 1] = tmp;
-              cont_act = 0;
+    // lto is not a default because outside of afl-cc RANLIB and AR have to
+    // be set to LLVM versions so this would work
+    if (aflcc->have_llvm)
+      aflcc->compiler_mode = LLVM;
+    else if (aflcc->have_gcc_plugin)
+      aflcc->compiler_mode = GCC_PLUGIN;
+    else if (aflcc->have_gcc)
+      aflcc->compiler_mode = GCC;
+    else if (aflcc->have_clang)
+      aflcc->compiler_mode = CLANG;
+    else if (aflcc->have_lto)
+      aflcc->compiler_mode = LTO;
+    else
+      FATAL("no compiler mode available");
 
-            } else {
+  }
 
-              args[count++] = strdup(value);
+  switch (aflcc->compiler_mode) {
+
+    case GCC:
+      if (!aflcc->have_gcc) FATAL("afl-gcc is not available on your platform!");
+      break;
+    case CLANG:
+      if (!aflcc->have_clang)
+        FATAL("afl-clang is not available on your platform!");
+      break;
+    case LLVM:
+      if (!aflcc->have_llvm)
+        FATAL(
+            "LLVM mode is not available, please install LLVM 13+ and recompile "
+            "AFL++");
+      break;
+    case GCC_PLUGIN:
+      if (!aflcc->have_gcc_plugin)
+        FATAL(
+            "GCC_PLUGIN mode is not available, install gcc plugin support and "
+            "recompile AFL++");
+      break;
+    case LTO:
+      if (!aflcc->have_lto)
+        FATAL(
+            "LTO mode is not available, please install LLVM 13+ and lld of the "
+            "same version and recompile AFL++");
+      break;
+    default:
+      FATAL("no compiler mode available");
 
-            }
+  }
 
-          } while (*ptr);
+  if (aflcc->compiler_mode == GCC) { aflcc->instrument_mode = INSTRUMENT_GCC; }
 
-        }
+  if (aflcc->compiler_mode == CLANG) {
 
-        if (cont) {
+    /* if our PCGUARD implementation is not available then silently switch to
+     native LLVM PCGUARD. Or classic asm instrument is explicitly preferred. */
+    if (!aflcc->have_optimized_pcguard &&
+        (aflcc->instrument_mode == INSTRUMENT_DEFAULT ||
+         aflcc->instrument_mode == INSTRUMENT_PCGUARD)) {
 
-          cont_act = 1;
-          cont = 0;
+      aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-        }
+    } else {
 
-      }
+      aflcc->instrument_mode = INSTRUMENT_CLANG;
+      setenv(CLANG_ENV_VAR, "1", 1);  // used by afl-as
 
-      if (count) { process_params(count, args); }
+    }
 
-      // we cannot free args[]
-      free(tmpbuf);
+  }
 
-      continue;
+  if (aflcc->compiler_mode == LTO) {
+
+    if (aflcc->instrument_mode == 0 ||
+        aflcc->instrument_mode == INSTRUMENT_LTO ||
+        aflcc->instrument_mode == INSTRUMENT_CFG ||
+        aflcc->instrument_mode == INSTRUMENT_PCGUARD) {
+
+      aflcc->lto_mode = 1;
+      aflcc->instrument_mode = INSTRUMENT_PCGUARD;
+
+    } else if (aflcc->instrument_mode == INSTRUMENT_CLASSIC) {
+
+      aflcc->lto_mode = 1;
+
+    } else {
+
+      if (!be_quiet) {
+
+        WARNF("afl-clang-lto called with mode %s, using that mode instead",
+              instrument_mode_2str(aflcc->instrument_mode));
+
+      }
 
     }
 
-    cc_params[cc_par_cnt++] = cur;
+  }
+
+  if (aflcc->instrument_mode == 0 && aflcc->compiler_mode < GCC_PLUGIN) {
+
+#if LLVM_MAJOR >= 7
+  #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1)
+    if (aflcc->have_instr_env) {
+
+      aflcc->instrument_mode = INSTRUMENT_AFL;
+      if (!be_quiet) {
+
+        WARNF(
+            "Switching to classic instrumentation because "
+            "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1.");
+
+      }
+
+    } else
+
+  #endif
+      aflcc->instrument_mode = INSTRUMENT_PCGUARD;
+
+#else
+    aflcc->instrument_mode = INSTRUMENT_AFL;
+#endif
 
   }
 
-}
+  if (!aflcc->instrument_opt_mode && aflcc->lto_mode &&
+      aflcc->instrument_mode == INSTRUMENT_CFG) {
 
-/* Copy argv to cc_params, making the necessary edits. */
+    aflcc->instrument_mode = INSTRUMENT_PCGUARD;
 
-static void edit_params(u32 argc, char **argv, char **envp) {
+  }
 
-  cc_params = ck_alloc(1024 * sizeof(u8 *));
+#ifndef AFL_CLANG_FLTO
+  if (aflcc->lto_mode)
+    FATAL(
+        "instrumentation mode LTO specified but LLVM support not available "
+        "(requires LLVM 11 or higher)");
+#endif
 
-  if (lto_mode) {
+  if (aflcc->lto_mode) {
 
-    if (lto_flag[0] != '-')
+    if (aflcc->lto_flag[0] != '-')
       FATAL(
           "Using afl-clang-lto is not possible because Makefile magic did not "
           "identify the correct -flto flag");
     else
-      compiler_mode = LTO;
+      aflcc->compiler_mode = LTO;
 
   }
 
-  if (plusplus_mode) {
+  if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO"))
+    FATAL(
+        "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set "
+        "together");
 
-    u8 *alt_cxx = getenv("AFL_CXX");
+#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1)
 
-    if (!alt_cxx) {
+  if (aflcc->instrument_mode == INSTRUMENT_PCGUARD && aflcc->have_instr_env) {
+
+    FATAL(
+        "Instrumentation type PCGUARD does not support "
+        "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead.");
 
-      if (compiler_mode >= GCC_PLUGIN) {
+  }
 
-        if (compiler_mode == GCC) {
+#endif
 
-          alt_cxx = clang_mode ? "clang++" : "g++";
+  instrument_opt_mode_exclude(aflcc);
 
-        } else if (compiler_mode == CLANG) {
+  u8 *ptr2;
 
-          alt_cxx = "clang++";
+  if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/')
+    FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path");
 
-        } else {
+  if (getenv("AFL_LLVM_LAF_ALL")) {
 
-          alt_cxx = "g++";
+    setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1);
+    setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1);
+    setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1);
+    setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1);
 
-        }
+  }
+
+  if (getenv("AFL_LLVM_DICT2FILE") &&
+      (getenv("AFL_LLVM_LAF_SPLIT_SWITCHES") ||
+       getenv("AFL_LLVM_LAF_SPLIT_COMPARES") ||
+       getenv("AFL_LLVM_LAF_SPLIT_FLOATS") ||
+       getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")))
+    FATAL("AFL_LLVM_DICT2FILE is incompatible with AFL_LLVM_LAF_*");
+
+  aflcc->cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") ||
+                       getenv("AFL_GCC_CMPLOG");
+
+}
+
+/*
+  Print welcome message on screen, giving brief notes about
+  compiler_mode and instrument_mode.
+*/
+void mode_notification(aflcc_state_t *aflcc) {
+
+  char *ptr2 = alloc_printf(" + NGRAM-%u", aflcc->ngram_size);
+  char *ptr3 = alloc_printf(" + K-CTX-%u", aflcc->ctx_k);
+
+  char *ptr1 = alloc_printf(
+      "%s%s%s%s%s", instrument_mode_2str(aflcc->instrument_mode),
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "",
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "",
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "",
+      (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : "");
+
+  ck_free(ptr2);
+  ck_free(ptr3);
+
+  if ((isatty(2) && !be_quiet) || aflcc->debug) {
+
+    SAYF(cCYA
+         "afl-cc" VERSION cRST
+         " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n",
+         compiler_mode_2str(aflcc->compiler_mode), ptr1);
+
+  }
+
+  ck_free(ptr1);
+
+  if (!be_quiet &&
+      (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG)) {
+
+    WARNF(
+        "You are using outdated instrumentation, install LLVM and/or "
+        "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast "
+        "instead!");
+
+  }
+
+}
+
+/*
+  Set argv[0] required by execvp. It can be
+  - specified by env AFL_CXX
+  - g++ or clang++
+  - CLANGPP_BIN or LLVM_BINDIR/clang++
+  when in C++ mode, or
+  - specified by env AFL_CC
+  - gcc or clang
+  - CLANG_BIN or LLVM_BINDIR/clang
+  otherwise.
+*/
+void add_real_argv0(aflcc_state_t *aflcc) {
+
+  static u8 llvm_fullpath[PATH_MAX];
+
+  if (aflcc->plusplus_mode) {
+
+    u8 *alt_cxx = getenv("AFL_CXX");
+
+    if (!alt_cxx) {
+
+      if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) {
+
+        alt_cxx = "g++";
+
+      } else if (aflcc->compiler_mode == CLANG) {
+
+        alt_cxx = "clang++";
 
       } else {
 
@@ -723,7 +1468,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
     }
 
-    cc_params[0] = alt_cxx;
+    aflcc->cc_params[0] = alt_cxx;
 
   } else {
 
@@ -731,21 +1476,13 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
     if (!alt_cc) {
 
-      if (compiler_mode >= GCC_PLUGIN) {
-
-        if (compiler_mode == GCC) {
+      if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) {
 
-          alt_cc = clang_mode ? "clang" : "gcc";
+        alt_cc = "gcc";
 
-        } else if (compiler_mode == CLANG) {
+      } else if (aflcc->compiler_mode == CLANG) {
 
-          alt_cc = "clang";
-
-        } else {
-
-          alt_cc = "gcc";
-
-        }
+        alt_cc = "clang";
 
       } else {
 
@@ -753,1319 +1490,1284 @@ static void edit_params(u32 argc, char **argv, char **envp) {
           snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang",
                    LLVM_BINDIR);
         else
-          snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s", CLANG_BIN);
+          snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANG_BIN);
         alt_cc = llvm_fullpath;
 
       }
 
     }
 
-    cc_params[0] = alt_cc;
+    aflcc->cc_params[0] = alt_cc;
 
   }
 
-  if (compiler_mode == GCC || compiler_mode == CLANG) {
+}
 
-    cc_params[cc_par_cnt++] = "-B";
-    cc_params[cc_par_cnt++] = obj_path;
+/** compiler_mode & instrument_mode selecting -----END----- **/
 
-    if (clang_mode || compiler_mode == CLANG) {
+/** Macro defs for the preprocessor -----BEGIN----- **/
 
-      cc_params[cc_par_cnt++] = "-no-integrated-as";
+void add_defs_common(aflcc_state_t *aflcc) {
 
-    }
+  insert_param(aflcc, "-D__AFL_COMPILER=1");
+  insert_param(aflcc, "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1");
 
-  }
-
-  if (compiler_mode == GCC_PLUGIN) {
+}
 
-    char *fplugin_arg;
+/*
+  __afl_coverage macro defs. See
+  instrumentation/README.instrument_list.md#
+  2-selective-instrumentation-with-_afl_coverage-directives
+*/
+void add_defs_selective_instr(aflcc_state_t *aflcc) {
 
-    if (cmplog_mode) {
+  if (aflcc->plusplus_mode) {
 
-      fplugin_arg =
-          alloc_printf("-fplugin=%s/afl-gcc-cmplog-pass.so", obj_path);
-      cc_params[cc_par_cnt++] = fplugin_arg;
-      fplugin_arg =
-          alloc_printf("-fplugin=%s/afl-gcc-cmptrs-pass.so", obj_path);
-      cc_params[cc_par_cnt++] = fplugin_arg;
+    insert_param(aflcc,
+                 "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;"
+                 "extern \"C\" void __afl_coverage_discard();"
+                 "extern \"C\" void __afl_coverage_skip();"
+                 "extern \"C\" void __afl_coverage_on();"
+                 "extern \"C\" void __afl_coverage_off();");
 
-    }
+  } else {
 
-    fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path);
-    cc_params[cc_par_cnt++] = fplugin_arg;
-    cc_params[cc_par_cnt++] = "-fno-if-conversion";
-    cc_params[cc_par_cnt++] = "-fno-if-conversion2";
+    insert_param(aflcc,
+                 "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;"
+                 "void __afl_coverage_discard();"
+                 "void __afl_coverage_skip();"
+                 "void __afl_coverage_on();"
+                 "void __afl_coverage_off();");
 
   }
 
-  if (compiler_mode == LLVM || compiler_mode == LTO) {
+  insert_param(
+      aflcc,
+      "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = "
+      "1;");
+  insert_param(aflcc, "-D__AFL_COVERAGE_ON()=__afl_coverage_on()");
+  insert_param(aflcc, "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()");
+  insert_param(aflcc, "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()");
+  insert_param(aflcc, "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()");
 
-    cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument";
+}
 
-    if (lto_mode && have_instr_env) {
+/*
+  Macro defs for persistent mode. As documented in
+  instrumentation/README.persistent_mode.md, deferred forkserver initialization
+  and persistent mode are not available in afl-gcc and afl-clang.
+*/
+void add_defs_persistent_mode(aflcc_state_t *aflcc) {
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] = alloc_printf(
-          "-fpass-plugin=%s/afl-llvm-lto-instrumentlist.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path);
-#endif
+  if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) return;
 
-    }
+  insert_param(aflcc, "-D__AFL_HAVE_MANUAL_CONTROL=1");
 
-    if (getenv("AFL_LLVM_DICT2FILE")) {
+  /* When the user tries to use persistent or deferred forkserver modes by
+      appending a single line to the program, we want to reliably inject a
+      signature into the binary (to be picked up by afl-fuzz) and we want
+      to call a function from the runtime .o file. This is unnecessarily
+      painful for three reasons:
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/afl-llvm-dict2file.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/afl-llvm-dict2file.so", obj_path);
-#endif
+      1) We need to convince the compiler not to optimize out the signature.
+        This is done with __attribute__((used)).
 
-    }
+      2) We need to convince the linker, when called with -Wl,--gc-sections,
+        not to do the same. This is done by forcing an assignment to a
+        'volatile' pointer.
 
-    // laf
-    if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
+      3) We need to declare __afl_persistent_loop() in the global namespace,
+        but doing this within a method in a class is hard - :: and extern "C"
+        are forbidden and __attribute__((alias(...))) doesn't work. Hence the
+        __asm__ aliasing trick.
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/split-switches-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/split-switches-pass.so", obj_path);
-#endif
+    */
 
-    }
+  insert_param(aflcc,
+               "-D__AFL_FUZZ_INIT()="
+               "int __afl_sharedmem_fuzzing = 1;"
+               "extern __attribute__((visibility(\"default\"))) "
+               "unsigned int *__afl_fuzz_len;"
+               "extern __attribute__((visibility(\"default\"))) "
+               "unsigned char *__afl_fuzz_ptr;"
+               "unsigned char __afl_fuzz_alt[1048576];"
+               "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;");
 
-    if (getenv("LAF_TRANSFORM_COMPARES") ||
-        getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
+  insert_param(aflcc,
+               "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : "
+               "__afl_fuzz_alt_ptr)");
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/compare-transform-pass.so", obj_path);
+  insert_param(
+      aflcc,
+      "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : "
+      "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff "
+      "? 0 : *__afl_fuzz_len)");
+
+  insert_param(
+      aflcc,
+      "-D__AFL_LOOP(_A)="
+      "({ static volatile const char *_B __attribute__((used,unused)); "
+      " _B = (const char*)\"" PERSIST_SIG
+      "\"; "
+      "extern __attribute__((visibility(\"default\"))) int __afl_connected;"
+#ifdef __APPLE__
+      "__attribute__((visibility(\"default\"))) "
+      "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
 #else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/compare-transform-pass.so", obj_path);
-#endif
+      "__attribute__((visibility(\"default\"))) "
+      "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
+#endif                                                        /* ^__APPLE__ */
+      // if afl is connected, we run _A times, else once.
+      "_L(__afl_connected ? _A : 1); })");
+
+  insert_param(
+      aflcc,
+      "-D__AFL_INIT()="
+      "do { static volatile const char *_A __attribute__((used,unused)); "
+      " _A = (const char*)\"" DEFER_SIG
+      "\"; "
+#ifdef __APPLE__
+      "__attribute__((visibility(\"default\"))) "
+      "void _I(void) __asm__(\"___afl_manual_init\"); "
+#else
+      "__attribute__((visibility(\"default\"))) "
+      "void _I(void) __asm__(\"__afl_manual_init\"); "
+#endif                                                        /* ^__APPLE__ */
+      "_I(); } while (0)");
 
-    }
+}
 
-    if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") ||
-        getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) {
+/*
+  Control macro def of _FORTIFY_SOURCE. It will do nothing
+  if we detect this routine has been called previously, or
+  the macro already here in these existing args.
+*/
+void add_defs_fortify(aflcc_state_t *aflcc, u8 action) {
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/split-compares-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/split-compares-pass.so", obj_path);
-#endif
+  if (aflcc->have_fortify) { return; }
 
-    }
+  switch (action) {
 
-    // /laf
+    case 1:
+      insert_param(aflcc, "-D_FORTIFY_SOURCE=1");
+      break;
 
-    unsetenv("AFL_LD");
-    unsetenv("AFL_LD_CALLER");
+    case 2:
+      insert_param(aflcc, "-D_FORTIFY_SOURCE=2");
+      break;
 
-    if (cmplog_mode) {
+    default:  // OFF
+      insert_param(aflcc, "-U_FORTIFY_SOURCE");
+      break;
 
-      cc_params[cc_par_cnt++] = "-fno-inline";
+  }
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/cmplog-switches-pass.so", obj_path);
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/split-switches-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/cmplog-switches-pass.so", obj_path);
+  aflcc->have_fortify = 1;
 
-      // reuse split switches from laf
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/split-switches-pass.so", obj_path);
-#endif
+}
 
-    }
+/* Macro defs of __AFL_LEAK_CHECK, __AFL_LSAN_ON and __AFL_LSAN_OFF */
+void add_defs_lsan_ctrl(aflcc_state_t *aflcc) {
 
-    //#if LLVM_MAJOR >= 13
-    //    // Use the old pass manager in LLVM 14 which the AFL++ passes still
-    //    use. cc_params[cc_par_cnt++] = "-flegacy-pass-manager";
-    //#endif
+  insert_param(aflcc, "-includesanitizer/lsan_interface.h");
+  insert_param(
+      aflcc,
+      "-D__AFL_LEAK_CHECK()={if(__lsan_do_recoverable_leak_check() > 0) "
+      "_exit(23); }");
+  insert_param(aflcc, "-D__AFL_LSAN_OFF()=__lsan_disable();");
+  insert_param(aflcc, "-D__AFL_LSAN_ON()=__lsan_enable();");
 
-    if (lto_mode && !have_c) {
+}
 
-      u8 *ld_path = NULL;
-      if (getenv("AFL_REAL_LD")) {
+/** Macro defs for the preprocessor -----END----- **/
 
-        ld_path = strdup(getenv("AFL_REAL_LD"));
+/** About -fsanitize -----BEGIN----- **/
 
-      } else {
+/* For input "-fsanitize=...", it:
 
-        ld_path = strdup(AFL_REAL_LD);
+  1. may have various OOB traps :) if ... doesn't contain ',' or
+    the input has bad syntax such as "-fsantiz=,"
+  2. strips any fuzzer* in ... and writes back (may result in "-fsanitize=")
+  3. rets 1 if exactly "fuzzer" found, otherwise rets 0
+*/
+static u8 fsanitize_fuzzer_comma(char *string) {
 
-      }
+  u8 detect_single_fuzzer = 0;
 
-      if (!ld_path || !*ld_path) {
+  char *p, *ptr = string + strlen("-fsanitize=");
+  // ck_alloc will check alloc failure
+  char *new = ck_alloc(strlen(string) + 1);
+  char *tmp = ck_alloc(strlen(ptr) + 1);
+  u32   count = 0, len, ende = 0;
 
-        if (ld_path) {
+  strcpy(new, "-fsanitize=");
 
-          // Freeing empty string
-          free(ld_path);
+  do {
 
-        }
+    p = strchr(ptr, ',');
+    if (!p) {
 
-        ld_path = strdup("ld.lld");
+      p = ptr + strlen(ptr) + 1;
+      ende = 1;
 
-      }
+    }
 
-      if (!ld_path) { PFATAL("Could not allocate mem for ld_path"); }
-#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12
-      cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", ld_path);
-#else
-      cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_path);
-#endif
-      free(ld_path);
+    len = p - ptr;
+    if (len) {
 
-#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15
-      // The NewPM implementation only works fully since LLVM 15.
-      cc_params[cc_par_cnt++] = alloc_printf(
-          "-Wl,--load-pass-plugin=%s/SanitizerCoverageLTO.so", obj_path);
-#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13
-      cc_params[cc_par_cnt++] = "-Wl,--lto-legacy-pass-manager";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path);
-#endif
+      strncpy(tmp, ptr, len);
+      tmp[len] = 0;
+      // fprintf(stderr, "Found: %s\n", tmp);
+      ptr += len + 1;
+      if (*tmp) {
 
-      cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition";
-      cc_params[cc_par_cnt++] = lto_flag;
+        u32 copy = 1;
+        if (!strcmp(tmp, "fuzzer")) {
 
-    } else {
+          detect_single_fuzzer = 1;
+          copy = 0;
 
-      if (instrument_mode == INSTRUMENT_PCGUARD) {
+        } else if (!strncmp(tmp, "fuzzer", 6)) {
 
-#if LLVM_MAJOR >= 11
-  #if defined __ANDROID__ || ANDROID
-        cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
-        instrument_mode = INSTRUMENT_LLVMNATIVE;
-  #else
-        if (have_instr_list) {
-
-          if (!be_quiet)
-            SAYF(
-                "Using unoptimized trace-pc-guard, due usage of "
-                "-fsanitize-coverage-allow/denylist, you can use "
-                "AFL_LLVM_ALLOWLIST/AFL_LLMV_DENYLIST instead.\n");
-          cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
-          instrument_mode = INSTRUMENT_LLVMNATIVE;
-
-        } else {
-
-    #if LLVM_MAJOR >= 11                            /* use new pass manager */
-      #if LLVM_MAJOR < 16
-          cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-      #endif
-          cc_params[cc_par_cnt++] = alloc_printf(
-              "-fpass-plugin=%s/SanitizerCoveragePCGUARD.so", obj_path);
-    #else
-          cc_params[cc_par_cnt++] = "-Xclang";
-          cc_params[cc_par_cnt++] = "-load";
-          cc_params[cc_par_cnt++] = "-Xclang";
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path);
-    #endif
+          copy = 0;
 
         }
 
-  #endif
-#else
-  #if LLVM_MAJOR >= 4
-        if (!be_quiet)
-          SAYF(
-              "Using unoptimized trace-pc-guard, upgrade to llvm 10.0.1+ for "
-              "enhanced version.\n");
-        cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
-        instrument_mode = INSTRUMENT_LLVMNATIVE;
-  #else
-        FATAL("pcguard instrumentation requires llvm 4.0.1+");
-  #endif
-#endif
+        if (copy) {
 
-      } else if (instrument_mode == INSTRUMENT_LLVMNATIVE) {
+          if (count) { strcat(new, ","); }
+          strcat(new, tmp);
+          ++count;
 
-#if LLVM_MAJOR >= 4
-        if (instrument_opt_mode & INSTRUMENT_OPT_CODECOV) {
+        }
 
-  #if LLVM_MAJOR >= 6
-          cc_params[cc_par_cnt++] =
-              "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table";
-  #else
-          FATAL("pcguard instrumentation with pc-table requires llvm 6.0.1+");
-  #endif
+      }
 
-        } else {
+    } else {
 
-          cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
+      ptr++;
 
-        }
+    }
 
-#else
-        FATAL("pcguard instrumentation requires llvm 4.0.1+");
-#endif
+  } while (!ende);
 
-      } else {
+  strcpy(string, new);
 
-#if LLVM_MAJOR >= 11                                /* use new pass manager */
-  #if LLVM_MAJOR < 16
-        cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-        cc_params[cc_par_cnt++] =
-            alloc_printf("-fpass-plugin=%s/afl-llvm-pass.so", obj_path);
-#else
+  ck_free(tmp);
+  ck_free(new);
 
-        cc_params[cc_par_cnt++] = "-Xclang";
-        cc_params[cc_par_cnt++] = "-load";
-        cc_params[cc_par_cnt++] = "-Xclang";
-        cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
-#endif
+  return detect_single_fuzzer;
 
-      }
+}
 
-    }
+/*
+  Parse and process possible -fsanitize related args, return PARAM_MISS
+  if nothing matched. We have 3 main tasks here for these args:
+  - Check which one of those sanitizers present here.
+  - Check if libfuzzer present. We need to block the request of enable
+    libfuzzer, and link harness with our libAFLDriver.a later.
+  - Check if SanCov allow/denylist options present. We need to try switching
+    to LLVMNATIVE instead of using our optimized PCGUARD anyway. If we
+    can't make it finally for various reasons, just drop these options.
+*/
+param_st parse_fsanitize(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) {
+
+  param_st final_ = PARAM_MISS;
+
+// MACRO START
+#define HAVE_SANITIZER_SCAN_KEEP(v, k)        \
+  do {                                        \
+                                              \
+    if (strstr(cur_argv, "=" STRINGIFY(k)) || \
+        strstr(cur_argv, "," STRINGIFY(k))) { \
+                                              \
+      if (scan) {                             \
+                                              \
+        aflcc->have_##v = 1;                  \
+        final_ = PARAM_SCAN;                  \
+                                              \
+      } else {                                \
+                                              \
+        final_ = PARAM_KEEP;                  \
+                                              \
+      }                                       \
+                                              \
+    }                                         \
+                                              \
+  } while (0)
+
+  // MACRO END
+
+  if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize="))) {
+
+    HAVE_SANITIZER_SCAN_KEEP(asan, address);
+    HAVE_SANITIZER_SCAN_KEEP(msan, memory);
+    HAVE_SANITIZER_SCAN_KEEP(ubsan, undefined);
+    HAVE_SANITIZER_SCAN_KEEP(tsan, thread);
+    HAVE_SANITIZER_SCAN_KEEP(lsan, leak);
+    HAVE_SANITIZER_SCAN_KEEP(cfisan, cfi);
 
-    if (cmplog_mode) {
+  }
 
-#if LLVM_MAJOR >= 11
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] = alloc_printf(
-          "-fpass-plugin=%s/cmplog-instructions-pass.so", obj_path);
-  #if LLVM_MAJOR < 16
-      cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
-  #endif
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-fpass-plugin=%s/cmplog-routines-pass.so", obj_path);
-#else
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/cmplog-instructions-pass.so", obj_path);
-
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] = "-load";
-      cc_params[cc_par_cnt++] = "-Xclang";
-      cc_params[cc_par_cnt++] =
-          alloc_printf("%s/cmplog-routines-pass.so", obj_path);
-#endif
+#undef HAVE_SANITIZER_SCAN_KEEP
+
+  // We can't use a "else if" there, because some of the following
+  // matching rules overlap with those in the if-statement above.
+  if (!strcmp(cur_argv, "-fsanitize=fuzzer")) {
+
+    if (scan) {
+
+      aflcc->need_aflpplib = 1;
+      final_ = PARAM_SCAN;
+
+    } else {
+
+      final_ = PARAM_DROP;
 
     }
 
-    // cc_params[cc_par_cnt++] = "-Qunused-arguments";
+  } else if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize=")) &&
+
+             strchr(cur_argv, ',') &&
+             !strstr(cur_argv, "=,")) {  // avoid OOB errors
+
+    if (scan) {
 
-    if (lto_mode && argc > 1) {
+      u8 *cur_argv_ = ck_strdup(cur_argv);
 
-      u32 idx;
-      for (idx = 1; idx < argc; idx++) {
+      if (fsanitize_fuzzer_comma(cur_argv_)) {
 
-        if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1;
+        aflcc->need_aflpplib = 1;
+        final_ = PARAM_SCAN;
 
       }
 
-    }
+      ck_free(cur_argv_);
 
-  }
+    } else {
 
-  /* Inspect the command line parameters. */
+      fsanitize_fuzzer_comma(cur_argv);
+      if (!cur_argv || strlen(cur_argv) <= strlen("-fsanitize="))
+        final_ = PARAM_DROP;  // this means it only has "fuzzer" previously.
 
-  process_params(argc, argv);
+    }
 
-  if (!have_pic) { cc_params[cc_par_cnt++] = "-fPIC"; }
+  } else if (!strncmp(cur_argv, "-fsanitize-coverage-", 20) &&
 
-  // in case LLVM is installed not via a package manager or "make install"
-  // e.g. compiled download or compiled from github then its ./lib directory
-  // might not be in the search path. Add it if so.
-  u8 *libdir = strdup(LLVM_LIBDIR);
-  if (plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) &&
-      strncmp(libdir, "/lib", 4)) {
+             strstr(cur_argv, "list=")) {
 
-    cc_params[cc_par_cnt++] = "-Wl,-rpath";
-    cc_params[cc_par_cnt++] = libdir;
+    if (scan) {
 
-  } else {
+      aflcc->have_instr_list = 1;
+      final_ = PARAM_SCAN;
 
-    free(libdir);
+    } else {
 
-  }
+      if (aflcc->instrument_mode != INSTRUMENT_LLVMNATIVE) {
 
-  if (getenv("AFL_HARDEN")) {
+        if (!be_quiet) { WARNF("Found '%s' - stripping!", cur_argv); }
+        final_ = PARAM_DROP;
+
+      } else {
 
-    cc_params[cc_par_cnt++] = "-fstack-protector-all";
+        final_ = PARAM_KEEP;
 
-    if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
+      }
+
+    }
 
   }
 
-  if (!asan_set) {
+  if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv);
 
-    if (getenv("AFL_USE_ASAN")) {
+  return final_;
 
-      if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive");
+}
 
-      if (getenv("AFL_HARDEN"))
-        FATAL("ASAN and AFL_HARDEN are mutually exclusive");
+/*
+  Add params for sanitizers. Here we need to consider:
+  - Use static runtime for asan, as much as possible.
+  - ASAN, MSAN, AFL_HARDEN are mutually exclusive.
+  - Add options if not found there, on request of AFL_USE_ASAN, AFL_USE_MSAN,
+  etc.
+  - Update have_* so that functions called after this can have correct context.
+    However this also means any functions called before should NOT depend on
+  these have_*, otherwise they may not work as expected.
+*/
+void add_sanitizers(aflcc_state_t *aflcc, char **envp) {
 
-      cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
-      cc_params[cc_par_cnt++] = "-fsanitize=address";
+  if (getenv("AFL_USE_ASAN") || aflcc->have_asan) {
 
-    } else if (getenv("AFL_USE_MSAN")) {
+    if (getenv("AFL_USE_MSAN") || aflcc->have_msan)
+      FATAL("ASAN and MSAN are mutually exclusive");
 
-      if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive");
+    if (getenv("AFL_HARDEN"))
+      FATAL("ASAN and AFL_HARDEN are mutually exclusive");
 
-      if (getenv("AFL_HARDEN"))
-        FATAL("MSAN and AFL_HARDEN are mutually exclusive");
+    if (aflcc->compiler_mode == GCC_PLUGIN && !aflcc->have_staticasan) {
 
-      cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
-      cc_params[cc_par_cnt++] = "-fsanitize=memory";
+      insert_param(aflcc, "-static-libasan");
 
     }
 
-  }
+    add_defs_fortify(aflcc, 0);
+    if (!aflcc->have_asan) {
 
-  if (getenv("AFL_USE_UBSAN")) {
+      insert_param(aflcc, "-fsanitize=address");
+      insert_param(aflcc, "-fno-common");
 
-    cc_params[cc_par_cnt++] = "-fsanitize=undefined";
-    cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error";
-    cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all";
-    cc_params[cc_par_cnt++] = "-fno-omit-frame-pointer";
+    }
 
-  }
+    aflcc->have_asan = 1;
+
+  } else if (getenv("AFL_USE_MSAN") || aflcc->have_msan) {
+
+    if (getenv("AFL_USE_ASAN") || aflcc->have_asan)
+      FATAL("ASAN and MSAN are mutually exclusive");
 
-  if (getenv("AFL_USE_TSAN")) {
+    if (getenv("AFL_HARDEN"))
+      FATAL("MSAN and AFL_HARDEN are mutually exclusive");
 
-    cc_params[cc_par_cnt++] = "-fsanitize=thread";
-    cc_params[cc_par_cnt++] = "-fno-omit-frame-pointer";
+    add_defs_fortify(aflcc, 0);
+    if (!aflcc->have_msan) { insert_param(aflcc, "-fsanitize=memory"); }
+    aflcc->have_msan = 1;
 
   }
 
-  if (getenv("AFL_USE_LSAN")) {
+  if (getenv("AFL_USE_UBSAN") || aflcc->have_ubsan) {
 
-    cc_params[cc_par_cnt++] = "-fsanitize=leak";
-    cc_params[cc_par_cnt++] = "-includesanitizer/lsan_interface.h";
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_LEAK_CHECK()={if(__lsan_do_recoverable_leak_check() > 0) "
-        "_exit(23); }";
-    cc_params[cc_par_cnt++] = "-D__AFL_LSAN_OFF()=__lsan_disable();";
-    cc_params[cc_par_cnt++] = "-D__AFL_LSAN_ON()=__lsan_enable();";
+    if (!aflcc->have_ubsan) {
 
-  }
+      insert_param(aflcc, "-fsanitize=undefined");
+      insert_param(aflcc, "-fsanitize-undefined-trap-on-error");
+      insert_param(aflcc, "-fno-sanitize-recover=all");
 
-  if (getenv("AFL_USE_CFISAN")) {
+    }
 
-    if (compiler_mode == GCC_PLUGIN || compiler_mode == GCC) {
+    if (!aflcc->have_fp) {
 
-      cc_params[cc_par_cnt++] = "-fcf-protection=full";
+      insert_param(aflcc, "-fno-omit-frame-pointer");
+      aflcc->have_fp = 1;
 
-    } else {
+    }
 
-      if (!lto_mode) {
+    aflcc->have_ubsan = 1;
 
-        uint32_t i = 0, found = 0;
-        while (envp[i] != NULL && !found)
-          if (strncmp("-flto", envp[i++], 5) == 0) found = 1;
-        if (!found) cc_params[cc_par_cnt++] = "-flto";
+  }
 
-      }
+  if (getenv("AFL_USE_TSAN") || aflcc->have_tsan) {
+
+    if (!aflcc->have_fp) {
 
-      cc_params[cc_par_cnt++] = "-fsanitize=cfi";
-      cc_params[cc_par_cnt++] = "-fvisibility=hidden";
+      insert_param(aflcc, "-fno-omit-frame-pointer");
+      aflcc->have_fp = 1;
 
     }
 
+    if (!aflcc->have_tsan) { insert_param(aflcc, "-fsanitize=thread"); }
+    aflcc->have_tsan = 1;
+
   }
 
-  if (!getenv("AFL_DONT_OPTIMIZE")) {
+  if (getenv("AFL_USE_LSAN") && !aflcc->have_lsan) {
 
-    cc_params[cc_par_cnt++] = "-g";
-    if (!have_o) cc_params[cc_par_cnt++] = "-O3";
-    if (!have_unroll) cc_params[cc_par_cnt++] = "-funroll-loops";
-    // if (strlen(march_opt) > 1 && march_opt[0] == '-')
-    //  cc_params[cc_par_cnt++] = march_opt;
+    insert_param(aflcc, "-fsanitize=leak");
+    add_defs_lsan_ctrl(aflcc);
+    aflcc->have_lsan = 1;
 
   }
 
-  if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
-      getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_ALL") ||
-      lto_mode) {
+  if (getenv("AFL_USE_CFISAN") || aflcc->have_cfisan) {
 
-    cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-bcmp";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
-    cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
+    if (aflcc->compiler_mode == GCC_PLUGIN || aflcc->compiler_mode == GCC) {
 
-  }
+      if (!aflcc->have_fcf) { insert_param(aflcc, "-fcf-protection=full"); }
 
-#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__
-  if (!have_c) cc_params[cc_par_cnt++] = "-lrt";
-#endif
+    } else {
 
-  cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
-  cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
+      if (!aflcc->lto_mode && !aflcc->have_flto) {
 
-  /* As documented in instrumentation/README.persistent_mode.md, deferred
-     forkserver initialization and persistent mode are not available in afl-gcc
-     and afl-clang. */
-  if (compiler_mode != GCC && compiler_mode != CLANG) {
+        uint32_t i = 0, found = 0;
+        while (envp[i] != NULL && !found) {
 
-    cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
+          if (strncmp("-flto", envp[i++], 5) == 0) found = 1;
 
-    /* When the user tries to use persistent or deferred forkserver modes by
-       appending a single line to the program, we want to reliably inject a
-       signature into the binary (to be picked up by afl-fuzz) and we want
-       to call a function from the runtime .o file. This is unnecessarily
-       painful for three reasons:
+        }
 
-       1) We need to convince the compiler not to optimize out the signature.
-          This is done with __attribute__((used)).
+        if (!found) { insert_param(aflcc, "-flto"); }
+        aflcc->have_flto = 1;
 
-       2) We need to convince the linker, when called with -Wl,--gc-sections,
-          not to do the same. This is done by forcing an assignment to a
-          'volatile' pointer.
+      }
 
-       3) We need to declare __afl_persistent_loop() in the global namespace,
-          but doing this within a method in a class is hard - :: and extern "C"
-          are forbidden and __attribute__((alias(...))) doesn't work. Hence the
-          __asm__ aliasing trick.
+      if (!aflcc->have_cfisan) { insert_param(aflcc, "-fsanitize=cfi"); }
 
-     */
+      if (!aflcc->have_hidden) {
 
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_FUZZ_INIT()="
-        "int __afl_sharedmem_fuzzing = 1;"
-        "extern unsigned int *__afl_fuzz_len;"
-        "extern unsigned char *__afl_fuzz_ptr;"
-        "unsigned char __afl_fuzz_alt[1048576];"
-        "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;";
+        insert_param(aflcc, "-fvisibility=hidden");
+        aflcc->have_hidden = 1;
 
-  }
+      }
 
-  if (plusplus_mode) {
+      aflcc->have_cfisan = 1;
 
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;"
-        "extern \"C\" void __afl_coverage_discard();"
-        "extern \"C\" void __afl_coverage_skip();"
-        "extern \"C\" void __afl_coverage_on();"
-        "extern \"C\" void __afl_coverage_off();";
+    }
 
-  } else {
+  }
 
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;"
-        "void __afl_coverage_discard();"
-        "void __afl_coverage_skip();"
-        "void __afl_coverage_on();"
-        "void __afl_coverage_off();";
+}
 
-  }
+/* Add params to enable LLVM SanCov, the native PCGUARD */
+void add_native_pcguard(aflcc_state_t *aflcc) {
 
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = "
-      "1;";
-  cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ON()=__afl_coverage_on()";
-  cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()";
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()";
-  cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()";
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : "
-      "__afl_fuzz_alt_ptr)";
-  cc_params[cc_par_cnt++] =
-      "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : "
-      "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff "
-      "? 0 : *__afl_fuzz_len)";
+  /* If there is a rust ASan runtime on the command line, it is likely we're
+   * linking from rust and adding native flags requiring the sanitizer runtime
+   * will trigger native clang to add yet another runtime, causing linker
+   * errors. For now we shouldn't add instrumentation here, we're linking
+   * anyway.
+   */
+  if (aflcc->have_rust_asanrt) { return; }
 
-  if (compiler_mode != GCC && compiler_mode != CLANG) {
+  /* If llvm-config doesn't figure out LLVM_MAJOR, just
+   go on anyway and let compiler complain if doesn't work. */
 
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_LOOP(_A)="
-        "({ static volatile const char *_B __attribute__((used,unused)); "
-        " _B = (const char*)\"" PERSIST_SIG
-        "\"; "
-        "extern int __afl_connected;"
-#ifdef __APPLE__
-        "__attribute__((visibility(\"default\"))) "
-        "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
-#else
-        "__attribute__((visibility(\"default\"))) "
-        "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
-#endif                                                        /* ^__APPLE__ */
-        // if afl is connected, we run _A times, else once.
-        "_L(__afl_connected ? _A : 1); })";
-
-    cc_params[cc_par_cnt++] =
-        "-D__AFL_INIT()="
-        "do { static volatile const char *_A __attribute__((used,unused)); "
-        " _A = (const char*)\"" DEFER_SIG
-        "\"; "
-#ifdef __APPLE__
-        "__attribute__((visibility(\"default\"))) "
-        "void _I(void) __asm__(\"___afl_manual_init\"); "
+#if LLVM_MAJOR > 0 && LLVM_MAJOR < 6
+  FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+");
 #else
-        "__attribute__((visibility(\"default\"))) "
-        "void _I(void) __asm__(\"__afl_manual_init\"); "
-#endif                                                        /* ^__APPLE__ */
-        "_I(); } while (0)";
+  #if LLVM_MAJOR == 0
+  WARNF(
+      "pcguard instrumentation with pc-table requires LLVM 6.0.1+"
+      " otherwise the compiler will fail");
+  #endif
+  if (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CODECOV) {
 
-  }
+    insert_param(aflcc,
+                 "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table");
 
-  if (x_set) {
+  } else {
 
-    cc_params[cc_par_cnt++] = "-x";
-    cc_params[cc_par_cnt++] = "none";
+    insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard,pc-table");
 
   }
 
-  // prevent unnecessary build errors
-  if (compiler_mode != GCC_PLUGIN && compiler_mode != GCC) {
+#endif
 
-    cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument";
+}
 
-  }
+/*
+  Add params to launch our optimized PCGUARD on request.
+  It will fallback to use the native PCGUARD in some cases. If so, plz
+  bear in mind that instrument_mode will be set to INSTRUMENT_LLVMNATIVE.
+*/
+void add_optimized_pcguard(aflcc_state_t *aflcc) {
 
-  if (preprocessor_only || have_c || !non_dash) {
+#if LLVM_MAJOR >= 13
+  #if defined __ANDROID__ || ANDROID
 
-    /* In the preprocessor_only case (-E), we are not actually compiling at
-       all but requesting the compiler to output preprocessed sources only.
-       We must not add the runtime in this case because the compiler will
-       simply output its binary content back on stdout, breaking any build
-       systems that rely on a separate source preprocessing step. */
-    cc_params[cc_par_cnt] = NULL;
-    return;
+  insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard");
+  aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-  }
+  #else
 
-#ifndef __ANDROID__
+  if (aflcc->have_instr_list) {
 
-  if (compiler_mode != GCC && compiler_mode != CLANG) {
+    if (!be_quiet)
+      SAYF(
+          "Using unoptimized trace-pc-guard, due usage of "
+          "-fsanitize-coverage-allow/denylist, you can use "
+          "AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST instead.\n");
 
-    switch (bit_mode) {
+    insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard");
+    aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-      case 0:
-        if (!shared_linking && !partial_linking)
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-compiler-rt.o", obj_path);
-        if (lto_mode)
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-llvm-rt-lto.o", obj_path);
-        break;
+  } else {
 
-      case 32:
-        if (!shared_linking && !partial_linking) {
+    /* Since LLVM_MAJOR >= 13 we use new pass manager */
+    #if LLVM_MAJOR < 16
+    insert_param(aflcc, "-fexperimental-new-pass-manager");
+    #endif
+    insert_object(aflcc, "SanitizerCoveragePCGUARD.so", "-fpass-plugin=%s", 0);
 
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-compiler-rt-32.o", obj_path);
-          if (access(cc_params[cc_par_cnt - 1], R_OK))
-            FATAL("-m32 is not supported by your compiler");
+  }
 
-        }
+  #endif  // defined __ANDROID__ || ANDROID
+#else     // LLVM_MAJOR < 13
+  #if LLVM_MAJOR >= 4
+
+  if (!be_quiet)
+    SAYF(
+        "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for "
+        "enhanced version.\n");
+  insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard");
+  aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
 
-        if (lto_mode) {
+  #else
 
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path);
-          if (access(cc_params[cc_par_cnt - 1], R_OK))
-            FATAL("-m32 is not supported by your compiler");
+  FATAL("pcguard instrumentation requires LLVM 4.0.1+");
 
-        }
+  #endif
+#endif
 
-        break;
+}
 
-      case 64:
-        if (!shared_linking && !partial_linking) {
+/** About -fsanitize -----END----- **/
 
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-compiler-rt-64.o", obj_path);
-          if (access(cc_params[cc_par_cnt - 1], R_OK))
-            FATAL("-m64 is not supported by your compiler");
+/** Linking behaviors -----BEGIN----- **/
 
-        }
+/*
+  Parse and process possible linking stage related args,
+  return PARAM_MISS if nothing matched.
+*/
+param_st parse_linking_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan,
+                              u8 *skip_next, char **argv) {
 
-        if (lto_mode) {
+  if (aflcc->lto_mode && !strncmp(cur_argv, "-flto=thin", 10)) {
 
-          cc_params[cc_par_cnt++] =
-              alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path);
-          if (access(cc_params[cc_par_cnt - 1], R_OK))
-            FATAL("-m64 is not supported by your compiler");
+    FATAL(
+        "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or "
+        "use afl-clang-fast!");
 
-        }
+  }
 
-        break;
+  param_st final_ = PARAM_MISS;
 
-    }
+  if (!strcmp(cur_argv, "-shared") || !strcmp(cur_argv, "-dynamiclib")) {
 
-  #if !defined(__APPLE__) && !defined(__sun)
-    if (!shared_linking && !partial_linking)
-      cc_params[cc_par_cnt++] =
-          alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path);
-  #endif
+    if (scan) {
 
-  #if defined(__APPLE__)
-    if (shared_linking || partial_linking) {
+      aflcc->shared_linking = 1;
+      final_ = PARAM_SCAN;
 
-      cc_params[cc_par_cnt++] = "-Wl,-U";
-      cc_params[cc_par_cnt++] = "-Wl,___afl_area_ptr";
-      cc_params[cc_par_cnt++] = "-Wl,-U";
-      cc_params[cc_par_cnt++] = "-Wl,___sanitizer_cov_trace_pc_guard_init";
+    } else {
+
+      final_ = PARAM_KEEP;
 
     }
 
-  #endif
+  } else if (!strcmp(cur_argv, "-Wl,-r") || !strcmp(cur_argv, "-Wl,-i") ||
 
-  }
+             !strcmp(cur_argv, "-Wl,--relocatable") ||
+             !strcmp(cur_argv, "-r") || !strcmp(cur_argv, "--relocatable")) {
 
-  #if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__
-  cc_params[cc_par_cnt++] = "-lrt";
-  #endif
+    if (scan) {
 
-#endif
+      aflcc->partial_linking = 1;
+      final_ = PARAM_SCAN;
 
-  cc_params[cc_par_cnt] = NULL;
+    } else {
 
-}
+      final_ = PARAM_KEEP;
 
-/* Main entry point */
+    }
 
-int main(int argc, char **argv, char **envp) {
+  } else if (!strncmp(cur_argv, "-fuse-ld=", 9) ||
 
-  int   i;
-  char *callname = argv[0], *ptr = NULL;
+             !strncmp(cur_argv, "--ld-path=", 10)) {
 
-  if (getenv("AFL_DEBUG")) {
+    if (scan) {
 
-    debug = 1;
-    if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG");
+      final_ = PARAM_SCAN;
 
-  } else if (getenv("AFL_QUIET"))
+    } else {
 
-    be_quiet = 1;
+      if (aflcc->lto_mode)
+        final_ = PARAM_DROP;
+      else
+        final_ = PARAM_KEEP;
 
-  if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") ||
-      getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") ||
-      getenv("AFL_LLVM_BLOCKLIST")) {
+    }
 
-    have_instr_env = 1;
+  } else if (!strcmp(cur_argv, "-Wl,-z,defs") ||
 
-  }
+             !strcmp(cur_argv, "-Wl,--no-undefined") ||
+             !strcmp(cur_argv, "-Wl,-no-undefined") ||
+             !strcmp(cur_argv, "--no-undefined") ||
+             strstr(cur_argv, "afl-compiler-rt") ||
+             strstr(cur_argv, "afl-llvm-rt")) {
 
-  if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) {
+    if (scan) {
 
-    passthrough = 1;
-    if (!debug) { be_quiet = 1; }
+      final_ = PARAM_SCAN;
 
-  }
+    } else {
 
-  if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1;
-  argvnull = (u8 *)argv[0];
-  check_environment_vars(envp);
+      final_ = PARAM_DROP;
 
-  if ((ptr = find_object("as", argv[0])) != NULL) {
+    }
 
-    have_gcc = 1;
-    ck_free(ptr);
+  } else if (!strcmp(cur_argv, "-z") || !strcmp(cur_argv, "-Wl,-z")) {
 
-  }
+    u8 *param = *(argv + 1);
+    if (param && (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs"))) {
 
-#if (LLVM_MAJOR >= 3)
+      *skip_next = 1;
 
-  if ((ptr = find_object("SanitizerCoverageLTO.so", argv[0])) != NULL) {
+      if (scan) {
 
-    have_lto = 1;
-    ck_free(ptr);
+        final_ = PARAM_SCAN;
 
-  }
+      } else {
 
-  if ((ptr = find_object("cmplog-routines-pass.so", argv[0])) != NULL) {
+        final_ = PARAM_DROP;
 
-    have_llvm = 1;
-    ck_free(ptr);
+      }
+
+    }
 
   }
 
-#endif
+  // Try to warn user for some unsupported cases
+  if (scan && final_ == PARAM_MISS) {
 
-#ifdef __ANDROID__
-  have_llvm = 1;
-#endif
+    u8 *ptr_ = NULL;
 
-  if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) {
+    if (!strcmp(cur_argv, "-Xlinker") && (ptr_ = *(argv + 1))) {
 
-    have_gcc_plugin = 1;
-    ck_free(ptr);
+      if (!strcmp(ptr_, "defs")) {
 
-  }
+        WARNF("'-Xlinker' 'defs' detected. This may result in a bad link.");
 
-#if (LLVM_MAJOR >= 3)
+      } else if (strstr(ptr_, "-no-undefined")) {
 
-  if (strncmp(callname, "afl-clang-fast", 14) == 0) {
+        WARNF(
+            "'-Xlinker' '%s' detected. The latter option may be dropped and "
+            "result in a bad link.",
+            ptr_);
 
-    compiler_mode = LLVM;
+      }
 
-  } else if (strncmp(callname, "afl-clang-lto", 13) == 0 ||
+    } else if (!strncmp(cur_argv, "-Wl,", 4) &&
 
-             strncmp(callname, "afl-lto", 7) == 0) {
+               (u8 *)strrchr(cur_argv, ',') != (cur_argv + 3)) {
 
-    compiler_mode = LTO;
+      ptr_ = cur_argv + 4;
 
-  } else
+      if (strstr(ptr_, "-shared") || strstr(ptr_, "-dynamiclib")) {
 
-#endif
-      if (strncmp(callname, "afl-gcc-fast", 12) == 0 ||
+        WARNF(
+            "'%s': multiple link options after '-Wl,' may break shared "
+            "linking.",
+            ptr_);
 
-          strncmp(callname, "afl-g++-fast", 12) == 0) {
+      }
 
-    compiler_mode = GCC_PLUGIN;
+      if (strstr(ptr_, "-r,") || strstr(ptr_, "-i,") || strstr(ptr_, ",-r") ||
+          strstr(ptr_, ",-i") || strstr(ptr_, "--relocatable")) {
 
-  } else if (strncmp(callname, "afl-gcc", 7) == 0 ||
+        WARNF(
+            "'%s': multiple link options after '-Wl,' may break partial "
+            "linking.",
+            ptr_);
 
-             strncmp(callname, "afl-g++", 7) == 0) {
+      }
 
-    compiler_mode = GCC;
+      if (strstr(ptr_, "defs") || strstr(ptr_, "no-undefined")) {
 
-  } else if (strcmp(callname, "afl-clang") == 0 ||
+        WARNF(
+            "'%s': multiple link options after '-Wl,' may enable report "
+            "unresolved symbol references and result in a bad link.",
+            ptr_);
 
-             strcmp(callname, "afl-clang++") == 0) {
+      }
 
-    compiler_mode = CLANG;
+    }
 
   }
 
-  if ((ptr = getenv("AFL_CC_COMPILER"))) {
+  if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv);
 
-    if (compiler_mode) {
+  return final_;
 
-      if (!be_quiet) {
+}
 
-        WARNF(
-            "\"AFL_CC_COMPILER\" is set but a specific compiler was already "
-            "selected by command line parameter or symlink, ignoring the "
-            "environment variable!");
+/* Add params to specify the linker used in LTO */
+void add_lto_linker(aflcc_state_t *aflcc) {
 
-      }
+  unsetenv("AFL_LD");
+  unsetenv("AFL_LD_CALLER");
 
-    } else {
+  u8 *ld_path = NULL;
+  if (getenv("AFL_REAL_LD")) {
 
-      if (strncasecmp(ptr, "LTO", 3) == 0) {
+    ld_path = strdup(getenv("AFL_REAL_LD"));
 
-        compiler_mode = LTO;
+  } else {
 
-      } else if (strncasecmp(ptr, "LLVM", 4) == 0) {
+    ld_path = strdup(AFL_REAL_LD);
 
-        compiler_mode = LLVM;
+  }
 
-      } else if (strncasecmp(ptr, "GCC_P", 5) == 0 ||
+  if (!ld_path || !*ld_path) {
 
-                 strncasecmp(ptr, "GCC-P", 5) == 0 ||
-                 strncasecmp(ptr, "GCCP", 4) == 0) {
+    if (ld_path) {
 
-        compiler_mode = GCC_PLUGIN;
+      // Freeing empty string
+      free(ld_path);
 
-      } else if (strcasecmp(ptr, "GCC") == 0) {
+    }
 
-        compiler_mode = GCC;
+    ld_path = strdup("ld.lld");
 
-      } else
+  }
 
-        FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr);
+  if (!ld_path) { PFATAL("Could not allocate mem for ld_path"); }
+#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12
+  insert_param(aflcc, alloc_printf("--ld-path=%s", ld_path));
+#else
+  insert_param(aflcc, alloc_printf("-fuse-ld=%s", ld_path));
+#endif
+  free(ld_path);
 
-    }
+}
 
-  }
+/* Add params to launch SanitizerCoverageLTO.so when linking  */
+void add_lto_passes(aflcc_state_t *aflcc) {
 
-  if (strcmp(callname, "afl-clang") == 0 ||
-      strcmp(callname, "afl-clang++") == 0) {
+#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15
+  // The NewPM implementation only works fully since LLVM 15.
+  insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,--load-pass-plugin=%s",
+                0);
+#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13
+  insert_param(aflcc, "-Wl,--lto-legacy-pass-manager");
+  insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0);
+#else
+  insert_param(aflcc, "-fno-experimental-new-pass-manager");
+  insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0);
+#endif
 
-    clang_mode = 1;
-    compiler_mode = CLANG;
+  insert_param(aflcc, "-Wl,--allow-multiple-definition");
 
-    if (strcmp(callname, "afl-clang++") == 0) { plusplus_mode = 1; }
+}
 
-  }
+/* Add params to link with libAFLDriver.a on request */
+static void add_aflpplib(aflcc_state_t *aflcc) {
 
-  for (i = 1; i < argc; i++) {
+  if (!aflcc->need_aflpplib) return;
 
-    if (strncmp(argv[i], "--afl", 5) == 0) {
+  u8 *afllib = find_object(aflcc, "libAFLDriver.a");
 
-      if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) {
+  if (!be_quiet) {
 
-        passthrough = 1;
-        argv[i] = "-g";  // we have to overwrite it, -g is always good
-        continue;
+    OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a");
 
-      }
+  }
 
-      if (compiler_mode && !be_quiet) {
+  if (!afllib) {
 
-        WARNF(
-            "--afl-... compiler mode supersedes the AFL_CC_COMPILER and "
-            "symlink compiler selection!");
+    if (!be_quiet) {
 
-      }
+      WARNF(
+          "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in "
+          "the flags - this will fail!");
 
-      ptr = argv[i];
-      ptr += 5;
-      while (*ptr == '-')
-        ptr++;
+    }
 
-      if (strncasecmp(ptr, "LTO", 3) == 0) {
+  } else {
 
-        compiler_mode = LTO;
+    insert_param(aflcc, afllib);
 
-      } else if (strncasecmp(ptr, "LLVM", 4) == 0) {
+#ifdef __APPLE__
+    insert_param(aflcc, "-Wl,-undefined");
+    insert_param(aflcc, "dynamic_lookup");
+#endif
 
-        compiler_mode = LLVM;
+  }
 
-      } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 ||
+}
 
-                 strncasecmp(ptr, "PC-GUARD", 8) == 0) {
+/* Add params to link with runtimes depended by our instrumentation */
+void add_runtime(aflcc_state_t *aflcc) {
 
-        compiler_mode = LLVM;
-        instrument_mode = INSTRUMENT_PCGUARD;
+  if (aflcc->preprocessor_only || aflcc->have_c || !aflcc->non_dash) {
 
-      } else if (strcasecmp(ptr, "INSTRIM") == 0 ||
+    /* In the preprocessor_only case (-E), we are not actually compiling at
+       all but requesting the compiler to output preprocessed sources only.
+       We must not add the runtime in this case because the compiler will
+       simply output its binary content back on stdout, breaking any build
+       systems that rely on a separate source preprocessing step. */
+    return;
 
-                 strcasecmp(ptr, "CFG") == 0) {
+  }
 
-        FATAL(
-            "InsTrim instrumentation was removed. Use a modern LLVM and "
-            "PCGUARD (default in afl-cc).\n");
+  if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC &&
+      !getenv("AFL_LLVM_NO_RPATH")) {
 
-      } else if (strcasecmp(ptr, "AFL") == 0 ||
+    // in case LLVM is installed not via a package manager or "make install"
+    // e.g. compiled download or compiled from github then its ./lib directory
+    // might not be in the search path. Add it if so.
+    const char *libdir = LLVM_LIBDIR;
+    if (aflcc->plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) &&
+        strncmp(libdir, "/lib", 4)) {
 
-                 strcasecmp(ptr, "CLASSIC") == 0) {
+#ifdef __APPLE__
+      u8 *libdir_opt = strdup("-Wl,-rpath," LLVM_LIBDIR);
+#else
+      u8 *libdir_opt = strdup("-Wl,-rpath=" LLVM_LIBDIR);
+#endif
+      insert_param(aflcc, libdir_opt);
 
-        compiler_mode = LLVM;
-        instrument_mode = INSTRUMENT_CLASSIC;
+    }
 
-      } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 ||
+  }
 
-                 strcasecmp(ptr, "NATIVE") == 0 ||
-                 strcasecmp(ptr, "LLVM-NATIVE") == 0) {
+#ifndef __ANDROID__
 
-        compiler_mode = LLVM;
-        instrument_mode = INSTRUMENT_LLVMNATIVE;
+  #define M32_ERR_MSG "-m32 is not supported by your compiler"
+  #define M64_ERR_MSG "-m64 is not supported by your compiler"
 
-      } else if (strncasecmp(ptr, "GCC_P", 5) == 0 ||
+  if (aflcc->compiler_mode != GCC && aflcc->compiler_mode != CLANG) {
 
-                 strncasecmp(ptr, "GCC-P", 5) == 0 ||
-                 strncasecmp(ptr, "GCCP", 4) == 0) {
+    switch (aflcc->bit_mode) {
 
-        compiler_mode = GCC_PLUGIN;
+      case 0:
+        if (!aflcc->shared_linking && !aflcc->partial_linking)
+          insert_object(aflcc, "afl-compiler-rt.o", 0, 0);
+        if (aflcc->lto_mode) insert_object(aflcc, "afl-llvm-rt-lto.o", 0, 0);
+        break;
 
-      } else if (strcasecmp(ptr, "GCC") == 0) {
+      case 32:
+        if (!aflcc->shared_linking && !aflcc->partial_linking)
+          insert_object(aflcc, "afl-compiler-rt-32.o", 0, M32_ERR_MSG);
+        if (aflcc->lto_mode)
+          insert_object(aflcc, "afl-llvm-rt-lto-32.o", 0, M32_ERR_MSG);
+        break;
 
-        compiler_mode = GCC;
+      case 64:
+        if (!aflcc->shared_linking && !aflcc->partial_linking)
+          insert_object(aflcc, "afl-compiler-rt-64.o", 0, M64_ERR_MSG);
+        if (aflcc->lto_mode)
+          insert_object(aflcc, "afl-llvm-rt-lto-64.o", 0, M64_ERR_MSG);
+        break;
 
-      } else if (strncasecmp(ptr, "CLANG", 5) == 0) {
+    }
 
-        compiler_mode = CLANG;
+  #if __AFL_CODE_COVERAGE
+    // Required for dladdr used in afl-compiler-rt.o
+    insert_param(aflcc, "-ldl");
+  #endif
 
-      } else
+  #if !defined(__APPLE__) && !defined(__sun)
+    if (!aflcc->shared_linking && !aflcc->partial_linking)
+      insert_object(aflcc, "dynamic_list.txt", "-Wl,--dynamic-list=%s", 0);
+  #endif
 
-        FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]);
+  #if defined(__APPLE__)
+    if (aflcc->shared_linking || aflcc->partial_linking) {
+
+      insert_param(aflcc, "-Wl,-U");
+      insert_param(aflcc, "-Wl,___afl_area_ptr");
+      insert_param(aflcc, "-Wl,-U");
+      insert_param(aflcc, "-Wl,___sanitizer_cov_trace_pc_guard_init");
 
     }
 
+  #endif
+
   }
 
-  if (strlen(callname) > 2 &&
-      (strncmp(callname + strlen(callname) - 2, "++", 2) == 0 ||
-       strstr(callname, "-g++") != NULL))
-    plusplus_mode = 1;
+#endif
 
-  if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") ||
-      getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) {
+  add_aflpplib(aflcc);
 
-    if (instrument_mode == 0)
-      instrument_mode = INSTRUMENT_PCGUARD;
-    else if (instrument_mode != INSTRUMENT_PCGUARD)
-      FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together");
+#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__
+  insert_param(aflcc, "-Wl,-lrt");
+#endif
 
-  }
+}
 
-  if (have_instr_env && getenv("AFL_DONT_OPTIMIZE") && !be_quiet) {
+/** Linking behaviors -----END----- **/
 
-    WARNF(
-        "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined "
-        "for file matching, only function matching!");
+/** Miscellaneous routines -----BEGIN----- **/
 
-  }
+/*
+  Add params to make compiler driver use our afl-as
+  as assembler, required by the vanilla instrumentation.
+*/
+void add_assembler(aflcc_state_t *aflcc) {
 
-  if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") ||
-      getenv("INSTRIM_LIB")) {
+  u8 *afl_as = find_object(aflcc, "afl-as");
 
-    FATAL(
-        "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD "
-        "(default in afl-cc).\n");
+  if (!afl_as) FATAL("Cannot find 'afl-as'.");
 
-  }
+  u8 *slash = strrchr(afl_as, '/');
+  if (slash) *slash = 0;
 
-  if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX;
-  if (getenv("AFL_LLVM_CALLER")) instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+    // Search for 'as' may be unreliable in some cases (see #2058)
+    // so use 'afl-as' instead, because 'as' is usually a symbolic link,
+    // or can be a renamed copy of 'afl-as' created in the same dir.
+    // Now we should verify if the compiler can find the 'as' we need.
 
-  if (getenv("AFL_LLVM_NGRAM_SIZE")) {
+#define AFL_AS_ERR "(should be a symlink or copy of 'afl-as')"
 
-    instrument_opt_mode |= INSTRUMENT_OPT_NGRAM;
-    ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE"));
-    if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX)
-      FATAL(
-          "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX "
-          "(%u)",
-          NGRAM_SIZE_MAX);
+  u8 *afl_as_dup = alloc_printf("%s/as", afl_as);
 
-  }
+  int fd = open(afl_as_dup, O_RDONLY);
+  if (fd < 0) { PFATAL("Unable to open '%s' " AFL_AS_ERR, afl_as_dup); }
 
-  if (getenv("AFL_LLVM_CTX_K")) {
+  struct stat st;
+  if (fstat(fd, &st) < 0) {
 
-    ctx_k = atoi(getenv("AFL_LLVM_CTX_K"));
-    if (ctx_k < 1 || ctx_k > CTX_MAX_K)
-      FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)",
-            CTX_MAX_K);
-    if (ctx_k == 1) {
+    PFATAL("Unable to fstat '%s' " AFL_AS_ERR, afl_as_dup);
 
-      setenv("AFL_LLVM_CALLER", "1", 1);
-      unsetenv("AFL_LLVM_CTX_K");
-      instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
+  }
 
-    } else {
+  u32 f_len = st.st_size;
 
-      instrument_opt_mode |= INSTRUMENT_OPT_CTX_K;
+  u8 *f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (f_data == MAP_FAILED) {
 
-    }
+    PFATAL("Unable to mmap file '%s' " AFL_AS_ERR, afl_as_dup);
 
   }
 
-  if (getenv("AFL_LLVM_INSTRUMENT")) {
+  close(fd);
 
-    u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;");
+  // "AFL_AS" is a const str passed to getenv in afl-as.c
+  if (!memmem(f_data, f_len, "AFL_AS", strlen("AFL_AS") + 1)) {
 
-    while (ptr2) {
+    FATAL(
+        "Looks like '%s' is not a valid symlink or copy of '%s/afl-as'. "
+        "It is a prerequisite to override system-wide 'as' for "
+        "instrumentation.",
+        afl_as_dup, afl_as);
 
-      if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 ||
-          strncasecmp(ptr2, "classic", strlen("classic")) == 0) {
+  }
 
-        if (instrument_mode == INSTRUMENT_LTO) {
+  if (munmap(f_data, f_len)) { PFATAL("unmap() failed"); }
 
-          instrument_mode = INSTRUMENT_CLASSIC;
-          lto_mode = 1;
+  ck_free(afl_as_dup);
 
-        } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) {
+#undef AFL_AS_ERR
 
-          instrument_mode = INSTRUMENT_AFL;
+  insert_param(aflcc, "-B");
+  insert_param(aflcc, afl_as);
 
-        } else {
+  if (aflcc->compiler_mode == CLANG) insert_param(aflcc, "-no-integrated-as");
 
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+}
 
-        }
+/* Add params to launch the gcc plugins for instrumentation. */
+void add_gcc_plugin(aflcc_state_t *aflcc) {
 
-      }
+  if (aflcc->cmplog_mode) {
 
-      if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 ||
-          strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) {
+    insert_object(aflcc, "afl-gcc-cmplog-pass.so", "-fplugin=%s", 0);
+    insert_object(aflcc, "afl-gcc-cmptrs-pass.so", "-fplugin=%s", 0);
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD)
-          instrument_mode = INSTRUMENT_PCGUARD;
-        else
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+  }
 
-      }
+  insert_object(aflcc, "afl-gcc-pass.so", "-fplugin=%s", 0);
 
-      if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 ||
-          strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0) {
+  insert_param(aflcc, "-fno-if-conversion");
+  insert_param(aflcc, "-fno-if-conversion2");
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE)
-          instrument_mode = INSTRUMENT_LLVMNATIVE;
-        else
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+}
 
-      }
+/* Add some miscellaneous params required by our instrumentation. */
+void add_misc_params(aflcc_state_t *aflcc) {
 
-      if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 ||
-          strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) {
+  if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") ||
+      getenv("AFL_LLVM_LAF_ALL") || getenv("AFL_LLVM_CMPLOG") ||
+      aflcc->lto_mode) {
+
+    insert_param(aflcc, "-fno-builtin-strcmp");
+    insert_param(aflcc, "-fno-builtin-strncmp");
+    insert_param(aflcc, "-fno-builtin-strcasecmp");
+    insert_param(aflcc, "-fno-builtin-strncasecmp");
+    insert_param(aflcc, "-fno-builtin-memcmp");
+    insert_param(aflcc, "-fno-builtin-bcmp");
+    insert_param(aflcc, "-fno-builtin-strstr");
+    insert_param(aflcc, "-fno-builtin-strcasestr");
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) {
+  }
 
-          instrument_mode = INSTRUMENT_LLVMNATIVE;
-          instrument_opt_mode |= INSTRUMENT_OPT_CODECOV;
+  if (!aflcc->have_pic) { insert_param(aflcc, "-fPIC"); }
 
-        } else {
+  if (getenv("AFL_HARDEN")) {
 
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+    insert_param(aflcc, "-fstack-protector-all");
 
-        }
+    if (!aflcc->fortify_set) add_defs_fortify(aflcc, 2);
 
-      }
+  }
 
-      if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 ||
-          strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) {
+  if (!getenv("AFL_DONT_OPTIMIZE")) {
 
-        FATAL(
-            "InsTrim instrumentation was removed. Use a modern LLVM and "
-            "PCGUARD (default in afl-cc).\n");
+    insert_param(aflcc, "-g");
+    if (!aflcc->have_o) insert_param(aflcc, "-O3");
+    if (!aflcc->have_unroll) insert_param(aflcc, "-funroll-loops");
+    // if (strlen(aflcc->march_opt) > 1 && aflcc->march_opt[0] == '-')
+    //     insert_param(aflcc, aflcc->march_opt);
 
-      }
+  }
 
-      if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) {
+  if (aflcc->x_set) {
 
-        lto_mode = 1;
-        if (!instrument_mode || instrument_mode == INSTRUMENT_LTO)
-          instrument_mode = INSTRUMENT_LTO;
-        else
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
+    insert_param(aflcc, "-x");
+    insert_param(aflcc, "none");
 
-      }
+  }
 
-      if (strcasecmp(ptr2, "gcc") == 0) {
+}
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_GCC)
-          instrument_mode = INSTRUMENT_GCC;
-        else if (instrument_mode != INSTRUMENT_GCC)
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
-        compiler_mode = GCC;
+/*
+  Parse and process a variety of args under our matching rules,
+  return PARAM_MISS if nothing matched.
+*/
+param_st parse_misc_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) {
 
-      }
+  param_st final_ = PARAM_MISS;
 
-      if (strcasecmp(ptr2, "clang") == 0) {
+// MACRO START
+#define SCAN_KEEP(dst, src) \
+  do {                      \
+                            \
+    if (scan) {             \
+                            \
+      dst = src;            \
+      final_ = PARAM_SCAN;  \
+                            \
+    } else {                \
+                            \
+      final_ = PARAM_KEEP;  \
+                            \
+    }                       \
+                            \
+  } while (0)
 
-        if (!instrument_mode || instrument_mode == INSTRUMENT_CLANG)
-          instrument_mode = INSTRUMENT_CLANG;
-        else if (instrument_mode != INSTRUMENT_CLANG)
-          FATAL("main instrumentation mode already set with %s",
-                instrument_mode_string[instrument_mode]);
-        compiler_mode = CLANG;
+  // MACRO END
 
-      }
+  if (!strncasecmp(cur_argv, "-fpic", 5)) {
 
-      if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0 ||
-          strncasecmp(ptr2, "kctx-", strlen("c-ctx-")) == 0 ||
-          strncasecmp(ptr2, "k-ctx-", strlen("k-ctx-")) == 0) {
+    SCAN_KEEP(aflcc->have_pic, 1);
 
-        u8 *ptr3 = ptr2;
-        while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9'))
-          ptr3++;
+  } else if (!strcmp(cur_argv, "-m32") ||
 
-        if (!*ptr3) {
+             !strcmp(cur_argv, "armv7a-linux-androideabi")) {
 
-          if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL)
-            FATAL(
-                "you must set the K-CTX K with (e.g. for value 2) "
-                "AFL_LLVM_INSTRUMENT=ctx-2");
+    SCAN_KEEP(aflcc->bit_mode, 32);
 
-        }
+  } else if (!strcmp(cur_argv, "-m64")) {
 
-        ctx_k = atoi(ptr3);
-        if (ctx_k < 1 || ctx_k > CTX_MAX_K)
-          FATAL(
-              "K-CTX instrumentation option must be between 1 and CTX_MAX_K "
-              "(%u)",
-              CTX_MAX_K);
+    SCAN_KEEP(aflcc->bit_mode, 64);
 
-        if (ctx_k == 1) {
+  } else if (strstr(cur_argv, "FORTIFY_SOURCE")) {
 
-          instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
-          setenv("AFL_LLVM_CALLER", "1", 1);
-          unsetenv("AFL_LLVM_CTX_K");
+    SCAN_KEEP(aflcc->fortify_set, 1);
 
-        } else {
+  } else if (!strcmp(cur_argv, "-x")) {
 
-          instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K);
-          u8 *ptr4 = alloc_printf("%u", ctx_k);
-          setenv("AFL_LLVM_CTX_K", ptr4, 1);
+    SCAN_KEEP(aflcc->x_set, 1);
 
-        }
+  } else if (!strcmp(cur_argv, "-E")) {
 
-      }
+    SCAN_KEEP(aflcc->preprocessor_only, 1);
 
-      if (strcasecmp(ptr2, "ctx") == 0) {
+  } else if (!strcmp(cur_argv, "--target=wasm32-wasi")) {
 
-        instrument_opt_mode |= INSTRUMENT_OPT_CTX;
-        setenv("AFL_LLVM_CTX", "1", 1);
+    SCAN_KEEP(aflcc->passthrough, 1);
 
-      }
+  } else if (!strcmp(cur_argv, "-c")) {
 
-      if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) {
+    SCAN_KEEP(aflcc->have_c, 1);
 
-        instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
-        setenv("AFL_LLVM_CALLER", "1", 1);
+  } else if (!strcmp(cur_argv, "-static-libasan")) {
 
-      }
+    SCAN_KEEP(aflcc->have_staticasan, 1);
 
-      if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) {
+  } else if (strstr(cur_argv, "librustc") && strstr(cur_argv, "_rt.asan.a")) {
 
-        u8 *ptr3 = ptr2 + strlen("ngram");
-        while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9'))
-          ptr3++;
+    SCAN_KEEP(aflcc->have_rust_asanrt, 1);
 
-        if (!*ptr3) {
+  } else if (!strcmp(cur_argv, "-fno-omit-frame-pointer")) {
 
-          if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL)
-            FATAL(
-                "you must set the NGRAM size with (e.g. for value 2) "
-                "AFL_LLVM_INSTRUMENT=ngram-2");
+    SCAN_KEEP(aflcc->have_fp, 1);
 
-        }
+  } else if (!strcmp(cur_argv, "-fvisibility=hidden")) {
 
-        ngram_size = atoi(ptr3);
-        if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX)
-          FATAL(
-              "NGRAM instrumentation option must be between 2 and "
-              "NGRAM_SIZE_MAX (%u)",
-              NGRAM_SIZE_MAX);
-        instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM);
-        u8 *ptr4 = alloc_printf("%u", ngram_size);
-        setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1);
+    SCAN_KEEP(aflcc->have_hidden, 1);
 
-      }
+  } else if (!strcmp(cur_argv, "-flto") || !strcmp(cur_argv, "-flto=full")) {
 
-      ptr2 = strtok(NULL, ":,;");
+    SCAN_KEEP(aflcc->have_flto, 1);
 
-    }
+  } else if (!strncmp(cur_argv, "-D_FORTIFY_SOURCE",
 
-  }
+                      strlen("-D_FORTIFY_SOURCE"))) {
 
-  if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
-      (instrument_opt_mode & INSTRUMENT_OPT_CALLER)) {
+    SCAN_KEEP(aflcc->have_fortify, 1);
 
-    FATAL("you cannot set CTX and CALLER together");
+  } else if (!strncmp(cur_argv, "-fcf-protection", strlen("-fcf-protection"))) {
 
-  }
+    SCAN_KEEP(aflcc->have_cfisan, 1);
 
-  if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
-      (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
+  } else if (!strncmp(cur_argv, "-O", 2)) {
 
-    FATAL("you cannot set CTX and K-CTX together");
+    SCAN_KEEP(aflcc->have_o, 1);
 
-  }
+  } else if (!strncmp(cur_argv, "-funroll-loop", 13)) {
 
-  if ((instrument_opt_mode & INSTRUMENT_OPT_CALLER) &&
-      (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
+    SCAN_KEEP(aflcc->have_unroll, 1);
 
-    FATAL("you cannot set CALLER and K-CTX together");
+  } else if (!strncmp(cur_argv, "--afl", 5)) {
 
-  }
-
-  if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT &&
-      (compiler_mode == LLVM || compiler_mode == UNSET)) {
+    if (scan)
+      final_ = PARAM_SCAN;
+    else
+      final_ = PARAM_DROP;
 
-    instrument_mode = INSTRUMENT_CLASSIC;
-    compiler_mode = LLVM;
+  } else if (!strncmp(cur_argv, "-fno-unroll", 11)) {
 
-  }
+    if (scan)
+      final_ = PARAM_SCAN;
+    else
+      final_ = PARAM_DROP;
 
-  if (!compiler_mode) {
+  } else if (!strcmp(cur_argv, "-pipe") && aflcc->compiler_mode == GCC_PLUGIN) {
 
-    // lto is not a default because outside of afl-cc RANLIB and AR have to
-    // be set to llvm versions so this would work
-    if (have_llvm)
-      compiler_mode = LLVM;
-    else if (have_gcc_plugin)
-      compiler_mode = GCC_PLUGIN;
-    else if (have_gcc)
-#ifdef __APPLE__
-      // on OSX clang masquerades as GCC
-      compiler_mode = CLANG;
-#else
-      compiler_mode = GCC;
-#endif
-    else if (have_lto)
-      compiler_mode = LTO;
+    if (scan)
+      final_ = PARAM_SCAN;
     else
-      FATAL("no compiler mode available");
+      final_ = PARAM_DROP;
 
-  }
+  } else if (!strncmp(cur_argv, "-stdlib=", 8) &&
 
-  if (compiler_mode == GCC) {
+             (aflcc->compiler_mode == GCC ||
+              aflcc->compiler_mode == GCC_PLUGIN)) {
 
-    if (clang_mode) {
+    if (scan) {
 
-      instrument_mode = INSTRUMENT_CLANG;
+      final_ = PARAM_SCAN;
 
     } else {
 
-      instrument_mode = INSTRUMENT_GCC;
+      if (!be_quiet) WARNF("Found '%s' - stripping!", cur_argv);
+      final_ = PARAM_DROP;
 
     }
 
-  }
+  } else if (cur_argv[0] != '-') {
 
-  if (compiler_mode == CLANG) {
+    /* It's a weak, loose pattern, with very different purpose
+     than others. We handle it at last, cautiously and robustly. */
 
-    instrument_mode = INSTRUMENT_CLANG;
-    setenv(CLANG_ENV_VAR, "1", 1);  // used by afl-as
+    if (scan && cur_argv[0] != '@')  // response file support
+      aflcc->non_dash = 1;
 
   }
 
+#undef SCAN_KEEP
+
+  if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv);
+
+  return final_;
+
+}
+
+/** Miscellaneous routines -----END----- **/
+
+/* Print help message on request */
+static void maybe_usage(aflcc_state_t *aflcc, int argc, char **argv) {
+
   if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) {
 
     printf("afl-cc" VERSION
@@ -2091,36 +2793,44 @@ int main(int argc, char **argv, char **envp) {
         "-------------|\n"
         "MODES:                                  NCC PERSIST DICT   LAF "
         "CMPLOG SELECT\n"
-        "  [LTO] llvm LTO:          %s%s\n"
-        "      PCGUARD              DEFAULT      yes yes     yes    yes yes "
-        "   yes\n"
-        "      CLASSIC                           yes yes     yes    yes yes "
-        "   yes\n"
-        "  [LLVM] llvm:             %s%s\n"
-        "      PCGUARD              %s      yes yes     module yes yes    "
+        "  [LLVM] LLVM:             %s%s\n"
+        "      PCGUARD              %s yes yes     module yes yes    "
         "yes\n"
-        "      CLASSIC              %s      no  yes     module yes yes    "
+        "      NATIVE               AVAILABLE    no  yes     no     no  "
+        "part.  yes\n"
+        "      CLASSIC              %s no  yes     module yes yes    "
         "yes\n"
         "        - NORMAL\n"
         "        - CALLER\n"
         "        - CTX\n"
         "        - NGRAM-{2-16}\n"
+        "  [LTO] LLVM LTO:          %s%s\n"
+        "      PCGUARD              DEFAULT      yes yes     yes    yes yes "
+        "   yes\n"
+        "      CLASSIC                           yes yes     yes    yes yes "
+        "   yes\n"
         "  [GCC_PLUGIN] gcc plugin: %s%s\n"
         "      CLASSIC              DEFAULT      no  yes     no     no  no     "
         "yes\n"
         "  [GCC/CLANG] simple gcc/clang: %s%s\n"
         "      CLASSIC              DEFAULT      no  no      no     no  no     "
         "no\n\n",
-        have_lto ? "AVAILABLE" : "unavailable!",
-        compiler_mode == LTO ? " [SELECTED]" : "",
-        have_llvm ? "AVAILABLE" : "unavailable!",
-        compiler_mode == LLVM ? " [SELECTED]" : "",
-        LLVM_MAJOR >= 7 ? "DEFAULT" : "       ",
-        LLVM_MAJOR >= 7 ? "       " : "DEFAULT",
-        have_gcc_plugin ? "AVAILABLE" : "unavailable!",
-        compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "",
-        have_gcc ? "AVAILABLE" : "unavailable!",
-        (compiler_mode == GCC || compiler_mode == CLANG) ? " [SELECTED]" : "");
+        aflcc->have_llvm ? "AVAILABLE   " : "unavailable!",
+        aflcc->compiler_mode == LLVM ? " [SELECTED]" : "",
+        aflcc->have_llvm ? "AVAILABLE   " : "unavailable!",
+        aflcc->have_llvm ? "AVAILABLE   " : "unavailable!",
+        aflcc->have_lto ? "AVAILABLE" : "unavailable!",
+        aflcc->compiler_mode == LTO ? " [SELECTED]" : "",
+        aflcc->have_gcc_plugin ? "AVAILABLE" : "unavailable!",
+        aflcc->compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "",
+        aflcc->have_gcc && aflcc->have_clang
+            ? "AVAILABLE"
+            : (aflcc->have_gcc
+                   ? "GCC ONLY "
+                   : (aflcc->have_clang ? "CLANG ONLY" : "unavailable!")),
+        (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG)
+            ? " [SELECTED]"
+            : "");
 
     SAYF(
         "Modes:\n"
@@ -2166,7 +2876,7 @@ int main(int argc, char **argv, char **envp) {
         "          (instrumentation/README.lto.md)\n"
         "  PERSIST: persistent mode support [code] (huge speed increase!)\n"
         "          (instrumentation/README.persistent_mode.md)\n"
-        "  DICT:   dictionary in the target [yes=automatic or llvm module "
+        "  DICT:   dictionary in the target [yes=automatic or LLVM module "
         "pass]\n"
         "          (instrumentation/README.lto.md + "
         "instrumentation/README.llvm.md)\n"
@@ -2194,7 +2904,7 @@ int main(int argc, char **argv, char **envp) {
           "  AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n"
           "  AFL_NO_BUILTIN: no builtins for string compare functions (for "
           "libtokencap.so)\n"
-          "  AFL_NOOP: behave like a normal compiler (to pass configure "
+          "  AFL_NOOPT: behave like a normal compiler (to pass configure "
           "tests)\n"
           "  AFL_PATH: path to instrumenting pass and runtime  "
           "(afl-compiler-rt.*o)\n"
@@ -2209,7 +2919,7 @@ int main(int argc, char **argv, char **envp) {
           "  AFL_USE_TSAN: activate thread sanitizer\n"
           "  AFL_USE_LSAN: activate leak-checker sanitizer\n");
 
-      if (have_gcc_plugin)
+      if (aflcc->have_gcc_plugin)
         SAYF(
             "\nGCC Plugin-specific environment variables:\n"
             "  AFL_GCC_CMPLOG: log operands of comparisons (RedQueen mutator)\n"
@@ -2225,7 +2935,7 @@ int main(int argc, char **argv, char **envp) {
   #define COUNTER_BEHAVIOUR \
     "  AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n"
 #endif
-      if (have_llvm)
+      if (aflcc->have_llvm)
         SAYF(
             "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment "
             "variables:\n"
@@ -2238,6 +2948,10 @@ int main(int argc, char **argv, char **envp) {
             "comparisons\n"
             "  AFL_LLVM_DICT2FILE_NO_MAIN: skip parsing main() for the "
             "dictionary\n"
+            "  AFL_LLVM_INJECTIONS_ALL: enables all injections hooking\n"
+            "  AFL_LLVM_INJECTIONS_SQL: enables SQL injections hooking\n"
+            "  AFL_LLVM_INJECTIONS_LDAP: enables LDAP injections hooking\n"
+            "  AFL_LLVM_INJECTIONS_XSS: enables XSS injections hooking\n"
             "  AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n"
             "  AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n"
             "  AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n"
@@ -2249,7 +2963,7 @@ int main(int argc, char **argv, char **envp) {
             "instrument allow/\n"
             "    deny listing (selective instrumentation)\n");
 
-      if (have_llvm)
+      if (aflcc->have_llvm)
         SAYF(
             "  AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen "
             "mutator)\n"
@@ -2263,10 +2977,12 @@ int main(int argc, char **argv, char **envp) {
             "  AFL_LLVM_CTX: use full context sensitive coverage (for "
             "CLASSIC)\n"
             "  AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for "
-            "CLASSIC)\n");
+            "CLASSIC)\n"
+            "  AFL_LLVM_NO_RPATH: disable rpath setting for custom LLVM "
+            "locations\n");
 
 #ifdef AFL_CLANG_FLTO
-      if (have_lto)
+      if (aflcc->have_lto)
         SAYF(
             "\nLTO/afl-clang-lto specific environment variables:\n"
             "  AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), "
@@ -2274,12 +2990,13 @@ int main(int argc, char **argv, char **envp) {
             "0x10000\n"
             "  AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding "
             "functions\n"
-            "    into this file\n"
+            "    into this file (LTO mode)\n"
+            "  AFL_LLVM_LTO_CALLER: activate CALLER/CTX instrumentation\n"
+            "  AFL_LLVM_LTO_CALLER_DEPTH: skip how many empty functions\n"
             "  AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a "
             "global var\n"
             "  AFL_LLVM_LTO_STARTID: from which ID to start counting from for "
-            "a "
-            "bb\n"
+            "a bb\n"
             "  AFL_REAL_LD: use this lld linker instead of the compiled in "
             "path\n"
             "  AFL_LLVM_LTO_SKIPINIT: don't inject initialization code "
@@ -2302,9 +3019,9 @@ int main(int argc, char **argv, char **envp) {
         "targets.\n\n");
 
 #if (LLVM_MAJOR >= 3)
-    if (have_lto)
+    if (aflcc->have_lto)
       SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO);
-    if (have_llvm)
+    if (aflcc->have_llvm)
       SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR,
            LLVM_BINDIR);
 #endif
@@ -2330,209 +3047,498 @@ int main(int argc, char **argv, char **envp) {
         "AFL_LLVM_CMPLOG and "
         "AFL_LLVM_DICT2FILE+AFL_LLVM_DICT2FILE_NO_MAIN.\n\n");
 
+    if (LLVM_MAJOR < 13) {
+
+      SAYF(
+          "Warning: It is highly recommended to use at least LLVM version 13 "
+          "(or better, higher) rather than %d!\n\n",
+          LLVM_MAJOR);
+
+    }
+
     exit(1);
 
   }
 
-  if (compiler_mode == LTO) {
+}
 
-    if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO ||
-        instrument_mode == INSTRUMENT_CFG ||
-        instrument_mode == INSTRUMENT_PCGUARD) {
+/*
+  Process params passed to afl-cc.
+
+  We have two working modes, *scan* and *non-scan*. In scan mode,
+  the main task is to set some variables in aflcc according to current argv[i],
+  while in non-scan mode, is to choose keep or drop current argv[i].
+
+  We have several matching routines being called sequentially in the while-loop,
+  and each of them try to parse and match current argv[i] according to their own
+  rules. If one miss match, the next will then take over. In non-scan mode, each
+  argv[i] mis-matched by all the routines will be kept.
+
+  These routines are:
+  1. parse_misc_params
+  2. parse_fsanitize
+  3. parse_linking_params
+  4. `if (*cur == '@') {...}`, i.e., parse response files
+*/
+static void process_params(aflcc_state_t *aflcc, u8 scan, u32 argc,
+                           char **argv) {
 
-      lto_mode = 1;
-      // force CFG
-      // if (!instrument_mode) {
+  // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]);
 
-      instrument_mode = INSTRUMENT_PCGUARD;
-      // ptr = instrument_mode_string[instrument_mode];
-      // }
+  /* Process the argument list. */
 
-    } else if (instrument_mode == INSTRUMENT_CLASSIC) {
+  u8 skip_next = 0;
+  while (--argc) {
 
-      lto_mode = 1;
+    u8 *cur = *(++argv);
 
-    } else {
+    if (skip_next > 0) {
 
-      if (!be_quiet) {
+      skip_next--;
+      continue;
 
-        WARNF("afl-clang-lto called with mode %s, using that mode instead",
-              instrument_mode_string[instrument_mode]);
+    }
+
+    if (PARAM_MISS != parse_misc_params(aflcc, cur, scan)) continue;
+
+    if (PARAM_MISS != parse_fsanitize(aflcc, cur, scan)) continue;
+
+    if (PARAM_MISS != parse_linking_params(aflcc, cur, scan, &skip_next, argv))
+      continue;
+
+    /* Response file support -----BEGIN-----
+      We have two choices - move everything to the command line or
+      rewrite the response files to temporary files and delete them
+      afterwards. We choose the first for easiness.
+      For clang, llvm::cl::ExpandResponseFiles does this, however it
+      only has C++ interface. And for gcc there is expandargv in libiberty,
+      written in C, but we can't simply copy-paste since its LGPL licensed.
+      So here we use an equivalent FSM as alternative, and try to be compatible
+      with the two above. See:
+        - https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
+        - driver::expand_at_files in gcc.git/gcc/gcc.c
+        - expandargv in gcc.git/libiberty/argv.c
+        - llvm-project.git/clang/tools/driver/driver.cpp
+        - ExpandResponseFiles in
+          llvm-project.git/llvm/lib/Support/CommandLine.cpp
+    */
+    if (*cur == '@') {
+
+      u8 *filename = cur + 1;
+      if (aflcc->debug) { DEBUGF("response file=%s\n", filename); }
+
+      // Check not found or empty? let the compiler complain if so.
+      FILE *f = fopen(filename, "r");
+      if (!f) {
+
+        if (!scan) insert_param(aflcc, cur);
+        continue;
 
       }
 
-    }
+      struct stat st;
+      if (fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size < 1) {
 
-  }
+        fclose(f);
+        if (!scan) insert_param(aflcc, cur);
+        continue;
 
-  if (instrument_mode == 0 && compiler_mode < GCC_PLUGIN) {
+      }
 
-#if LLVM_MAJOR >= 7
-  #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1)
-    if (have_instr_env) {
+      // Limit the number of response files, the max value
+      // just keep consistent with expandargv. Only do this in
+      // scan mode, and not touch rsp_count anymore in the next.
+      static u32 rsp_count = 2000;
+      if (scan) {
 
-      instrument_mode = INSTRUMENT_AFL;
-      if (!be_quiet) {
+        if (rsp_count == 0) FATAL("Too many response files provided!");
 
-        WARNF(
-            "Switching to classic instrumentation because "
-            "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1.");
+        --rsp_count;
 
       }
 
-    } else
+      // argc, argv acquired from this rsp file. Note that
+      // process_params ignores argv[0], we need to put a const "" here.
+      u32    argc_read = 1;
+      char **argv_read = ck_alloc(sizeof(char *));
+      argv_read[0] = "";
 
-  #endif
-      instrument_mode = INSTRUMENT_PCGUARD;
+      char *arg_buf = NULL;
+      u64   arg_len = 0;
 
-#else
-    instrument_mode = INSTRUMENT_AFL;
-#endif
+      enum fsm_state {
 
-  }
+        fsm_whitespace,    // whitespace seen so far
+        fsm_double_quote,  // have unpaired double quote
+        fsm_single_quote,  // have unpaired single quote
+        fsm_backslash,     // a backslash is seen with no unpaired quote
+        fsm_normal         // a normal char is seen
 
-  if (instrument_opt_mode && compiler_mode != LLVM)
-    FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode");
+      };
 
-  if (!instrument_opt_mode) {
+      // Workaround to append c to arg buffer, and append the buffer to argv
+#define ARG_ALLOC(c)                                             \
+  do {                                                           \
+                                                                 \
+    ++arg_len;                                                   \
+    arg_buf = ck_realloc(arg_buf, (arg_len + 1) * sizeof(char)); \
+    arg_buf[arg_len] = '\0';                                     \
+    arg_buf[arg_len - 1] = (char)c;                              \
+                                                                 \
+  } while (0)
 
-    if (lto_mode && instrument_mode == INSTRUMENT_CFG)
-      instrument_mode = INSTRUMENT_PCGUARD;
-    ptr = instrument_mode_string[instrument_mode];
+#define ARG_STORE()                                                \
+  do {                                                             \
+                                                                   \
+    ++argc_read;                                                   \
+    argv_read = ck_realloc(argv_read, argc_read * sizeof(char *)); \
+    argv_read[argc_read - 1] = arg_buf;                            \
+    arg_buf = NULL;                                                \
+    arg_len = 0;                                                   \
+                                                                   \
+  } while (0)
 
-  } else {
+      int cur_chr = (int)' ';  // init as whitespace, as a good start :)
+      enum fsm_state state_ = fsm_whitespace;
 
-    char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size);
-    char *ptr3 = alloc_printf(" + K-CTX-%u", ctx_k);
+      while (cur_chr != EOF) {
 
-    ptr = alloc_printf(
-        "%s%s%s%s%s", instrument_mode_string[instrument_mode],
-        (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "",
-        (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "",
-        (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "",
-        (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : "");
+        switch (state_) {
 
-    ck_free(ptr2);
-    ck_free(ptr3);
+          case fsm_whitespace:
 
-  }
+            if (arg_buf) {
 
-#ifndef AFL_CLANG_FLTO
-  if (lto_mode)
-    FATAL(
-        "instrumentation mode LTO specified but LLVM support not available "
-        "(requires LLVM 11 or higher)");
-#endif
+              ARG_STORE();
+              break;
 
-  if (instrument_opt_mode && instrument_opt_mode != INSTRUMENT_OPT_CODECOV &&
-      instrument_mode != INSTRUMENT_CLASSIC)
-    FATAL(
-        "CALLER, CTX and NGRAM instrumentation options can only be used with "
-        "the LLVM CLASSIC instrumentation mode.");
+            }
 
-  if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO"))
-    FATAL(
-        "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set "
-        "together");
+            if (isspace(cur_chr)) {
 
-#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1)
-  if (instrument_mode == INSTRUMENT_PCGUARD && have_instr_env) {
+              cur_chr = fgetc(f);
 
-    FATAL(
-        "Instrumentation type PCGUARD does not support "
-        "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead.");
+            } else if (cur_chr == (int)'\'') {
 
-  }
+              state_ = fsm_single_quote;
+              cur_chr = fgetc(f);
 
-#endif
+            } else if (cur_chr == (int)'"') {
 
-  u8 *ptr2;
+              state_ = fsm_double_quote;
+              cur_chr = fgetc(f);
 
-  if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/')
-    FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path");
+            } else if (cur_chr == (int)'\\') {
 
-  if ((isatty(2) && !be_quiet) || debug) {
+              state_ = fsm_backslash;
+              cur_chr = fgetc(f);
 
-    SAYF(cCYA
-         "afl-cc" VERSION cRST
-         " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n",
-         compiler_mode_string[compiler_mode], ptr);
+            } else {
 
-  }
+              state_ = fsm_normal;
 
-  if (!be_quiet && (compiler_mode == GCC || compiler_mode == CLANG)) {
+            }
 
-    WARNF(
-        "You are using outdated instrumentation, install LLVM and/or "
-        "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast "
-        "instead!");
+            break;
+
+          case fsm_normal:
+
+            if (isspace(cur_chr)) {
+
+              state_ = fsm_whitespace;
+
+            } else if (cur_chr == (int)'\'') {
+
+              state_ = fsm_single_quote;
+              cur_chr = fgetc(f);
+
+            } else if (cur_chr == (int)'\"') {
+
+              state_ = fsm_double_quote;
+              cur_chr = fgetc(f);
+
+            } else if (cur_chr == (int)'\\') {
+
+              state_ = fsm_backslash;
+              cur_chr = fgetc(f);
+
+            } else {
+
+              ARG_ALLOC(cur_chr);
+              cur_chr = fgetc(f);
+
+            }
+
+            break;
+
+          case fsm_backslash:
+
+            ARG_ALLOC(cur_chr);
+            cur_chr = fgetc(f);
+            state_ = fsm_normal;
+
+            break;
+
+          case fsm_single_quote:
+
+            if (cur_chr == (int)'\\') {
+
+              cur_chr = fgetc(f);
+              if (cur_chr == EOF) break;
+              ARG_ALLOC(cur_chr);
+
+            } else if (cur_chr == (int)'\'') {
+
+              state_ = fsm_normal;
+
+            } else {
+
+              ARG_ALLOC(cur_chr);
+
+            }
+
+            cur_chr = fgetc(f);
+            break;
+
+          case fsm_double_quote:
+
+            if (cur_chr == (int)'\\') {
+
+              cur_chr = fgetc(f);
+              if (cur_chr == EOF) break;
+              ARG_ALLOC(cur_chr);
+
+            } else if (cur_chr == (int)'"') {
+
+              state_ = fsm_normal;
+
+            } else {
+
+              ARG_ALLOC(cur_chr);
+
+            }
+
+            cur_chr = fgetc(f);
+            break;
+
+          default:
+            break;
+
+        }
+
+      }
+
+      if (arg_buf) { ARG_STORE(); }  // save the pending arg after EOF
+
+#undef ARG_ALLOC
+#undef ARG_STORE
+
+      if (argc_read > 1) { process_params(aflcc, scan, argc_read, argv_read); }
+
+      // We cannot free argv_read[] unless we don't need to keep any
+      // reference in cc_params. Never free argv[0], the const "".
+      if (scan) {
+
+        while (argc_read > 1)
+          ck_free(argv_read[--argc_read]);
+
+        ck_free(argv_read);
+
+      }
+
+      continue;
+
+    }                                /* Response file support -----END----- */
+
+    if (!scan) insert_param(aflcc, cur);
 
   }
 
-  if (debug) {
+}
 
-    DEBUGF("cd '%s';", getthecwd());
-    for (i = 0; i < argc; i++)
-      SAYF(" '%s'", argv[i]);
-    SAYF("\n");
-    fflush(stdout);
-    fflush(stderr);
+/* Process each of the existing argv, also add a few new args. */
+static void edit_params(aflcc_state_t *aflcc, u32 argc, char **argv,
+                        char **envp) {
+
+  add_real_argv0(aflcc);
+
+  // prevent unnecessary build errors
+  if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC) {
+
+    insert_param(aflcc, "-Wno-unused-command-line-argument");
 
   }
 
-  if (getenv("AFL_LLVM_LAF_ALL")) {
+  if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) {
 
-    setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1);
-    setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1);
-    setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1);
-    setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1);
+    add_assembler(aflcc);
 
   }
 
-  cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") ||
-                getenv("AFL_GCC_CMPLOG");
+  if (aflcc->compiler_mode == GCC_PLUGIN) { add_gcc_plugin(aflcc); }
 
-#if !defined(__ANDROID__) && !defined(ANDROID)
-  ptr = find_object("afl-compiler-rt.o", argv[0]);
+  if (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == LTO) {
 
-  if (!ptr) {
+    if (aflcc->lto_mode && aflcc->have_instr_env) {
 
-    FATAL(
-        "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH "
-        "environment variable.");
+      load_llvm_pass(aflcc, "afl-llvm-lto-instrumentlist.so");
 
-  }
+    }
 
-  if (debug) { DEBUGF("rt=%s obj_path=%s\n", ptr, obj_path); }
+    if (getenv("AFL_LLVM_DICT2FILE")) {
 
-  ck_free(ptr);
-#endif
+      load_llvm_pass(aflcc, "afl-llvm-dict2file.so");
 
-  edit_params(argc, argv, envp);
+    }
 
-  if (debug) {
+    // laf
+    if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
 
-    DEBUGF("cd '%s';", getthecwd());
-    for (i = 0; i < (s32)cc_par_cnt; i++)
-      SAYF(" '%s'", cc_params[i]);
-    SAYF("\n");
-    fflush(stdout);
-    fflush(stderr);
+      load_llvm_pass(aflcc, "split-switches-pass.so");
+
+    }
+
+    if (getenv("LAF_TRANSFORM_COMPARES") ||
+        getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
+
+      load_llvm_pass(aflcc, "compare-transform-pass.so");
+
+    }
+
+    if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") ||
+        getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) {
+
+      load_llvm_pass(aflcc, "split-compares-pass.so");
+
+    }
+
+    // /laf
+
+    if (aflcc->cmplog_mode) {
+
+      insert_param(aflcc, "-fno-inline");
+
+      load_llvm_pass(aflcc, "cmplog-switches-pass.so");
+      // reuse split switches from laf
+      load_llvm_pass(aflcc, "split-switches-pass.so");
+
+    }
+
+    // #if LLVM_MAJOR >= 13
+    //     // Use the old pass manager in LLVM 14 which the AFL++ passes still
+    //     use. insert_param(aflcc, "-flegacy-pass-manager");
+    // #endif
+
+    if (aflcc->lto_mode) {
+
+      insert_param(aflcc, aflcc->lto_flag);
+
+      if (!aflcc->have_c) {
+
+        add_lto_linker(aflcc);
+        add_lto_passes(aflcc);
+
+      }
+
+    } else {
+
+      if (aflcc->instrument_mode == INSTRUMENT_PCGUARD) {
+
+        add_optimized_pcguard(aflcc);
+
+      } else if (aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) {
+
+        add_native_pcguard(aflcc);
+
+      } else {
+
+        load_llvm_pass(aflcc, "afl-llvm-pass.so");
+
+      }
+
+    }
+
+    if (aflcc->cmplog_mode) {
+
+      load_llvm_pass(aflcc, "cmplog-instructions-pass.so");
+      load_llvm_pass(aflcc, "cmplog-routines-pass.so");
+
+    }
+
+    if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
+        getenv("AFL_LLVM_INJECTIONS_SQL") ||
+        getenv("AFL_LLVM_INJECTIONS_LDAP") ||
+        getenv("AFL_LLVM_INJECTIONS_XSS")) {
+
+      load_llvm_pass(aflcc, "injection-pass.so");
+
+    }
+
+    // insert_param(aflcc, "-Qunused-arguments");
 
   }
 
-  if (passthrough) {
+  /* Inspect the command line parameters. */
+
+  process_params(aflcc, 0, argc, argv);
+
+  add_sanitizers(aflcc, envp);
+
+  add_misc_params(aflcc);
+
+  add_defs_common(aflcc);
+  add_defs_selective_instr(aflcc);
+  add_defs_persistent_mode(aflcc);
+
+  add_runtime(aflcc);
+
+  insert_param(aflcc, NULL);
+
+}
+
+/* Main entry point */
+int main(int argc, char **argv, char **envp) {
+
+  aflcc_state_t *aflcc = malloc(sizeof(aflcc_state_t));
+  aflcc_state_init(aflcc, (u8 *)argv[0]);
+
+  check_environment_vars(envp);
+
+  find_built_deps(aflcc);
+
+  compiler_mode_by_callname(aflcc);
+  compiler_mode_by_environ(aflcc);
+  compiler_mode_by_cmdline(aflcc, argc, argv);
+
+  instrument_mode_by_environ(aflcc);
+
+  mode_final_checkout(aflcc, argc, argv);
+
+  process_params(aflcc, 1, argc, argv);
+
+  maybe_usage(aflcc, argc, argv);
+
+  mode_notification(aflcc);
+
+  if (aflcc->debug) debugf_args(argc, argv);
+
+  edit_params(aflcc, argc, argv, envp);
+
+  if (aflcc->debug)
+    debugf_args((s32)aflcc->cc_par_cnt, (char **)aflcc->cc_params);
+
+  if (aflcc->passthrough) {
 
-    argv[0] = cc_params[0];
-    execvp(cc_params[0], (char **)argv);
+    argv[0] = aflcc->cc_params[0];
+    execvp(aflcc->cc_params[0], (char **)argv);
 
   } else {
 
-    execvp(cc_params[0], (char **)cc_params);
+    execvp(aflcc->cc_params[0], (char **)aflcc->cc_params);
 
   }
 
-  FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
+  FATAL("Oops, failed to execute '%s' - check your PATH", aflcc->cc_params[0]);
 
   return 0;
 
diff --git a/src/afl-common.c b/src/afl-common.c
index a5c48e80..9a27824d 100644
--- a/src/afl-common.c
+++ b/src/afl-common.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
 #endif
 #include <string.h>
 #include <strings.h>
+#include <time.h>
 #include <math.h>
 #include <sys/mman.h>
 
@@ -58,6 +59,27 @@ u8  last_intr = 0;
   #define AFL_PATH "/usr/local/lib/afl/"
 #endif
 
+/* - Some BSD (i.e.: FreeBSD) offer the FAST clock source as
+ *   equivalent to Linux COARSE clock source. Aliasing COARSE to
+ *   FAST on such systems when COARSE is not already defined.
+ * - macOS has no support of CLOCK_MONOTONIC_COARSE clock type.
+ */
+#if defined(OS_DARWIN) || defined(OS_SUNOS) || defined(__APPLE__) || \
+    defined(__sun) || defined(__NetBSD__)
+  #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC
+#elif defined(OS_FREEBSD)
+  #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST
+#endif
+
+/* Convert seconds to milliseconds. */
+#define SEC_TO_MS(sec) ((sec) * 1000)
+/* Convert seconds to microseconds. */
+#define SEC_TO_US(sec) ((sec) * 1000000)
+/* Convert nanoseconds to milliseconds. */
+#define NS_TO_MS(ns) ((ns) / 1000000)
+/* Convert nanoseconds to microseconds. */
+#define NS_TO_US(ns) ((ns) / 1000)
+
 void *afl_memmem(const void *haystack, size_t haystacklen, const void *needle,
                  size_t needlelen) {
 
@@ -86,9 +108,10 @@ void set_sanitizer_defaults() {
   u8 *have_lsan_options = getenv("LSAN_OPTIONS");
   u8  have_san_options = 0;
   u8  default_options[1024] =
-      "detect_odr_violation=0:abort_on_error=1:symbolize=0:allocator_may_"
-      "return_null=1:handle_segv=0:handle_sigbus=0:handle_abort=0:handle_"
-      "sigfpe=0:handle_sigill=0:";
+      "detect_odr_violation=0:abort_on_error=1:symbolize=0:"
+      "allocator_may_return_null=1:handle_segv=0:handle_sigbus=0:"
+      "handle_abort=0:handle_sigfpe=0:handle_sigill=0:"
+      "detect_stack_use_after_return=0:check_initialization_order=0:";
 
   if (have_asan_options || have_ubsan_options || have_msan_options ||
       have_lsan_options) {
@@ -98,12 +121,27 @@ void set_sanitizer_defaults() {
   }
 
   /* LSAN does not support abort_on_error=1. (is this still true??) */
+  u8 should_detect_leaks = 0;
 
   if (!have_lsan_options) {
 
     u8 buf[2048] = "";
     if (!have_san_options) { strcpy(buf, default_options); }
-    strcat(buf, "exitcode=" STRINGIFY(LSAN_ERROR) ":fast_unwind_on_malloc=0:print_suppressions=0:detect_leaks=1:malloc_context_size=30:");
+    if (have_asan_options) {
+
+      if (NULL != strstr(have_asan_options, "detect_leaks=0")) {
+
+        strcat(buf, "exitcode=" STRINGIFY(LSAN_ERROR) ":fast_unwind_on_malloc=0:print_suppressions=0:detect_leaks=0:malloc_context_size=0:");
+
+      } else {
+
+        should_detect_leaks = 1;
+        strcat(buf, "exitcode=" STRINGIFY(LSAN_ERROR) ":fast_unwind_on_malloc=0:print_suppressions=0:detect_leaks=1:malloc_context_size=30:");
+
+      }
+
+    }
+
     setenv("LSAN_OPTIONS", buf, 1);
 
   }
@@ -112,7 +150,15 @@ void set_sanitizer_defaults() {
 
   if (!have_lsan_options) {
 
-    strcat(default_options, "detect_leaks=0:malloc_context_size=0:");
+    if (should_detect_leaks) {
+
+      strcat(default_options, "detect_leaks=1:malloc_context_size=30:");
+
+    } else {
+
+      strcat(default_options, "detect_leaks=0:malloc_context_size=0:");
+
+    }
 
   }
 
@@ -403,7 +449,7 @@ u8 *find_binary(u8 *fname) {
 
           FATAL(
               "Unexpected overflow when processing ENV. This should never "
-              "happend.");
+              "had happened.");
 
         }
 
@@ -949,14 +995,18 @@ void read_bitmap(u8 *fname, u8 *map, size_t len) {
 
 /* Get unix time in milliseconds */
 
-u64 get_cur_time(void) {
+inline u64 get_cur_time(void) {
+
+  struct timespec ts;
+  int             rc = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
+  if (rc == -1) {
 
-  struct timeval  tv;
-  struct timezone tz;
+    PFATAL("Failed to obtain timestamp (errno = %i: %s)\n", errno,
+           strerror(errno));
 
-  gettimeofday(&tv, &tz);
+  }
 
-  return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000);
+  return SEC_TO_MS((uint64_t)ts.tv_sec) + NS_TO_MS((uint64_t)ts.tv_nsec);
 
 }
 
@@ -964,12 +1014,16 @@ u64 get_cur_time(void) {
 
 u64 get_cur_time_us(void) {
 
-  struct timeval  tv;
-  struct timezone tz;
+  struct timespec ts;
+  int             rc = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
+  if (rc == -1) {
 
-  gettimeofday(&tv, &tz);
+    PFATAL("Failed to obtain timestamp (errno = %i: %s)\n", errno,
+           strerror(errno));
 
-  return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
+  }
+
+  return SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec);
 
 }
 
@@ -1298,6 +1352,35 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) {
 
 }
 
+/* Unsafe describe time delta as simple string.
+   Returns a pointer to buf for convenience. */
+
+u8 *u_simplestring_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) {
+
+  if (!event_ms) {
+
+    sprintf(buf, "00:00:00");
+
+  } else {
+
+    u64 delta;
+    s32 t_d, t_h, t_m, t_s;
+
+    delta = cur_ms - event_ms;
+
+    t_d = delta / 1000 / 60 / 60 / 24;
+    t_h = (delta / 1000 / 60 / 60) % 24;
+    t_m = (delta / 1000 / 60) % 60;
+    t_s = (delta / 1000) % 60;
+
+    sprintf(buf, "%d:%02d:%02d:%02d", t_d, t_h, t_m, t_s);
+
+  }
+
+  return buf;
+
+}
+
 /* Reads the map size from ENV */
 u32 get_map_size(void) {
 
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index 30c8901c..beb6bdeb 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -7,13 +7,13 @@
    Forkserver design by Jann Horn <jannhorn@googlemail.com>
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com> and
                         Dominik Maier <mail@dmnk.co>
 
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -27,6 +27,9 @@
  */
 
 #include "config.h"
+#ifdef AFL_PERSISTENT_RECORD
+  #include "afl-fuzz.h"
+#endif
 #include "types.h"
 #include "debug.h"
 #include "common.h"
@@ -129,6 +132,10 @@ nyx_plugin_handler_t *afl_load_libnyx_plugin(u8 *libnyx_binary) {
   plugin->nyx_remove_work_dir = dlsym(handle, "nyx_remove_work_dir");
   if (plugin->nyx_remove_work_dir == NULL) { goto fail; }
 
+  plugin->nyx_config_set_aux_buffer_size =
+      dlsym(handle, "nyx_config_set_aux_buffer_size");
+  if (plugin->nyx_config_set_aux_buffer_size == NULL) { goto fail; }
+
   OKF("libnyx plugin is ready!");
   return plugin;
 
@@ -160,6 +167,8 @@ void afl_nyx_runner_kill(afl_forkserver_t *fsrv) {
 
     }
 
+    if (fsrv->nyx_log_fd >= 0) { close(fsrv->nyx_log_fd); }
+
   }
 
 }
@@ -214,6 +223,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
   fsrv->nyx_bind_cpu_id = 0xFFFFFFFF;
   fsrv->nyx_use_tmp_workdir = false;
   fsrv->nyx_tmp_workdir_path = NULL;
+  fsrv->nyx_log_fd = -1;
 #endif
 
   // this structure needs default so we initialize it if this was not done
@@ -265,6 +275,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) {
   fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode;
   fsrv_to->crash_exitcode = from->crash_exitcode;
   fsrv_to->child_kill_signal = from->child_kill_signal;
+  fsrv_to->fsrv_kill_signal = from->fsrv_kill_signal;
   fsrv_to->debug = from->debug;
 
   // These are forkserver specific.
@@ -381,7 +392,7 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) {
   while (1) {
 
     uint32_t was_killed;
-    int      status;
+    u32      status;
 
     /* Wait for parent by reading from the pipe. Exit if read fails. */
 
@@ -516,7 +527,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
                     volatile u8 *stop_soon_p, u8 debug_child_output) {
 
   int   st_pipe[2], ctl_pipe[2];
-  s32   status;
+  u32   status;
   s32   rlen;
   char *ignore_autodict = getenv("AFL_NO_AUTODICT");
 
@@ -567,10 +578,26 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
     void *nyx_config = fsrv->nyx_handlers->nyx_config_load(fsrv->target_path);
 
     fsrv->nyx_handlers->nyx_config_set_workdir_path(nyx_config, workdir_path);
-    fsrv->nyx_handlers->nyx_config_set_input_buffer_size(nyx_config, MAX_FILE);
+    fsrv->nyx_handlers->nyx_config_set_input_buffer_size(nyx_config, fsrv->max_length);
     fsrv->nyx_handlers->nyx_config_set_input_buffer_write_protection(nyx_config,
                                                                      true);
 
+    char *nyx_log_path = getenv("AFL_NYX_LOG");
+    if (nyx_log_path) {
+
+      fsrv->nyx_log_fd =
+          open(nyx_log_path, O_CREAT | O_TRUNC | O_WRONLY, DEFAULT_PERMISSION);
+      if (fsrv->nyx_log_fd < 0) {
+
+        NYX_PRE_FATAL(fsrv, "AFL_NYX_LOG path could not be written");
+
+      }
+
+      fsrv->nyx_handlers->nyx_config_set_hprintf_fd(nyx_config,
+                                                    fsrv->nyx_log_fd);
+
+    }
+
     if (fsrv->nyx_standalone) {
 
       fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, StandAlone);
@@ -589,23 +616,42 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
     }
 
-    if (getenv("NYX_REUSE_SNAPSHOT") != NULL) {
+    if (getenv("AFL_NYX_AUX_SIZE") != NULL) {
+
+      fsrv->nyx_aux_string_len = atoi(getenv("AFL_NYX_AUX_SIZE"));
+
+      if (fsrv->nyx_handlers->nyx_config_set_aux_buffer_size(
+              nyx_config, fsrv->nyx_aux_string_len) != 1) {
+
+        NYX_PRE_FATAL(fsrv,
+                      "Invalid AFL_NYX_AUX_SIZE value set (must be a multiple "
+                      "of 4096) ...");
+
+      }
+
+    } else {
+
+      fsrv->nyx_aux_string_len = 0x1000;
+
+    }
+
+    if (getenv("AFL_NYX_REUSE_SNAPSHOT") != NULL) {
 
-      if (access(getenv("NYX_REUSE_SNAPSHOT"), F_OK) == -1) {
+      if (access(getenv("AFL_NYX_REUSE_SNAPSHOT"), F_OK) == -1) {
 
-        NYX_PRE_FATAL(fsrv, "NYX_REUSE_SNAPSHOT path does not exist");
+        NYX_PRE_FATAL(fsrv, "AFL_NYX_REUSE_SNAPSHOT path does not exist");
 
       }
 
       /* stupid sanity check to avoid passing an empty or invalid snapshot
        * directory */
       char *snapshot_file_path =
-          alloc_printf("%s/global.state", getenv("NYX_REUSE_SNAPSHOT"));
+          alloc_printf("%s/global.state", getenv("AFL_NYX_REUSE_SNAPSHOT"));
       if (access(snapshot_file_path, R_OK) == -1) {
 
-        NYX_PRE_FATAL(
-            fsrv,
-            "NYX_REUSE_SNAPSHOT path does not contain a valid Nyx snapshot");
+        NYX_PRE_FATAL(fsrv,
+                      "AFL_NYX_REUSE_SNAPSHOT path does not contain a valid "
+                      "Nyx snapshot");
 
       }
 
@@ -617,13 +663,14 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
       char *workdir_snapshot_path =
           alloc_printf("%s/workdir/snapshot", outdir_path_absolute);
       char *reuse_snapshot_path_real =
-          realpath(getenv("NYX_REUSE_SNAPSHOT"), NULL);
+          realpath(getenv("AFL_NYX_REUSE_SNAPSHOT"), NULL);
 
       if (strcmp(workdir_snapshot_path, reuse_snapshot_path_real) == 0) {
 
-        NYX_PRE_FATAL(fsrv,
-                      "NYX_REUSE_SNAPSHOT path is located in current workdir "
-                      "(use another output directory)");
+        NYX_PRE_FATAL(
+            fsrv,
+            "AFL_NYX_REUSE_SNAPSHOT path is located in current workdir "
+            "(use another output directory)");
 
       }
 
@@ -631,12 +678,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
       ck_free(workdir_snapshot_path);
 
       fsrv->nyx_handlers->nyx_config_set_reuse_snapshot_path(
-          nyx_config, getenv("NYX_REUSE_SNAPSHOT"));
+          nyx_config, getenv("AFL_NYX_REUSE_SNAPSHOT"));
 
     }
 
-    fsrv->nyx_runner =
-        fsrv->nyx_handlers->nyx_new(nyx_config, fsrv->nyx_bind_cpu_id);
+    fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new(nyx_config, fsrv->nyx_id);
 
     ck_free(workdir_path);
     ck_free(outdir_path_absolute);
@@ -653,27 +699,27 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
         fsrv->nyx_handlers->nyx_get_bitmap_buffer(fsrv->nyx_runner);
 
     fsrv->nyx_handlers->nyx_option_set_reload_mode(
-        fsrv->nyx_runner, getenv("NYX_DISABLE_SNAPSHOT_MODE") == NULL);
+        fsrv->nyx_runner, getenv("AFL_NYX_DISABLE_SNAPSHOT_MODE") == NULL);
     fsrv->nyx_handlers->nyx_option_apply(fsrv->nyx_runner);
 
     fsrv->nyx_handlers->nyx_option_set_timeout(fsrv->nyx_runner, 2, 0);
     fsrv->nyx_handlers->nyx_option_apply(fsrv->nyx_runner);
 
-    fsrv->nyx_aux_string = malloc(0x1000);
-    memset(fsrv->nyx_aux_string, 0, 0x1000);
+    fsrv->nyx_aux_string = malloc(fsrv->nyx_aux_string_len);
+    memset(fsrv->nyx_aux_string, 0, fsrv->nyx_aux_string_len);
 
     /* dry run */
     fsrv->nyx_handlers->nyx_set_afl_input(fsrv->nyx_runner, "INIT", 4);
     switch (fsrv->nyx_handlers->nyx_exec(fsrv->nyx_runner)) {
 
       case Abort:
-        NYX_PRE_FATAL(fsrv, "Error: Nyx abort occured...");
+        NYX_PRE_FATAL(fsrv, "Error: Nyx abort occurred...");
         break;
       case IoError:
         NYX_PRE_FATAL(fsrv, "Error: QEMU-Nyx has died...");
         break;
       case Error:
-        NYX_PRE_FATAL(fsrv, "Error: Nyx runtime error has occured...");
+        NYX_PRE_FATAL(fsrv, "Error: Nyx runtime error has occurred...");
         break;
       default:
         break;
@@ -681,7 +727,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
     }
 
     /* autodict in Nyx mode */
-    if (!ignore_autodict) {
+    if (!ignore_autodict && fsrv->add_extra_func) {
 
       char *x =
           alloc_printf("%s/workdir/dump/afl_autodict.txt", fsrv->out_dir_path);
@@ -974,75 +1020,68 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
   if (rlen == 4) {
 
-    if (!be_quiet) { OKF("All right - fork server is up."); }
+    /*
+     *  The new fork server model works like this:
+     *    Client: sends "AFLx" in little endian, with x being the forkserver
+     *            protocol version.
+     *    Server: replies with XOR of the message or exits with an error if it
+     *            is not a supported version.
+     *    Client: sends 32 bit of options and then sends all parameters of
+     *            the options, one after another, increasing by option number.
+     *            Ends with "AFLx".
+     *  After the initial protocol version confirmation the server does not
+     *  send any data anymore - except a future option requires this.
+     */
 
-    if (getenv("AFL_DEBUG")) {
+    if ((status & FS_NEW_ERROR) == FS_NEW_ERROR) {
 
-      ACTF("Extended forkserver functions received (%08x).", status);
+      report_error_and_exit(status & 0x0000ffff);
 
     }
 
-    if ((status & FS_OPT_ERROR) == FS_OPT_ERROR)
-      report_error_and_exit(FS_OPT_GET_ERROR(status));
+    if (status >= 0x41464c00 && status <= 0x41464cff) {
 
-    if ((status & FS_OPT_ENABLED) == FS_OPT_ENABLED) {
+      u32 version = status - 0x41464c00;
 
-      // workaround for recent AFL++ versions
-      if ((status & FS_OPT_OLD_AFLPP_WORKAROUND) == FS_OPT_OLD_AFLPP_WORKAROUND)
-        status = (status & 0xf0ffffff);
+      if (!version) {
 
-      if ((status & FS_OPT_NEWCMPLOG) == 0 && fsrv->cmplog_binary) {
+        FATAL(
+            "Fork server version is not assigned, this should not happen. "
+            "Recompile target.");
 
-        if (fsrv->qemu_mode || fsrv->frida_mode) {
+      } else if (version < FS_NEW_VERSION_MIN || version > FS_NEW_VERSION_MAX) {
 
-          report_error_and_exit(FS_ERROR_OLD_CMPLOG_QEMU);
-
-        } else {
-
-          report_error_and_exit(FS_ERROR_OLD_CMPLOG);
-
-        }
+        FATAL(
+            "Fork server version is not not supported.  Recompile the target.");
 
       }
 
-      if ((status & FS_OPT_SNAPSHOT) == FS_OPT_SNAPSHOT) {
+      u32 keep = status;
+      status ^= 0xffffffff;
+      if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
 
-        fsrv->snapshot = 1;
-        if (!be_quiet) { ACTF("Using SNAPSHOT feature."); }
+        FATAL("Writing to forkserver failed.");
 
       }
 
-      if ((status & FS_OPT_SHDMEM_FUZZ) == FS_OPT_SHDMEM_FUZZ) {
-
-        if (fsrv->support_shmem_fuzz) {
-
-          fsrv->use_shmem_fuzz = 1;
-          if (!be_quiet) { ACTF("Using SHARED MEMORY FUZZING feature."); }
-
-          if ((status & FS_OPT_AUTODICT) == 0 || ignore_autodict) {
-
-            u32 send_status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ);
-            if (write(fsrv->fsrv_ctl_fd, &send_status, 4) != 4) {
+      if (!be_quiet) {
 
-              FATAL("Writing to forkserver failed.");
-
-            }
+        OKF("All right - new fork server model v%u is up.", version);
 
-          }
+      }
 
-        } else {
+      rlen = read(fsrv->fsrv_st_fd, &status, 4);
 
-          FATAL(
-              "Target requested sharedmem fuzzing, but we failed to enable "
-              "it.");
+      if (getenv("AFL_DEBUG")) {
 
-        }
+        ACTF("Forkserver options received: (0x%08x)", status);
 
       }
 
-      if ((status & FS_OPT_MAPSIZE) == FS_OPT_MAPSIZE) {
+      if ((status & FS_NEW_OPT_MAPSIZE)) {
 
-        u32 tmp_map_size = FS_OPT_GET_MAPSIZE(status);
+        u32 tmp_map_size;
+        rlen = read(fsrv->fsrv_st_fd, &tmp_map_size, 4);
 
         if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; }
 
@@ -1059,7 +1098,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
           FATAL(
               "Target's coverage map size of %u is larger than the one this "
-              "AFL++ is set with (%u). Either set AFL_MAP_SIZE=%u and restart "
+              "AFL++ is set with (%u). Either set AFL_MAP_SIZE=%u and "
+              "restart "
               " afl-fuzz, or change MAP_SIZE_POW2 in config.h and recompile "
               "afl-fuzz",
               tmp_map_size, fsrv->map_size, tmp_map_size);
@@ -1068,106 +1108,326 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
         fsrv->map_size = tmp_map_size;
 
+      } else {
+
+        fsrv->real_map_size = fsrv->map_size = MAP_SIZE;
+
       }
 
-      if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) {
+      if (status & FS_NEW_OPT_SHDMEM_FUZZ) {
+
+        if (fsrv->support_shmem_fuzz) {
 
-        if (!ignore_autodict) {
+          fsrv->use_shmem_fuzz = 1;
+          if (!be_quiet) { ACTF("Using SHARED MEMORY FUZZING feature."); }
 
-          if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) {
+        } else {
 
-            // this is not afl-fuzz - or it is cmplog - we deny and return
-            if (fsrv->use_shmem_fuzz) {
+          FATAL(
+              "Target requested sharedmem fuzzing, but we failed to enable "
+              "it.");
 
-              status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ);
+        }
 
-            } else {
+      }
 
-              status = (FS_OPT_ENABLED);
+      if (status & FS_NEW_OPT_AUTODICT) {
 
-            }
+        // even if we do not need the dictionary we have to read it
 
-            if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
+        u32 dict_size;
+        if (read(fsrv->fsrv_st_fd, &dict_size, 4) != 4) {
 
-              FATAL("Writing to forkserver failed.");
+          FATAL("Reading from forkserver failed.");
 
-            }
+        }
 
-            return;
+        if (dict_size < 2 || dict_size > 0xffffff) {
 
-          }
+          FATAL("Dictionary has an illegal size: %d", dict_size);
+
+        }
+
+        u32 offset = 0, count = 0;
+        u8 *dict = ck_alloc(dict_size);
+        if (dict == NULL) {
+
+          FATAL("Could not allocate %u bytes of autodictionary memory",
+                dict_size);
+
+        }
 
-          if (!be_quiet) { ACTF("Using AUTODICT feature."); }
+        while (offset < dict_size) {
 
-          if (fsrv->use_shmem_fuzz) {
+          rlen = read(fsrv->fsrv_st_fd, dict + offset, dict_size - offset);
+          if (rlen > 0) {
 
-            status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ);
+            offset += rlen;
 
           } else {
 
-            status = (FS_OPT_ENABLED | FS_OPT_AUTODICT);
+            FATAL(
+                "Reading autodictionary fail at position %u with %u bytes "
+                "left.",
+                offset, dict_size - offset);
+
+          }
+
+        }
+
+        offset = 0;
+        while (offset < dict_size && (u8)dict[offset] + offset < dict_size) {
+
+          if (!ignore_autodict && fsrv->add_extra_func) {
+
+            fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1,
+                                 (u8)dict[offset]);
+            count++;
 
           }
 
-          if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
+          offset += (1 + dict[offset]);
+
+        }
+
+        if (!be_quiet && count) {
+
+          ACTF("Loaded %u autodictionary entries", count);
+
+        }
+
+        ck_free(dict);
+
+      }
+
+      u32 status2;
+      rlen = read(fsrv->fsrv_st_fd, &status2, 4);
+
+      if (status2 != keep) {
+
+        FATAL("Error in forkserver communication (%08x=>%08x)", keep, status2);
+
+      }
+
+    } else {
+
+      if (!fsrv->qemu_mode && !fsrv->cs_mode
+#ifdef __linux__
+          && !fsrv->nyx_mode
+#endif
+      ) {
+
+        WARNF(
+            "Old fork server model is used by the target, this still works "
+            "though.");
+
+      }
+
+      if (!be_quiet) { OKF("All right - old fork server is up."); }
+
+      if (getenv("AFL_DEBUG")) {
+
+        ACTF("Extended forkserver functions received (%08x).", status);
+
+      }
+
+      if ((status & FS_OPT_ERROR) == FS_OPT_ERROR)
+        report_error_and_exit(FS_OPT_GET_ERROR(status));
+
+      if (fsrv->cmplog_binary && !fsrv->qemu_mode) {
+
+        FATAL("Target was compiled with outdated CMPLOG, recompile it!\n");
+
+      }
+
+      if ((status & FS_OPT_ENABLED) == FS_OPT_ENABLED) {
+
+        // workaround for recent AFL++ versions
+        if ((status & FS_OPT_OLD_AFLPP_WORKAROUND) ==
+            FS_OPT_OLD_AFLPP_WORKAROUND)
+          status = (status & 0xf0ffffff);
+
+        if ((status & FS_OPT_NEWCMPLOG) == 0 && fsrv->cmplog_binary) {
+
+          if (fsrv->qemu_mode || fsrv->frida_mode) {
+
+            report_error_and_exit(FS_ERROR_OLD_CMPLOG_QEMU);
 
-            FATAL("Writing to forkserver failed.");
+          } else {
+
+            report_error_and_exit(FS_ERROR_OLD_CMPLOG);
 
           }
 
-          if (read(fsrv->fsrv_st_fd, &status, 4) != 4) {
+        }
+
+        if ((status & FS_OPT_SNAPSHOT) == FS_OPT_SNAPSHOT) {
+
+          fsrv->snapshot = 1;
+          if (!be_quiet) { ACTF("Using SNAPSHOT feature."); }
+
+        }
+
+        if ((status & FS_OPT_SHDMEM_FUZZ) == FS_OPT_SHDMEM_FUZZ) {
+
+          if (fsrv->support_shmem_fuzz) {
+
+            fsrv->use_shmem_fuzz = 1;
+            if (!be_quiet) { ACTF("Using SHARED MEMORY FUZZING feature."); }
+
+            if ((status & FS_OPT_AUTODICT) == 0 || ignore_autodict) {
+
+              u32 send_status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ);
+              if (write(fsrv->fsrv_ctl_fd, &send_status, 4) != 4) {
+
+                FATAL("Writing to forkserver failed.");
+
+              }
+
+            }
+
+          } else {
 
-            FATAL("Reading from forkserver failed.");
+            FATAL(
+                "Target requested sharedmem fuzzing, but we failed to enable "
+                "it.");
 
           }
 
-          if (status < 2 || (u32)status > 0xffffff) {
+        }
+
+        if ((status & FS_OPT_MAPSIZE) == FS_OPT_MAPSIZE) {
+
+          u32 tmp_map_size = FS_OPT_GET_MAPSIZE(status);
+
+          if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; }
+
+          fsrv->real_map_size = tmp_map_size;
+
+          if (tmp_map_size % 64) {
 
-            FATAL("Dictionary has an illegal size: %d", status);
+            tmp_map_size = (((tmp_map_size + 63) >> 6) << 6);
 
           }
 
-          u32 offset = 0, count = 0;
-          u32 len = status;
-          u8 *dict = ck_alloc(len);
-          if (dict == NULL) {
+          if (!be_quiet) { ACTF("Target map size: %u", fsrv->real_map_size); }
+          if (tmp_map_size > fsrv->map_size) {
 
-            FATAL("Could not allocate %u bytes of autodictionary memory", len);
+            FATAL(
+                "Target's coverage map size of %u is larger than the one this "
+                "AFL++ is set with (%u). Either set AFL_MAP_SIZE=%u and "
+                "restart "
+                " afl-fuzz, or change MAP_SIZE_POW2 in config.h and recompile "
+                "afl-fuzz",
+                tmp_map_size, fsrv->map_size, tmp_map_size);
 
           }
 
-          while (len != 0) {
+          fsrv->map_size = tmp_map_size;
 
-            rlen = read(fsrv->fsrv_st_fd, dict + offset, len);
-            if (rlen > 0) {
+        }
 
-              len -= rlen;
-              offset += rlen;
+        if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) {
+
+          if (!ignore_autodict) {
+
+            if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) {
+
+              // this is not afl-fuzz - or it is cmplog - we deny and return
+              if (fsrv->use_shmem_fuzz) {
+
+                status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ);
+
+              } else {
+
+                status = (FS_OPT_ENABLED);
+
+              }
+
+              if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
+
+                FATAL("Writing to forkserver failed.");
+
+              }
+
+              return;
+
+            }
+
+            if (!be_quiet) { ACTF("Using AUTODICT feature."); }
+
+            if (fsrv->use_shmem_fuzz) {
+
+              status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ);
 
             } else {
 
-              FATAL(
-                  "Reading autodictionary fail at position %u with %u bytes "
-                  "left.",
-                  offset, len);
+              status = (FS_OPT_ENABLED | FS_OPT_AUTODICT);
 
             }
 
-          }
+            if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
 
-          offset = 0;
-          while (offset < (u32)status &&
-                 (u8)dict[offset] + offset < (u32)status) {
+              FATAL("Writing to forkserver failed.");
 
-            fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1,
-                                 (u8)dict[offset]);
-            offset += (1 + dict[offset]);
-            count++;
+            }
 
-          }
+            if (read(fsrv->fsrv_st_fd, &status, 4) != 4) {
 
-          if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); }
-          ck_free(dict);
+              FATAL("Reading from forkserver failed.");
+
+            }
+
+            if (status < 2 || (u32)status > 0xffffff) {
+
+              FATAL("Dictionary has an illegal size: %d", status);
+
+            }
+
+            u32 offset = 0, count = 0;
+            u32 len = status;
+            u8 *dict = ck_alloc(len);
+            if (dict == NULL) {
+
+              FATAL("Could not allocate %u bytes of autodictionary memory",
+                    len);
+
+            }
+
+            while (len != 0) {
+
+              rlen = read(fsrv->fsrv_st_fd, dict + offset, len);
+              if (rlen > 0) {
+
+                len -= rlen;
+                offset += rlen;
+
+              } else {
+
+                FATAL(
+                    "Reading autodictionary fail at position %u with %u bytes "
+                    "left.",
+                    offset, len);
+
+              }
+
+            }
+
+            offset = 0;
+            while (offset < (u32)status &&
+                   (u8)dict[offset] + offset < (u32)status) {
+
+              fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1,
+                                   (u8)dict[offset]);
+              offset += (1 + dict[offset]);
+              count++;
+
+            }
+
+            if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); }
+            ck_free(dict);
+
+          }
 
         }
 
@@ -1226,7 +1486,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
            "    - Less likely, there is a horrible bug in the fuzzer. If other "
            "options\n"
-           "      fail, poke <afl-users@googlegroups.com> for troubleshooting "
+           "      fail, poke the Awesome Fuzzing Discord for troubleshooting "
            "tips.\n");
 
     } else {
@@ -1271,7 +1531,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
            "    - Less likely, there is a horrible bug in the fuzzer. If other "
            "options\n"
-           "      fail, poke <afl-users@googlegroups.com> for troubleshooting "
+           "      fail, poke the Awesome Fuzzing Discord for troubleshooting "
            "tips.\n",
            stringify_mem_size(val_buf, sizeof(val_buf), fsrv->mem_limit << 20),
            fsrv->mem_limit - 1);
@@ -1321,7 +1581,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
          "      Retry with setting AFL_MAP_SIZE=10000000.\n\n"
 
          "Otherwise there is a horrible bug in the fuzzer.\n"
-         "Poke <afl-users@googlegroups.com> for troubleshooting tips.\n");
+         "Poke the Awesome Fuzzing Discord for troubleshooting tips.\n");
 
   } else {
 
@@ -1370,7 +1630,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
         "    - Less likely, there is a horrible bug in the fuzzer. If other "
         "options\n"
-        "      fail, poke <afl-users@googlegroups.com> for troubleshooting "
+        "      fail, poke the Awesome Fuzzing Discord for troubleshooting "
         "tips.\n",
         getenv(DEFER_ENV_VAR)
             ? "    - You are using deferred forkserver, but __AFL_INIT() is "
@@ -1548,6 +1808,11 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
   u32 exec_ms;
   u32 write_value = fsrv->last_run_timed_out;
 
+#ifdef AFL_PERSISTENT_RECORD
+  fsrv_run_result_t retval = FSRV_RUN_OK;
+  char             *persistent_out_fmt;
+#endif
+
 #ifdef __linux__
   if (fsrv->nyx_mode) {
 
@@ -1577,11 +1842,13 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
       case Timeout:
         return FSRV_RUN_TMOUT;
       case InvalidWriteToPayload:
+        if (!!getenv("AFL_NYX_HANDLE_INVALID_WRITE")) { return FSRV_RUN_CRASH; }
+
         /* ??? */
         FATAL("FixMe: Nyx InvalidWriteToPayload handler is missing");
         break;
       case Abort:
-        FATAL("Error: Nyx abort occured...");
+        FATAL("Error: Nyx abort occurred...");
       case IoError:
         if (*stop_soon_p) {
 
@@ -1595,7 +1862,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
 
         break;
       case Error:
-        FATAL("Error: Nyx runtime error has occured...");
+        FATAL("Error: Nyx runtime error has occurred...");
         break;
 
     }
@@ -1610,7 +1877,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
      territory. */
 
 #ifdef __linux__
-  if (!fsrv->nyx_mode) {
+  if (likely(!fsrv->nyx_mode)) {
 
     memset(fsrv->trace_bits, 0, fsrv->map_size);
     MEM_BARRIER();
@@ -1680,7 +1947,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
 
   if (exec_ms > timeout) {
 
-    /* If there was no response from forkserver after timeout seconds,
+    /* If there was no response from forkserver after timeout milliseconds,
     we kill the child. The forkserver should inform us afterwards */
 
     s32 tmp_pid = fsrv->child_pid;
@@ -1747,6 +2014,18 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
   if (unlikely(fsrv->last_run_timed_out)) {
 
     fsrv->last_kill_signal = fsrv->child_kill_signal;
+
+#ifdef AFL_PERSISTENT_RECORD
+    if (unlikely(fsrv->persistent_record)) {
+
+      retval = FSRV_RUN_TMOUT;
+      persistent_out_fmt = "%s/hangs/RECORD:%06u,cnt:%06u%s%s";
+      goto store_persistent_record;
+
+    }
+
+#endif
+
     return FSRV_RUN_TMOUT;
 
   }
@@ -1768,48 +2047,66 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
           (fsrv->uses_crash_exitcode &&
            WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) {
 
+    /* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
+    fsrv->last_kill_signal =
+        WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
+
 #ifdef AFL_PERSISTENT_RECORD
     if (unlikely(fsrv->persistent_record)) {
 
-      char fn[PATH_MAX];
-      u32  i, writecnt = 0;
-      for (i = 0; i < fsrv->persistent_record; ++i) {
+      retval = FSRV_RUN_CRASH;
+      persistent_out_fmt = "%s/crashes/RECORD:%06u,cnt:%06u%s%s";
+      goto store_persistent_record;
 
-        u32 entry = (i + fsrv->persistent_record_idx) % fsrv->persistent_record;
-        u8 *data = fsrv->persistent_record_data[entry];
-        u32 len = fsrv->persistent_record_len[entry];
-        if (likely(len && data)) {
+    }
 
-          snprintf(fn, sizeof(fn), "%s/RECORD:%06u,cnt:%06u",
-                   fsrv->persistent_record_dir, fsrv->persistent_record_cnt,
-                   writecnt++);
-          int fd = open(fn, O_CREAT | O_TRUNC | O_WRONLY, 0644);
-          if (fd >= 0) {
+#endif
 
-            ck_write(fd, data, len, fn);
-            close(fd);
+    return FSRV_RUN_CRASH;
 
-          }
+  }
 
-        }
+  /* success :) */
+  return FSRV_RUN_OK;
 
-      }
+#ifdef AFL_PERSISTENT_RECORD
+store_persistent_record: {
+
+  char fn[PATH_MAX];
+  u32  i, writecnt = 0;
+  for (i = 0; i < fsrv->persistent_record; ++i) {
+
+    u32 entry = (i + fsrv->persistent_record_idx) % fsrv->persistent_record;
+    u8 *data = fsrv->persistent_record_data[entry];
+    u32 len = fsrv->persistent_record_len[entry];
+    if (likely(len && data)) {
+
+      snprintf(
+          fn, sizeof(fn), persistent_out_fmt, fsrv->persistent_record_dir,
+          fsrv->persistent_record_cnt, writecnt++,
+          ((afl_state_t *)(fsrv->afl_ptr))->file_extension ? "." : "",
+          ((afl_state_t *)(fsrv->afl_ptr))->file_extension
+              ? (const char *)((afl_state_t *)(fsrv->afl_ptr))->file_extension
+              : "");
+      int fd = open(fn, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+      if (fd >= 0) {
+
+        ck_write(fd, data, len, fn);
+        close(fd);
 
-      ++fsrv->persistent_record_cnt;
+      }
 
     }
 
-#endif
+  }
 
-    /* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
-    fsrv->last_kill_signal =
-        WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
-    return FSRV_RUN_CRASH;
+  ++fsrv->persistent_record_cnt;
 
-  }
+  return retval;
 
-  /* success :) */
-  return FSRV_RUN_OK;
+}
+
+#endif
 
 }
 
diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c
index 556bb5d1..03bc5d6c 100644
--- a/src/afl-fuzz-bitmap.c
+++ b/src/afl-fuzz-bitmap.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -459,6 +459,17 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
   if (unlikely(fault == FSRV_RUN_TMOUT && afl->afl_env.afl_ignore_timeouts)) {
 
+    if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) {
+
+      classify_counts(&afl->fsrv);
+      u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
+
+      // Saturated increment
+      if (likely(afl->n_fuzz[cksum % N_FUZZ_SIZE] < 0xFFFFFFFF))
+        afl->n_fuzz[cksum % N_FUZZ_SIZE]++;
+
+    }
+
     return 0;
 
   }
@@ -474,7 +485,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
   /* Generating a hash on every input is super expensive. Bad idea and should
      only be used for special schedules */
-  if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) {
+  if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) {
 
     classify_counts(&afl->fsrv);
     classified = 1;
@@ -516,23 +527,56 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
 #ifndef SIMPLE_FILES
 
-    queue_fn =
-        alloc_printf("%s/queue/id:%06u,%s", afl->out_dir, afl->queued_items,
-                     describe_op(afl, new_bits + is_timeout,
-                                 NAME_MAX - strlen("id:000000,")));
+    if (!afl->afl_env.afl_sha1_filenames) {
+
+      queue_fn = alloc_printf(
+          "%s/queue/id:%06u,%s%s%s", afl->out_dir, afl->queued_items,
+          describe_op(afl, new_bits + is_timeout,
+                      NAME_MAX - strlen("id:000000,")),
+          afl->file_extension ? "." : "",
+          afl->file_extension ? (const char *)afl->file_extension : "");
+
+    } else {
+
+      const char *hex = sha1_hex(mem, len);
+      queue_fn = alloc_printf(
+          "%s/queue/%s%s%s", afl->out_dir, hex, afl->file_extension ? "." : "",
+          afl->file_extension ? (const char *)afl->file_extension : "");
+      ck_free((char *)hex);
+
+    }
 
 #else
 
-    queue_fn =
-        alloc_printf("%s/queue/id_%06u", afl->out_dir, afl->queued_items);
+    queue_fn = alloc_printf(
+        "%s/queue/id_%06u", afl->out_dir, afl->queued_items,
+        afl->file_extension ? "." : "",
+        afl->file_extension ? (const char *)afl->file_extension : "");
 
 #endif                                                    /* ^!SIMPLE_FILES */
-    fd = open(queue_fn, O_WRONLY | O_CREAT | O_EXCL, DEFAULT_PERMISSION);
-    if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", queue_fn); }
-    ck_write(fd, mem, len, queue_fn);
-    close(fd);
+    fd = permissive_create(afl, queue_fn);
+    if (likely(fd >= 0)) {
+
+      ck_write(fd, mem, len, queue_fn);
+      close(fd);
+
+    }
+
     add_to_queue(afl, queue_fn, len, 0);
 
+    if (unlikely(afl->fuzz_mode) &&
+        likely(afl->switch_fuzz_mode && !afl->non_instrumented_mode)) {
+
+      if (afl->afl_env.afl_no_ui) {
+
+        ACTF("New coverage found, switching back to exploration mode.");
+
+      }
+
+      afl->fuzz_mode = 0;
+
+    }
+
 #ifdef INTROSPECTION
     if (afl->custom_mutators_count && afl->current_custom_fuzz) {
 
@@ -715,14 +759,29 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
 #ifndef SIMPLE_FILES
 
-      snprintf(fn, PATH_MAX, "%s/hangs/id:%06llu,%s", afl->out_dir,
-               afl->saved_hangs,
-               describe_op(afl, 0, NAME_MAX - strlen("id:000000,")));
+      if (!afl->afl_env.afl_sha1_filenames) {
+
+        snprintf(fn, PATH_MAX, "%s/hangs/id:%06llu,%s%s%s", afl->out_dir,
+                 afl->saved_hangs,
+                 describe_op(afl, 0, NAME_MAX - strlen("id:000000,")),
+                 afl->file_extension ? "." : "",
+                 afl->file_extension ? (const char *)afl->file_extension : "");
+
+      } else {
+
+        const char *hex = sha1_hex(mem, len);
+        snprintf(fn, PATH_MAX, "%s/hangs/%s%s%s", afl->out_dir, hex,
+                 afl->file_extension ? "." : "",
+                 afl->file_extension ? (const char *)afl->file_extension : "");
+        ck_free((char *)hex);
+
+      }
 
 #else
 
-      snprintf(fn, PATH_MAX, "%s/hangs/id_%06llu", afl->out_dir,
-               afl->saved_hangs);
+      snprintf(fn, PATH_MAX, "%s/hangs/id_%06llu%s%s", afl->out_dir,
+               afl->saved_hangs, afl->file_extension ? "." : "",
+               afl->file_extension ? (const char *)afl->file_extension : "");
 
 #endif                                                    /* ^!SIMPLE_FILES */
 
@@ -768,14 +827,30 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
 #ifndef SIMPLE_FILES
 
-      snprintf(fn, PATH_MAX, "%s/crashes/id:%06llu,sig:%02u,%s", afl->out_dir,
-               afl->saved_crashes, afl->fsrv.last_kill_signal,
-               describe_op(afl, 0, NAME_MAX - strlen("id:000000,sig:00,")));
+      if (!afl->afl_env.afl_sha1_filenames) {
+
+        snprintf(fn, PATH_MAX, "%s/crashes/id:%06llu,sig:%02u,%s%s%s",
+                 afl->out_dir, afl->saved_crashes, afl->fsrv.last_kill_signal,
+                 describe_op(afl, 0, NAME_MAX - strlen("id:000000,sig:00,")),
+                 afl->file_extension ? "." : "",
+                 afl->file_extension ? (const char *)afl->file_extension : "");
+
+      } else {
+
+        const char *hex = sha1_hex(mem, len);
+        snprintf(fn, PATH_MAX, "%s/crashes/%s%s%s", afl->out_dir, hex,
+                 afl->file_extension ? "." : "",
+                 afl->file_extension ? (const char *)afl->file_extension : "");
+        ck_free((char *)hex);
+
+      }
 
 #else
 
-      snprintf(fn, PATH_MAX, "%s/crashes/id_%06llu_%02u", afl->out_dir,
-               afl->saved_crashes, afl->fsrv.last_kill_signal);
+      snprintf(fn, PATH_MAX, "%s/crashes/id_%06llu_%02u%s%s", afl->out_dir,
+               afl->saved_crashes, afl->fsrv.last_kill_signal,
+               afl->file_extension ? "." : "",
+               afl->file_extension ? (const char *)afl->file_extension : "");
 
 #endif                                                    /* ^!SIMPLE_FILES */
 
@@ -838,10 +913,13 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
   /* If we're here, we apparently want to save the crash or hang
      test case, too. */
 
-  fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, DEFAULT_PERMISSION);
-  if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", fn); }
-  ck_write(fd, mem, len, fn);
-  close(fd);
+  fd = permissive_create(afl, fn);
+  if (fd >= 0) {
+
+    ck_write(fd, mem, len, fn);
+    close(fd);
+
+  }
 
 #ifdef __linux__
   if (afl->fsrv.nyx_mode && fault == FSRV_RUN_CRASH) {
@@ -853,7 +931,8 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
     if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", fn_log); }
 
     u32 nyx_aux_string_len = afl->fsrv.nyx_handlers->nyx_get_aux_string(
-        afl->fsrv.nyx_runner, afl->fsrv.nyx_aux_string, 0x1000);
+        afl->fsrv.nyx_runner, afl->fsrv.nyx_aux_string,
+        afl->fsrv.nyx_aux_string_len);
 
     ck_write(fd, afl->fsrv.nyx_aux_string, nyx_aux_string_len, fn_log);
     close(fd);
diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c
index 3e6432ca..8c48eb49 100644
--- a/src/afl-fuzz-cmplog.c
+++ b/src/afl-fuzz-cmplog.c
@@ -7,11 +7,11 @@
    Forkserver design by Jann Horn <jannhorn@googlemail.com>
 
    Now maintained by by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c
index f6de11ae..55b6be04 100644
--- a/src/afl-fuzz-extras.c
+++ b/src/afl-fuzz-extras.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -176,6 +176,8 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len,
     afl->extras =
         afl_realloc((void **)&afl->extras,
                     (afl->extras_cnt + 1) * sizeof(struct extra_data));
+    char *hexdigits = "0123456789abcdef";
+
     if (unlikely(!afl->extras)) { PFATAL("alloc"); }
 
     wptr = afl->extras[afl->extras_cnt].data = ck_alloc(rptr - lptr);
@@ -184,13 +186,12 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len,
 
     while (*lptr) {
 
-      char *hexdigits = "0123456789abcdef";
-
       switch (*lptr) {
 
         case 1 ... 31:
         case 128 ... 255:
           WARNF("Non-printable characters in line %u.", cur_line);
+          ++lptr;
           continue;
           break;
 
@@ -741,8 +742,11 @@ void save_auto(afl_state_t *afl) {
 
   for (i = 0; i < MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt); ++i) {
 
-    u8 *fn =
-        alloc_printf("%s/queue/.state/auto_extras/auto_%06u", afl->out_dir, i);
+    u8 *fn = alloc_printf(
+        "%s/queue/.state/auto_extras/auto_%06u%s%s", afl->out_dir, i,
+        afl->file_extension ? "." : "",
+        afl->file_extension ? (const char *)afl->file_extension : "");
+
     s32 fd;
 
     fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_PERMISSION);
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index baf56a5f..7310e49f 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -124,6 +124,9 @@ void bind_to_free_cpu(afl_state_t *afl) {
     }
 
     WARNF("Not binding to a CPU core (AFL_NO_AFFINITY set).");
+  #ifdef __linux__
+    if (afl->fsrv.nyx_mode) { afl->fsrv.nyx_bind_cpu_id = 0; }
+  #endif
     return;
 
   }
@@ -151,6 +154,9 @@ void bind_to_free_cpu(afl_state_t *afl) {
     } else {
 
       OKF("CPU binding request using -b %d successful.", afl->cpu_to_bind);
+  #ifdef __linux__
+      if (afl->fsrv.nyx_mode) { afl->fsrv.nyx_bind_cpu_id = afl->cpu_to_bind; }
+  #endif
 
     }
 
@@ -453,6 +459,24 @@ void bind_to_free_cpu(afl_state_t *afl) {
 
 #endif                                                     /* HAVE_AFFINITY */
 
+/* transforms spaces in a string to underscores (inplace) */
+
+static void no_spaces(u8 *string) {
+
+  if (string) {
+
+    u8 *ptr = string;
+    while (*ptr != 0) {
+
+      if (*ptr == ' ') { *ptr = '_'; }
+      ++ptr;
+
+    }
+
+  }
+
+}
+
 /* Shuffle an array of pointers. Might be slightly biased. */
 
 static void shuffle_ptrs(afl_state_t *afl, void **ptrs, u32 cnt) {
@@ -553,6 +577,8 @@ void read_foreign_testcases(afl_state_t *afl, int first) {
       afl->stage_cur = 0;
       afl->stage_max = 0;
 
+      show_stats(afl);
+
       for (i = 0; i < (u32)nl_cnt; ++i) {
 
         struct stat st;
@@ -631,7 +657,12 @@ void read_foreign_testcases(afl_state_t *afl, int first) {
         munmap(mem, st.st_size);
         close(fd);
 
-        if (st.st_mtime > mtime_max) mtime_max = st.st_mtime;
+        if (st.st_mtime > mtime_max) {
+
+          mtime_max = st.st_mtime;
+          show_stats(afl);
+
+        }
 
       }
 
@@ -908,6 +939,14 @@ void perform_dry_run(afl_state_t *afl) {
 
     res = calibrate_case(afl, q, use_mem, 0, 1);
 
+    /* For AFLFast schedules we update the queue entry */
+    if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE) &&
+        likely(q->exec_cksum)) {
+
+      q->n_fuzz_entry = q->exec_cksum % N_FUZZ_SIZE;
+
+    }
+
     if (afl->stop_soon) { return; }
 
     if (res == afl->crash_mode || res == FSRV_RUN_NOBITS) {
@@ -942,6 +981,7 @@ void perform_dry_run(afl_state_t *afl) {
           if (!q->was_fuzzed) {
 
             q->was_fuzzed = 1;
+            afl->reinit_table = 1;
             --afl->pending_not_fuzzed;
             --afl->active_items;
 
@@ -951,19 +991,48 @@ void perform_dry_run(afl_state_t *afl) {
 
         } else {
 
-          SAYF("\n" cLRD "[-] " cRST
-               "The program took more than %u ms to process one of the initial "
-               "test cases.\n"
-               "    This is bad news; raising the limit with the -t option is "
-               "possible, but\n"
-               "    will probably make the fuzzing process extremely slow.\n\n"
+          static int say_once = 0;
+
+          if (!say_once) {
+
+            SAYF(
+                "\n" cLRD "[-] " cRST
+                "The program took more than %u ms to process one of the "
+                "initial "
+                "test cases.\n"
+                "    This is bad news; raising the limit with the -t option is "
+                "possible, but\n"
+                "    will probably make the fuzzing process extremely slow.\n\n"
+
+                "    If this test case is just a fluke, the other option is to "
+                "just avoid it\n"
+                "    altogether, and find one that is less of a CPU hog.\n",
+                afl->fsrv.exec_tmout);
+
+            if (!afl->afl_env.afl_ignore_seed_problems) {
+
+              FATAL("Test case '%s' results in a timeout", fn);
 
-               "    If this test case is just a fluke, the other option is to "
-               "just avoid it\n"
-               "    altogether, and find one that is less of a CPU hog.\n",
-               afl->fsrv.exec_tmout);
+            }
 
-          FATAL("Test case '%s' results in a timeout", fn);
+            say_once = 1;
+
+          }
+
+          if (!q->was_fuzzed) {
+
+            q->was_fuzzed = 1;
+            afl->reinit_table = 1;
+            --afl->pending_not_fuzzed;
+            --afl->active_items;
+
+          }
+
+          q->disabled = 1;
+          q->perf_score = 0;
+
+          WARNF("Test case '%s' results in a timeout, skipping", fn);
+          break;
 
         }
 
@@ -1012,7 +1081,7 @@ void perform_dry_run(afl_state_t *afl) {
 
                "    - Least likely, there is a horrible bug in the fuzzer. If "
                "other options\n"
-               "      fail, poke <afl-users@googlegroups.com> for "
+               "      fail, poke the Awesome Fuzzing Discord for "
                "troubleshooting tips.\n",
                stringify_mem_size(val_buf, sizeof(val_buf),
                                   afl->fsrv.mem_limit << 20),
@@ -1041,7 +1110,7 @@ void perform_dry_run(afl_state_t *afl) {
 
                "    - Least likely, there is a horrible bug in the fuzzer. If "
                "other options\n"
-               "      fail, poke <afl-users@googlegroups.com> for "
+               "      fail, poke the Awesome Fuzzing Discord for "
                "troubleshooting tips.\n");
 
         }
@@ -1058,7 +1127,19 @@ void perform_dry_run(afl_state_t *afl) {
 
         } else {
 
-          WARNF("Test case '%s' results in a crash, skipping", fn);
+          if (afl->afl_env.afl_crashing_seeds_as_new_crash) {
+
+            WARNF(
+                "Test case '%s' results in a crash, "
+                "as AFL_CRASHING_SEEDS_AS_NEW_CRASH is set, "
+                "saving as a new crash",
+                fn);
+
+          } else {
+
+            WARNF("Test case '%s' results in a crash, skipping", fn);
+
+          }
 
         }
 
@@ -1073,41 +1154,118 @@ void perform_dry_run(afl_state_t *afl) {
         if (!q->was_fuzzed) {
 
           q->was_fuzzed = 1;
+          afl->reinit_table = 1;
           --afl->pending_not_fuzzed;
           --afl->active_items;
 
         }
 
-        q->disabled = 1;
-        q->perf_score = 0;
+        /* Crashing seeds will be regarded as new crashes on startup */
+        if (afl->afl_env.afl_crashing_seeds_as_new_crash) {
 
-        u32 i = 0;
-        while (unlikely(i < afl->queued_items && afl->queue_buf[i] &&
-                        afl->queue_buf[i]->disabled)) {
+          ++afl->total_crashes;
 
-          ++i;
+          if (likely(!afl->non_instrumented_mode)) {
 
-        }
+            classify_counts(&afl->fsrv);
+
+            simplify_trace(afl, afl->fsrv.trace_bits);
+
+            if (!has_new_bits(afl, afl->virgin_crash)) { break; }
+
+          }
+
+          if (unlikely(!afl->saved_crashes) &&
+              (afl->afl_env.afl_no_crash_readme != 1)) {
 
-        if (i < afl->queued_items && afl->queue_buf[i]) {
+            write_crash_readme(afl);
 
-          afl->queue = afl->queue_buf[i];
+          }
+
+          u8  crash_fn[PATH_MAX];
+          u8 *use_name = strstr(q->fname, ",orig:");
+
+          afl->stage_name = "dry_run";
+          afl->stage_short = "dry_run";
+
+#ifndef SIMPLE_FILES
+
+          if (!afl->afl_env.afl_sha1_filenames) {
+
+            snprintf(
+                crash_fn, PATH_MAX, "%s/crashes/id:%06llu,sig:%02u,%s%s%s%s",
+                afl->out_dir, afl->saved_crashes, afl->fsrv.last_kill_signal,
+                describe_op(
+                    afl, 0,
+                    NAME_MAX - strlen("id:000000,sig:00,") - strlen(use_name)),
+                use_name, afl->file_extension ? "." : "",
+                afl->file_extension ? (const char *)afl->file_extension : "");
+
+          } else {
+
+            const char *hex = sha1_hex(use_mem, read_len);
+            snprintf(
+                crash_fn, PATH_MAX, "%s/crashes/%s%s%s", afl->out_dir, hex,
+                afl->file_extension ? "." : "",
+                afl->file_extension ? (const char *)afl->file_extension : "");
+            ck_free((char *)hex);
+
+          }
+
+#else
+
+          snprintf(
+              crash_fn, PATH_MAX, "%s/crashes/id_%06llu_%02u%s%s", afl->out_dir,
+              afl->saved_crashes, afl->fsrv.last_kill_signal,
+              afl->file_extension ? "." : "",
+              afl->file_extension ? (const char *)afl->file_extension : "");
+
+#endif
+
+          ++afl->saved_crashes;
+
+          fd = open(crash_fn, O_WRONLY | O_CREAT | O_EXCL, DEFAULT_PERMISSION);
+          if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", crash_fn); }
+          ck_write(fd, use_mem, read_len, crash_fn);
+          close(fd);
+
+          afl->last_crash_time = get_cur_time();
+          afl->last_crash_execs = afl->fsrv.total_execs;
 
         } else {
 
-          afl->queue = afl->queue_buf[0];
+          u32 i = 0;
+          while (unlikely(i < afl->queued_items && afl->queue_buf[i] &&
+                          afl->queue_buf[i]->disabled)) {
 
-        }
+            ++i;
+
+          }
+
+          if (i < afl->queued_items && afl->queue_buf[i]) {
+
+            afl->queue = afl->queue_buf[i];
+
+          } else {
+
+            afl->queue = afl->queue_buf[0];
+
+          }
+
+          afl->max_depth = 0;
+          for (i = 0; i < afl->queued_items && likely(afl->queue_buf[i]); i++) {
 
-        afl->max_depth = 0;
-        for (i = 0; i < afl->queued_items && likely(afl->queue_buf[i]); i++) {
+            if (!afl->queue_buf[i]->disabled &&
+                afl->queue_buf[i]->depth > afl->max_depth)
+              afl->max_depth = afl->queue_buf[i]->depth;
 
-          if (!afl->queue_buf[i]->disabled &&
-              afl->queue_buf[i]->depth > afl->max_depth)
-            afl->max_depth = afl->queue_buf[i]->depth;
+          }
 
         }
 
+        q->disabled = 1;
+        q->perf_score = 0;
+
         break;
 
       case FSRV_RUN_ERROR:
@@ -1192,6 +1350,7 @@ void perform_dry_run(afl_state_t *afl) {
           if (!p->was_fuzzed) {
 
             p->was_fuzzed = 1;
+            afl->reinit_table = 1;
             --afl->pending_not_fuzzed;
             --afl->active_items;
 
@@ -1212,6 +1371,7 @@ void perform_dry_run(afl_state_t *afl) {
           if (!q->was_fuzzed) {
 
             q->was_fuzzed = 1;
+            afl->reinit_table = 1;
             --afl->pending_not_fuzzed;
             --afl->active_items;
 
@@ -1262,11 +1422,11 @@ void perform_dry_run(afl_state_t *afl) {
 static void link_or_copy(u8 *old_path, u8 *new_path) {
 
   s32 i = link(old_path, new_path);
+  if (!i) { return; }
+
   s32 sfd, dfd;
   u8 *tmp;
 
-  if (!i) { return; }
-
   sfd = open(old_path, O_RDONLY);
   if (sfd < 0) { PFATAL("Unable to open '%s'", old_path); }
 
@@ -1329,7 +1489,9 @@ void pivot_inputs(afl_state_t *afl) {
       u32 src_id;
 
       afl->resuming_fuzz = 1;
-      nfn = alloc_printf("%s/queue/%s", afl->out_dir, rsl);
+      nfn = alloc_printf(
+          "%s/queue/%s%s%s", afl->out_dir, rsl, afl->file_extension ? "." : "",
+          afl->file_extension ? (const char *)afl->file_extension : "");
 
       /* Since we're at it, let's also get the parent and figure out the
          appropriate depth for this entry. */
@@ -1369,12 +1531,33 @@ void pivot_inputs(afl_state_t *afl) {
 
       }
 
-      nfn = alloc_printf("%s/queue/id:%06u,time:0,execs:%llu,orig:%s",
-                         afl->out_dir, id, afl->fsrv.total_execs, use_name);
+      if (!afl->afl_env.afl_sha1_filenames) {
+
+        nfn = alloc_printf(
+            "%s/queue/id:%06u,time:0,execs:%llu,orig:%s%s%s", afl->out_dir, id,
+            afl->fsrv.total_execs, use_name, afl->file_extension ? "." : "",
+            afl->file_extension ? (const char *)afl->file_extension : "");
+
+      } else {
+
+        const char *hex = sha1_hex_for_file(q->fname, q->len);
+        nfn = alloc_printf(
+            "%s/queue/%s%s%s", afl->out_dir, hex,
+            afl->file_extension ? "." : "",
+            afl->file_extension ? (const char *)afl->file_extension : "");
+        ck_free((char *)hex);
+
+      }
+
+      u8 *pos = strrchr(nfn, '/');
+      no_spaces(pos + 30);
 
 #else
 
-      nfn = alloc_printf("%s/queue/id_%06u", afl->out_dir, id);
+      nfn = alloc_printf(
+          "%s/queue/id_%06u%s%s", afl->out_dir, id,
+          afl->file_extension ? "." : "",
+          afl->file_extension ? (const char *)afl->file_extension : "");
 
 #endif                                                    /* ^!SIMPLE_FILES */
 
@@ -1542,8 +1725,8 @@ double get_runnable_processes(void) {
      processes well. */
 
   FILE *f = fopen("/proc/stat", "r");
-  u8 tmp[1024];
-  u32 val = 0;
+  u8    tmp[1024];
+  u32   val = 0;
 
   if (!f) { return 0; }
 
@@ -1581,10 +1764,11 @@ double get_runnable_processes(void) {
 
 void nuke_resume_dir(afl_state_t *afl) {
 
-  u8 *fn;
+  u8 *const case_prefix = afl->afl_env.afl_sha1_filenames ? "" : CASE_PREFIX;
+  u8       *fn;
 
   fn = alloc_printf("%s/_resume/.state/deterministic_done", afl->out_dir);
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   fn = alloc_printf("%s/_resume/.state/auto_extras", afl->out_dir);
@@ -1592,11 +1776,11 @@ void nuke_resume_dir(afl_state_t *afl) {
   ck_free(fn);
 
   fn = alloc_printf("%s/_resume/.state/redundant_edges", afl->out_dir);
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   fn = alloc_printf("%s/_resume/.state/variable_behavior", afl->out_dir);
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   fn = alloc_printf("%s/_resume/.state", afl->out_dir);
@@ -1604,7 +1788,7 @@ void nuke_resume_dir(afl_state_t *afl) {
   ck_free(fn);
 
   fn = alloc_printf("%s/_resume", afl->out_dir);
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   return;
@@ -1621,8 +1805,9 @@ dir_cleanup_failed:
 
 static void handle_existing_out_dir(afl_state_t *afl) {
 
-  FILE *f;
-  u8   *fn = alloc_printf("%s/fuzzer_stats", afl->out_dir);
+  u8 *const case_prefix = afl->afl_env.afl_sha1_filenames ? "" : CASE_PREFIX;
+  FILE     *f;
+  u8       *fn = alloc_printf("%s/fuzzer_stats", afl->out_dir);
 
   /* See if the output directory is locked. If yes, bail out. If not,
      create a lock that will persist for the lifetime of the process
@@ -1744,7 +1929,7 @@ static void handle_existing_out_dir(afl_state_t *afl) {
   /* Next, we need to clean up <afl->out_dir>/queue/.state/ subdirectories: */
 
   fn = alloc_printf("%s/queue/.state/deterministic_done", afl->out_dir);
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   fn = alloc_printf("%s/queue/.state/auto_extras", afl->out_dir);
@@ -1752,11 +1937,11 @@ static void handle_existing_out_dir(afl_state_t *afl) {
   ck_free(fn);
 
   fn = alloc_printf("%s/queue/.state/redundant_edges", afl->out_dir);
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   fn = alloc_printf("%s/queue/.state/variable_behavior", afl->out_dir);
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   /* Then, get rid of the .state subdirectory itself (should be empty by now)
@@ -1767,7 +1952,7 @@ static void handle_existing_out_dir(afl_state_t *afl) {
   ck_free(fn);
 
   fn = alloc_printf("%s/queue", afl->out_dir);
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   /* All right, let's do <afl->out_dir>/crashes/id:* and
@@ -1811,7 +1996,10 @@ static void handle_existing_out_dir(afl_state_t *afl) {
 
   }
 
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+#ifdef AFL_PERSISTENT_RECORD
+  delete_files(fn, RECORD_PREFIX);
+#endif
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   fn = alloc_printf("%s/hangs", afl->out_dir);
@@ -1843,7 +2031,10 @@ static void handle_existing_out_dir(afl_state_t *afl) {
 
   }
 
-  if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
+#ifdef AFL_PERSISTENT_RECORD
+  delete_files(fn, RECORD_PREFIX);
+#endif
+  if (delete_files(fn, case_prefix)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
   /* And now, for some finishing touches. */
@@ -2126,6 +2317,21 @@ void setup_dirs_fds(afl_state_t *afl) {
 
   fflush(afl->fsrv.plot_file);
 
+#ifdef INTROSPECTION
+
+  tmp = alloc_printf("%s/plot_det_data", afl->out_dir);
+
+  int fd = open(tmp, O_WRONLY | O_CREAT, DEFAULT_PERMISSION);
+  if (fd < 0) { PFATAL("Unable to create '%s'", tmp); }
+  ck_free(tmp);
+
+  afl->fsrv.det_plot_file = fdopen(fd, "w");
+  if (!afl->fsrv.det_plot_file) { PFATAL("fdopen() failed"); }
+
+  if (afl->in_place_resume) { fseek(afl->fsrv.det_plot_file, 0, SEEK_END); }
+
+#endif
+
   /* ignore errors */
 
 }
@@ -2199,7 +2405,8 @@ void check_crash_handling(void) {
      reporting the awful way. */
 
   #if !TARGET_OS_IPHONE
-  if (system("launchctl list 2>/dev/null | grep -q '\\.ReportCrash$'")) return;
+  if (system("launchctl list 2>/dev/null | grep -q '\\.ReportCrash\\>'"))
+    return;
 
   SAYF(
       "\n" cLRD "[-] " cRST
@@ -2226,7 +2433,7 @@ void check_crash_handling(void) {
    *BSD, so we can just let it slide for now. */
 
   s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY);
-  u8 fchar;
+  u8  fchar;
 
   if (fd < 0) { return; }
 
@@ -2365,7 +2572,7 @@ void check_cpu_governor(afl_state_t *afl) {
   FATAL("Suboptimal CPU scaling governor");
 
 #elif defined __APPLE__
-  u64 min = 0, max = 0;
+  u64    min = 0, max = 0;
   size_t mlen = sizeof(min);
   if (afl->afl_env.afl_skip_cpufreq) return;
 
diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c
index 64dbe7c6..2f6af4bc 100644
--- a/src/afl-fuzz-mutators.c
+++ b/src/afl-fuzz-mutators.c
@@ -5,12 +5,12 @@
    Originally written by Shengtuo Hu
 
    Now maintained by  Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
                         Dominik Maier <mail@dmnk.co>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -397,6 +397,18 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) {
 
   }
 
+  /* "afl_custom_post_run", optional */
+  mutator->afl_custom_post_run = dlsym(dh, "afl_custom_post_run");
+  if (!mutator->afl_custom_post_run) {
+
+    ACTF("optional symbol 'afl_custom_post_run' not found.");
+
+  } else {
+
+    OKF("Found 'afl_custom_post_run'.");
+
+  }
+
   /* "afl_custom_queue_new_entry", optional */
   mutator->afl_custom_queue_new_entry = dlsym(dh, "afl_custom_queue_new_entry");
   if (!mutator->afl_custom_queue_new_entry) {
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index c6e9a295..74bb8cbc 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <limits.h>
 #include "cmplog.h"
+#include "afl-mutations.h"
 
 /* MOpt */
 
@@ -70,50 +71,6 @@ static int select_algorithm(afl_state_t *afl, u32 max_algorithm) {
 
 }
 
-/* Helper to choose random block len for block operations in fuzz_one().
-   Doesn't return zero, provided that max_len is > 0. */
-
-static inline u32 choose_block_len(afl_state_t *afl, u32 limit) {
-
-  u32 min_value, max_value;
-  u32 rlim = MIN(afl->queue_cycle, (u32)3);
-
-  if (unlikely(!afl->run_over10m)) { rlim = 1; }
-
-  switch (rand_below(afl, rlim)) {
-
-    case 0:
-      min_value = 1;
-      max_value = HAVOC_BLK_SMALL;
-      break;
-
-    case 1:
-      min_value = HAVOC_BLK_SMALL;
-      max_value = HAVOC_BLK_MEDIUM;
-      break;
-
-    default:
-
-      if (likely(rand_below(afl, 10))) {
-
-        min_value = HAVOC_BLK_MEDIUM;
-        max_value = HAVOC_BLK_LARGE;
-
-      } else {
-
-        min_value = HAVOC_BLK_LARGE;
-        max_value = HAVOC_BLK_XL;
-
-      }
-
-  }
-
-  if (min_value >= limit) { min_value = 1; }
-
-  return min_value + rand_below(afl, MIN(max_value, limit) - min_value + 1);
-
-}
-
 /* Helper function to see if a particular change (xor_val = old ^ new) could
    be a product of deterministic bit flips with the lengths and stepovers
    attempted by afl-fuzz. This is used to avoid dupes in some of the
@@ -372,9 +329,9 @@ u8 fuzz_one_original(afl_state_t *afl) {
   u32 len, temp_len;
   u32 j;
   u32 i;
-  u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0;
+  u8 *in_buf, *out_buf, *orig_in, *ex_tmp;
   u64 havoc_queued = 0, orig_hit_cnt, new_hit_cnt = 0, prev_cksum, _prev_cksum;
-  u32 splice_cycle = 0, perf_score = 100, orig_perf, eff_cnt = 1;
+  u32 splice_cycle = 0, perf_score = 100, orig_perf;
 
   u8 ret_val = 1, doing_det = 0;
 
@@ -442,18 +399,24 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
 #endif                                                     /* ^IGNORE_FINDS */
 
-  if (unlikely(afl->not_on_tty)) {
+  if (likely(afl->not_on_tty)) {
+
+    u8 time_tmp[64];
 
+    u_simplestring_time_diff(time_tmp, afl->prev_run_time + get_cur_time(),
+                             afl->start_time);
     ACTF(
-        "Fuzzing test case #%u (%u total, %llu crashes saved, "
+        "Fuzzing test case #%u (%u total, %llu crashes saved, state: %s, "
+        "mode=%s, "
         "perf_score=%0.0f, weight=%0.0f, favorite=%u, was_fuzzed=%u, "
-        "exec_us=%llu, hits=%u, map=%u, ascii=%u)...",
+        "exec_us=%llu, hits=%u, map=%u, ascii=%u, run_time=%s)...",
         afl->current_entry, afl->queued_items, afl->saved_crashes,
+        get_fuzzing_state(afl), afl->fuzz_mode ? "exploit" : "explore",
         afl->queue_cur->perf_score, afl->queue_cur->weight,
         afl->queue_cur->favored, afl->queue_cur->was_fuzzed,
         afl->queue_cur->exec_us,
         likely(afl->n_fuzz) ? afl->n_fuzz[afl->queue_cur->n_fuzz_entry] : 0,
-        afl->queue_cur->bitmap_size, afl->queue_cur->is_ascii);
+        afl->queue_cur->bitmap_size, afl->queue_cur->is_ascii, time_tmp);
     fflush(stdout);
 
   }
@@ -582,12 +545,37 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
   }
 
+  u64 before_det_time = get_cur_time();
+#ifdef INTROSPECTION
+
+  u64 before_havoc_time;
+  u32 before_det_findings = afl->queued_items,
+      before_det_edges = count_non_255_bytes(afl, afl->virgin_bits),
+      before_havoc_findings, before_havoc_edges;
+  u8 is_logged = 0;
+
+#endif
+  if (!afl->skip_deterministic) {
+
+    if (!skip_deterministic_stage(afl, in_buf, out_buf, len, before_det_time)) {
+
+      goto abandon_entry;
+
+    }
+
+  }
+
+  u8 *skip_eff_map = afl->queue_cur->skipdet_e->skip_eff_map;
+
   /* Skip right away if -d is given, if it has not been chosen sufficiently
      often to warrant the expensive deterministic stage (fuzz_level), or
      if it has gone through deterministic testing in earlier, resumed runs
      (passed_det). */
+  /* if skipdet decide to skip the seed or no interesting bytes found,
+     we skip the whole deterministic stage as well */
 
   if (likely(afl->skip_deterministic) || likely(afl->queue_cur->passed_det) ||
+      likely(!afl->queue_cur->skipdet_e->quick_eff_bytes) ||
       likely(perf_score <
              (afl->queue_cur->depth * 30 <= afl->havoc_max_mult * 100
                   ? afl->queue_cur->depth * 30
@@ -614,13 +602,13 @@ u8 fuzz_one_original(afl_state_t *afl) {
    * SIMPLE BITFLIP (+dictionary construction) *
    *********************************************/
 
-#define FLIP_BIT(_ar, _b)                   \
-  do {                                      \
-                                            \
-    u8 *_arf = (u8 *)(_ar);                 \
-    u32 _bf = (_b);                         \
-    _arf[(_bf) >> 3] ^= (128 >> ((_bf)&7)); \
-                                            \
+#define FLIP_BIT(_ar, _b)                     \
+  do {                                        \
+                                              \
+    u8 *_arf = (u8 *)(_ar);                   \
+    u32 _bf = (_b);                           \
+    _arf[(_bf) >> 3] ^= (128 >> ((_bf) & 7)); \
+                                              \
   } while (0)
 
   /* Single walking bit. */
@@ -646,6 +634,10 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
     afl->stage_cur_byte = afl->stage_cur >> 3;
 
+    if (!skip_eff_map[afl->stage_cur_byte]) continue;
+
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
+
     FLIP_BIT(out_buf, afl->stage_cur);
 
 #ifdef INTROSPECTION
@@ -762,6 +754,10 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
     afl->stage_cur_byte = afl->stage_cur >> 3;
 
+    if (!skip_eff_map[afl->stage_cur_byte]) continue;
+
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
+
     FLIP_BIT(out_buf, afl->stage_cur);
     FLIP_BIT(out_buf, afl->stage_cur + 1);
 
@@ -797,6 +793,10 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
     afl->stage_cur_byte = afl->stage_cur >> 3;
 
+    if (!skip_eff_map[afl->stage_cur_byte]) continue;
+
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
+
     FLIP_BIT(out_buf, afl->stage_cur);
     FLIP_BIT(out_buf, afl->stage_cur + 1);
     FLIP_BIT(out_buf, afl->stage_cur + 2);
@@ -824,34 +824,6 @@ u8 fuzz_one_original(afl_state_t *afl) {
   afl->queue_cur->stats_mutated += afl->stage_max;
 #endif
 
-  /* Effector map setup. These macros calculate:
-
-     EFF_APOS      - position of a particular file offset in the map.
-     EFF_ALEN      - length of a map with a particular number of bytes.
-     EFF_SPAN_ALEN - map span for a sequence of bytes.
-
-   */
-
-#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2)
-#define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1))
-#define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l))
-#define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l)-1) - EFF_APOS(_p) + 1)
-
-  /* Initialize effector map for the next step (see comments below). Always
-     flag first and last byte as doing something. */
-
-  eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len));
-  if (unlikely(!eff_map)) { PFATAL("alloc"); }
-  memset(eff_map, 0, EFF_ALEN(len));
-  eff_map[0] = 1;
-
-  if (EFF_APOS(len - 1) != 0) {
-
-    eff_map[EFF_APOS(len - 1)] = 1;
-    ++eff_cnt;
-
-  }
-
   /* Walking byte. */
 
   afl->stage_name = "bitflip 8/8";
@@ -865,6 +837,10 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
     afl->stage_cur_byte = afl->stage_cur;
 
+    if (!skip_eff_map[afl->stage_cur_byte]) continue;
+
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
+
     out_buf[afl->stage_cur] ^= 0xFF;
 
 #ifdef INTROSPECTION
@@ -874,59 +850,19 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
     if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; }
 
-    /* We also use this stage to pull off a simple trick: we identify
-       bytes that seem to have no effect on the current execution path
-       even when fully flipped - and we skip them during more expensive
-       deterministic stages, such as arithmetics or known ints. */
-
-    if (!eff_map[EFF_APOS(afl->stage_cur)]) {
-
-      u64 cksum;
-
-      /* If in non-instrumented mode or if the file is very short, just flag
-         everything without wasting time on checksums. */
-
-      if (!afl->non_instrumented_mode && len >= EFF_MIN_LEN) {
-
-        cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
-
-      } else {
-
-        cksum = ~prev_cksum;
-
-      }
-
-      if (cksum != prev_cksum) {
-
-        eff_map[EFF_APOS(afl->stage_cur)] = 1;
-        ++eff_cnt;
-
-      }
-
-    }
-
     out_buf[afl->stage_cur] ^= 0xFF;
 
   }
 
-  /* If the effector map is more than EFF_MAX_PERC dense, just flag the
-     whole thing as worth fuzzing, since we wouldn't be saving much time
-     anyway. */
+  /* New effective bytes calculation. */
 
-  if (eff_cnt != (u32)EFF_ALEN(len) &&
-      eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) {
+  for (i = 0; i < len; i++) {
 
-    memset(eff_map, 1, EFF_ALEN(len));
-
-    afl->blocks_eff_select += EFF_ALEN(len);
-
-  } else {
-
-    afl->blocks_eff_select += eff_cnt;
+    if (skip_eff_map[i]) afl->blocks_eff_select += 1;
 
   }
 
-  afl->blocks_eff_total += EFF_ALEN(len);
+  afl->blocks_eff_total += len;
 
   new_hit_cnt = afl->queued_items + afl->saved_crashes;
 
@@ -951,12 +887,9 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
     /* Let's consult the effector map... */
 
-    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) {
+    if (!skip_eff_map[i]) continue;
 
-      --afl->stage_max;
-      continue;
-
-    }
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
 
     afl->stage_cur_byte = i;
 
@@ -996,13 +929,10 @@ u8 fuzz_one_original(afl_state_t *afl) {
   for (i = 0; i < len - 3; ++i) {
 
     /* Let's consult the effector map... */
-    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
-        !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) {
 
-      --afl->stage_max;
-      continue;
+    if (!skip_eff_map[i]) continue;
 
-    }
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
 
     afl->stage_cur_byte = i;
 
@@ -1053,12 +983,9 @@ skip_bitflip:
 
     /* Let's consult the effector map... */
 
-    if (!eff_map[EFF_APOS(i)]) {
-
-      afl->stage_max -= 2 * ARITH_MAX;
-      continue;
+    if (!skip_eff_map[i]) continue;
 
-    }
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
 
     afl->stage_cur_byte = i;
 
@@ -1140,12 +1067,9 @@ skip_bitflip:
 
     /* Let's consult the effector map... */
 
-    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) {
-
-      afl->stage_max -= 4 * ARITH_MAX;
-      continue;
+    if (!skip_eff_map[i]) continue;
 
-    }
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
 
     afl->stage_cur_byte = i;
 
@@ -1273,13 +1197,9 @@ skip_bitflip:
 
     /* Let's consult the effector map... */
 
-    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
-        !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) {
+    if (!skip_eff_map[i]) continue;
 
-      afl->stage_max -= 4 * ARITH_MAX;
-      continue;
-
-    }
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
 
     afl->stage_cur_byte = i;
 
@@ -1411,12 +1331,9 @@ skip_arith:
 
     /* Let's consult the effector map... */
 
-    if (!eff_map[EFF_APOS(i)]) {
-
-      afl->stage_max -= sizeof(interesting_8);
-      continue;
+    if (!skip_eff_map[i]) continue;
 
-    }
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
 
     afl->stage_cur_byte = i;
 
@@ -1474,12 +1391,9 @@ skip_arith:
 
     /* Let's consult the effector map... */
 
-    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) {
+    if (!skip_eff_map[i]) continue;
 
-      afl->stage_max -= sizeof(interesting_16);
-      continue;
-
-    }
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
 
     afl->stage_cur_byte = i;
 
@@ -1565,13 +1479,9 @@ skip_arith:
 
     /* Let's consult the effector map... */
 
-    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
-        !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) {
-
-      afl->stage_max -= sizeof(interesting_32) >> 1;
-      continue;
+    if (!skip_eff_map[i]) continue;
 
-    }
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
 
     afl->stage_cur_byte = i;
 
@@ -1663,6 +1573,10 @@ skip_interest:
 
     u32 last_len = 0;
 
+    if (!skip_eff_map[i]) continue;
+
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
+
     afl->stage_cur_byte = i;
 
     /* Extras are sorted by size, from smallest to largest. This means
@@ -1680,9 +1594,7 @@ skip_interest:
       if ((afl->extras_cnt > afl->max_det_extras &&
            rand_below(afl, afl->extras_cnt) >= afl->max_det_extras) ||
           afl->extras[j].len > len - i ||
-          !memcmp(afl->extras[j].data, out_buf + i, afl->extras[j].len) ||
-          !memchr(eff_map + EFF_APOS(i), 1,
-                  EFF_SPAN_ALEN(i, afl->extras[j].len))) {
+          !memcmp(afl->extras[j].data, out_buf + i, afl->extras[j].len)) {
 
         --afl->stage_max;
         continue;
@@ -1730,6 +1642,10 @@ skip_interest:
 
   for (i = 0; i <= (u32)len; ++i) {
 
+    if (!skip_eff_map[i % len]) continue;
+
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
+
     afl->stage_cur_byte = i;
 
     for (j = 0; j < afl->extras_cnt; ++j) {
@@ -1792,6 +1708,10 @@ skip_user_extras:
 
     u32 last_len = 0;
 
+    if (!skip_eff_map[i]) continue;
+
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
+
     afl->stage_cur_byte = i;
 
     u32 min_extra_len = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS);
@@ -1800,9 +1720,7 @@ skip_user_extras:
       /* See the comment in the earlier code; extras are sorted by size. */
 
       if (afl->a_extras[j].len > len - i ||
-          !memcmp(afl->a_extras[j].data, out_buf + i, afl->a_extras[j].len) ||
-          !memchr(eff_map + EFF_APOS(i), 1,
-                  EFF_SPAN_ALEN(i, afl->a_extras[j].len))) {
+          !memcmp(afl->a_extras[j].data, out_buf + i, afl->a_extras[j].len)) {
 
         --afl->stage_max;
         continue;
@@ -1850,6 +1768,10 @@ skip_user_extras:
 
   for (i = 0; i <= (u32)len; ++i) {
 
+    if (!skip_eff_map[i % len]) continue;
+
+    if (is_det_timeout(before_det_time, 0)) { goto custom_mutator_stage; }
+
     afl->stage_cur_byte = i;
 
     for (j = 0; j < afl->a_extras_cnt; ++j) {
@@ -1912,6 +1834,7 @@ custom_mutator_stage:
 
   afl->stage_name = "custom mutator";
   afl->stage_short = "custom";
+  afl->stage_cur = 0;
   afl->stage_val_type = STAGE_VAL_NONE;
   bool has_custom_fuzz = false;
   u32  shift = unlikely(afl->custom_only) ? 7 : 8;
@@ -1931,6 +1854,8 @@ custom_mutator_stage:
 
     if (el->afl_custom_fuzz) {
 
+      havoc_queued = afl->queued_items;
+
       afl->current_custom_fuzz = el;
       afl->stage_name = el->name_short;
 
@@ -2054,6 +1979,19 @@ custom_mutator_stage:
 
 havoc_stage:
 
+#ifdef INTROSPECTION
+
+  if (!is_logged) {
+
+    is_logged = 1;
+    before_havoc_findings = afl->queued_items;
+    before_havoc_edges = count_non_255_bytes(afl, afl->virgin_bits);
+    before_havoc_time = get_cur_time();
+
+  }
+
+#endif
+
   if (unlikely(afl->custom_only)) {
 
     /* Force UI update */
@@ -2122,45 +2060,97 @@ havoc_stage:
   /* We essentially just do several thousand runs (depending on perf_score)
      where we take the input file and make random stacked tweaks. */
 
-#define MAX_HAVOC_ENTRY 64
-#define MUTATE_ASCII_DICT 64
+  u32 *mutation_array;
+  u32  stack_max, rand_max;  // stack_max_pow = afl->havoc_stack_pow2;
+
+  switch (afl->input_mode) {
+
+    case 1: {  // TEXT
 
-  u32 r_max, r;
+      if (likely(afl->fuzz_mode == 0)) {  // is exploration?
+        mutation_array = (unsigned int *)&binary_array;
+        rand_max = MUT_BIN_ARRAY_SIZE;
 
-  r_max = (MAX_HAVOC_ENTRY + 1) + (afl->extras_cnt ? 4 : 0) +
-          (afl->a_extras_cnt
-               ? (unlikely(afl->cmplog_binary && afl->queue_cur->is_ascii)
-                      ? MUTATE_ASCII_DICT
-                      : 4)
-               : 0);
+      } else {  // exploitation mode
 
-  if (unlikely(afl->expand_havoc && afl->ready_for_splicing_count > 1)) {
+        mutation_array = (unsigned int *)&text_array;
+        rand_max = MUT_TXT_ARRAY_SIZE;
 
-    /* add expensive havoc cases here, they are activated after a full
-       cycle without finds happened */
+      }
+
+      break;
+
+    }
+
+    case 2: {  // BINARY
+
+      if (likely(afl->fuzz_mode == 0)) {  // is exploration?
+        mutation_array = (unsigned int *)&mutation_strategy_exploration_binary;
+        rand_max = MUT_STRATEGY_ARRAY_SIZE;
+
+      } else {  // exploitation mode
+
+        mutation_array = (unsigned int *)&mutation_strategy_exploitation_binary;
+        rand_max = MUT_STRATEGY_ARRAY_SIZE;
+        // or this one? we do not have enough binary bug benchmarks :-(
+        // mutation_array = (unsigned int *)&binary_array;
+        // rand_max = MUT_BIN_ARRAY_SIZE;
+
+      }
+
+      break;
+
+    }
+
+    default: {  // DEFAULT/GENERIC
 
-    r_max += 4;
+      if (likely(afl->fuzz_mode == 0)) {  // is exploration?
+        mutation_array = (unsigned int *)&binary_array;
+        rand_max = MUT_BIN_ARRAY_SIZE;
+
+      } else {  // exploitation mode
+
+        mutation_array = (unsigned int *)&text_array;
+        rand_max = MUT_TXT_ARRAY_SIZE;
+
+      }
+
+      break;
+
+    }
 
   }
 
-  if (unlikely(get_cur_time() - afl->last_find_time > 5000 /* 5 seconds */ &&
-               afl->ready_for_splicing_count > 1)) {
+  /*
+  if (temp_len < 64) {
+
+    --stack_max_pow;
 
-    /* add expensive havoc cases here if there is no findings in the last 5s */
+  } else if (temp_len <= 8096) {
 
-    r_max += 4;
+    ++stack_max_pow;
+
+  } else {
+
+    ++stack_max_pow;
 
   }
 
+  */
+
+  stack_max = 1 << (1 + rand_below(afl, afl->havoc_stack_pow2));
+
+  // + (afl->extras_cnt ? 2 : 0) + (afl->a_extras_cnt ? 2 : 0);
+
   for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) {
 
-    u32 use_stacking = 1 << (1 + rand_below(afl, afl->havoc_stack_pow2));
+    u32 use_stacking = 1 + rand_below(afl, stack_max);
 
     afl->stage_cur_val = use_stacking;
 
 #ifdef INTROSPECTION
-    snprintf(afl->mutation, sizeof(afl->mutation), "%s HAVOC-%u",
-             afl->queue_cur->fname, use_stacking);
+    snprintf(afl->mutation, sizeof(afl->mutation), "%s HAVOC-%u-%u",
+             afl->queue_cur->fname, afl->queue_cur->is_ascii, use_stacking);
 #endif
 
     for (i = 0; i < use_stacking; ++i) {
@@ -2169,8 +2159,8 @@ havoc_stage:
 
         LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
 
-          if (el->stacked_custom &&
-              rand_below(afl, 100) < el->stacked_custom_prob) {
+          if (unlikely(el->stacked_custom &&
+                       rand_below(afl, 100) < el->stacked_custom_prob)) {
 
             u8    *custom_havoc_buf = NULL;
             size_t new_len = el->afl_custom_havoc_mutation(
@@ -2200,159 +2190,173 @@ havoc_stage:
 
       }
 
-      switch ((r = rand_below(afl, r_max))) {
+    retry_havoc_step: {
+
+      u32 r = rand_below(afl, rand_max), item;
+
+      switch (mutation_array[r]) {
 
-        case 0 ... 3: {
+        case MUT_FLIPBIT: {
 
           /* Flip a single bit somewhere. Spooky! */
+          u8  bit = rand_below(afl, 8);
+          u32 off = rand_below(afl, temp_len);
+          out_buf[off] ^= 1 << bit;
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP-BIT_%u", bit);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          FLIP_BIT(out_buf, rand_below(afl, temp_len << 3));
           break;
 
         }
 
-        case 4 ... 7: {
+        case MUT_INTERESTING8: {
 
           /* Set byte to interesting value. */
 
+          item = rand_below(afl, sizeof(interesting_8));
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          out_buf[rand_below(afl, temp_len)] =
-              interesting_8[rand_below(afl, sizeof(interesting_8))];
+          out_buf[rand_below(afl, temp_len)] = interesting_8[item];
           break;
 
         }
 
-        case 8 ... 9: {
+        case MUT_INTERESTING16: {
 
           /* Set word to interesting value, little endian. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
+          item = rand_below(afl, sizeof(interesting_16) >> 1);
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
+
           *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) =
-              interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)];
+              interesting_16[item];
 
           break;
 
         }
 
-        case 10 ... 11: {
+        case MUT_INTERESTING16BE: {
 
           /* Set word to interesting value, big endian. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
+          item = rand_below(afl, sizeof(interesting_16) >> 1);
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16BE");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16BE_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16(
-              interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]);
+          *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) =
+              SWAP16(interesting_16[item]);
 
           break;
 
         }
 
-        case 12 ... 13: {
+        case MUT_INTERESTING32: {
 
           /* Set dword to interesting value, little endian. */
 
-          if (temp_len < 4) { break; }
+          if (unlikely(temp_len < 4)) { break; }  // no retry
 
+          item = rand_below(afl, sizeof(interesting_32) >> 2);
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
+
           *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) =
-              interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)];
+              interesting_32[item];
 
           break;
 
         }
 
-        case 14 ... 15: {
+        case MUT_INTERESTING32BE: {
 
           /* Set dword to interesting value, big endian. */
 
-          if (temp_len < 4) { break; }
+          if (unlikely(temp_len < 4)) { break; }  // no retry
 
+          item = rand_below(afl, sizeof(interesting_32) >> 2);
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32BE");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32BE_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32(
-              interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]);
+          *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) =
+              SWAP32(interesting_32[item]);
 
           break;
 
         }
 
-        case 16 ... 19: {
+        case MUT_ARITH8_: {
 
           /* Randomly subtract from byte. */
 
+          item = 1 + rand_below(afl, ARITH_MAX);
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8_");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8-_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX);
+          out_buf[rand_below(afl, temp_len)] -= item;
           break;
 
         }
 
-        case 20 ... 23: {
+        case MUT_ARITH8: {
 
           /* Randomly add to byte. */
 
+          item = 1 + rand_below(afl, ARITH_MAX);
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8+");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8+_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX);
+          out_buf[rand_below(afl, temp_len)] += item;
           break;
 
         }
 
-        case 24 ... 25: {
+        case MUT_ARITH16_: {
 
           /* Randomly subtract from word, little endian. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
           u32 pos = rand_below(afl, temp_len - 1);
+          item = 1 + rand_below(afl, ARITH_MAX);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16_-%u", pos);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX);
+          *(u16 *)(out_buf + pos) -= item;
 
           break;
 
         }
 
-        case 26 ... 27: {
+        case MUT_ARITH16BE_: {
 
           /* Randomly subtract from word, big endian. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
           u32 pos = rand_below(afl, temp_len - 1);
           u16 num = 1 + rand_below(afl, ARITH_MAX);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16_BE-%u_%u", pos,
-                   num);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE-_%u", num);
           strcat(afl->mutation, afl->m_tmp);
 #endif
           *(u16 *)(out_buf + pos) =
@@ -2362,36 +2366,36 @@ havoc_stage:
 
         }
 
-        case 28 ... 29: {
+        case MUT_ARITH16: {
 
           /* Randomly add to word, little endian. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
           u32 pos = rand_below(afl, temp_len - 1);
+          item = 1 + rand_below(afl, ARITH_MAX);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+-%u", pos);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX);
+          *(u16 *)(out_buf + pos) += item;
 
           break;
 
         }
 
-        case 30 ... 31: {
+        case MUT_ARITH16BE: {
 
           /* Randomly add to word, big endian. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
           u32 pos = rand_below(afl, temp_len - 1);
           u16 num = 1 + rand_below(afl, ARITH_MAX);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+BE-%u_%u", pos,
-                   num);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE+__%u", num);
           strcat(afl->mutation, afl->m_tmp);
 #endif
           *(u16 *)(out_buf + pos) =
@@ -2401,36 +2405,36 @@ havoc_stage:
 
         }
 
-        case 32 ... 33: {
+        case MUT_ARITH32_: {
 
           /* Randomly subtract from dword, little endian. */
 
-          if (temp_len < 4) { break; }
+          if (unlikely(temp_len < 4)) { break; }  // no retry
 
           u32 pos = rand_below(afl, temp_len - 3);
+          item = 1 + rand_below(afl, ARITH_MAX);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32_-%u", pos);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX);
+          *(u32 *)(out_buf + pos) -= item;
 
           break;
 
         }
 
-        case 34 ... 35: {
+        case MUT_ARITH32BE_: {
 
           /* Randomly subtract from dword, big endian. */
 
-          if (temp_len < 4) { break; }
+          if (unlikely(temp_len < 4)) { break; }  // no retry
 
           u32 pos = rand_below(afl, temp_len - 3);
           u32 num = 1 + rand_below(afl, ARITH_MAX);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32_BE-%u-%u", pos,
-                   num);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE-_%u", num);
           strcat(afl->mutation, afl->m_tmp);
 #endif
           *(u32 *)(out_buf + pos) =
@@ -2440,36 +2444,36 @@ havoc_stage:
 
         }
 
-        case 36 ... 37: {
+        case MUT_ARITH32: {
 
           /* Randomly add to dword, little endian. */
 
-          if (temp_len < 4) { break; }
+          if (unlikely(temp_len < 4)) { break; }  // no retry
 
           u32 pos = rand_below(afl, temp_len - 3);
+          item = 1 + rand_below(afl, ARITH_MAX);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+-%u", pos);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+_%u", item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX);
+          *(u32 *)(out_buf + pos) += item;
 
           break;
 
         }
 
-        case 38 ... 39: {
+        case MUT_ARITH32BE: {
 
           /* Randomly add to dword, big endian. */
 
-          if (temp_len < 4) { break; }
+          if (unlikely(temp_len < 4)) { break; }  // no retry
 
           u32 pos = rand_below(afl, temp_len - 3);
           u32 num = 1 + rand_below(afl, ARITH_MAX);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+BE-%u-%u", pos,
-                   num);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE+_%u", num);
           strcat(afl->mutation, afl->m_tmp);
 #endif
           *(u32 *)(out_buf + pos) =
@@ -2479,24 +2483,27 @@ havoc_stage:
 
         }
 
-        case 40 ... 43: {
+        case MUT_RAND8: {
 
           /* Just set a random byte to a random value. Because,
              why not. We use XOR with 1-255 to eliminate the
              possibility of a no-op. */
 
+          u32 pos = rand_below(afl, temp_len);
+          item = 1 + rand_below(afl, 255);
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8_%u",
+                   out_buf[pos] ^ item);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255);
+          out_buf[pos] ^= item;
           break;
 
         }
 
-        case 44 ... 46: {
+        case MUT_CLONE_COPY: {
 
-          if (temp_len + HAVOC_BLK_XL < MAX_FILE) {
+          if (likely(temp_len + HAVOC_BLK_XL < MAX_FILE)) {
 
             /* Clone bytes. */
 
@@ -2505,8 +2512,8 @@ havoc_stage:
             u32 clone_to = rand_below(afl, temp_len);
 
 #ifdef INTROSPECTION
-            snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE-%s-%u-%u-%u",
-                     "clone", clone_from, clone_to, clone_len);
+            snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE-%s_%u_%u_%u",
+                     "COPY", clone_from, clone_to, clone_len);
             strcat(afl->mutation, afl->m_tmp);
 #endif
             u8 *new_buf =
@@ -2529,24 +2536,35 @@ havoc_stage:
             afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch));
             temp_len += clone_len;
 
+          } else if (unlikely(temp_len < 8)) {
+
+            break;
+
+          } else {
+
+            goto retry_havoc_step;
+
           }
 
           break;
 
         }
 
-        case 47: {
+        case MUT_CLONE_FIXED: {
 
-          if (temp_len + HAVOC_BLK_XL < MAX_FILE) {
+          if (likely(temp_len + HAVOC_BLK_XL < MAX_FILE)) {
 
             /* Insert a block of constant bytes (25%). */
 
             u32 clone_len = choose_block_len(afl, HAVOC_BLK_XL);
             u32 clone_to = rand_below(afl, temp_len);
+            u32 strat = rand_below(afl, 2);
+            u32 clone_from = clone_to ? clone_to - 1 : 0;
+            item = strat ? rand_below(afl, 256) : out_buf[clone_from];
 
 #ifdef INTROSPECTION
-            snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE-%s-%u-%u",
-                     "insert", clone_to, clone_len);
+            snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE-%s_%u_%u_%u",
+                     "FIXED", strat, clone_to, clone_len);
             strcat(afl->mutation, afl->m_tmp);
 #endif
             u8 *new_buf =
@@ -2559,10 +2577,7 @@ havoc_stage:
 
             /* Inserted part */
 
-            memset(new_buf + clone_to,
-                   rand_below(afl, 2) ? rand_below(afl, 256)
-                                      : out_buf[rand_below(afl, temp_len)],
-                   clone_len);
+            memset(new_buf + clone_to, item, clone_len);
 
             /* Tail */
             memcpy(new_buf + clone_to + clone_len, out_buf + clone_to,
@@ -2572,66 +2587,77 @@ havoc_stage:
             afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch));
             temp_len += clone_len;
 
+          } else if (unlikely(temp_len < 8)) {
+
+            break;
+
+          } else {
+
+            goto retry_havoc_step;
+
           }
 
           break;
 
         }
 
-        case 48 ... 50: {
+        case MUT_OVERWRITE_COPY: {
 
           /* Overwrite bytes with a randomly selected chunk bytes. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
-          u32 copy_len = choose_block_len(afl, temp_len - 1);
-          u32 copy_from = rand_below(afl, temp_len - copy_len + 1);
-          u32 copy_to = rand_below(afl, temp_len - copy_len + 1);
+          u32 copy_from, copy_to,
+              copy_len = choose_block_len(afl, temp_len - 1);
 
-          if (likely(copy_from != copy_to)) {
+          do {
+
+            copy_from = rand_below(afl, temp_len - copy_len + 1);
+            copy_to = rand_below(afl, temp_len - copy_len + 1);
+
+          } while (unlikely(copy_from == copy_to));
 
 #ifdef INTROSPECTION
-            snprintf(afl->m_tmp, sizeof(afl->m_tmp), " OVERWRITE_COPY-%u-%u-%u",
-                     copy_from, copy_to, copy_len);
-            strcat(afl->mutation, afl->m_tmp);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " OVERWRITE-COPY_%u_%u_%u",
+                   copy_from, copy_to, copy_len);
+          strcat(afl->mutation, afl->m_tmp);
 #endif
-            memmove(out_buf + copy_to, out_buf + copy_from, copy_len);
-
-          }
+          memmove(out_buf + copy_to, out_buf + copy_from, copy_len);
 
           break;
 
         }
 
-        case 51: {
+        case MUT_OVERWRITE_FIXED: {
 
           /* Overwrite bytes with fixed bytes. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
           u32 copy_len = choose_block_len(afl, temp_len - 1);
           u32 copy_to = rand_below(afl, temp_len - copy_len + 1);
+          u32 strat = rand_below(afl, 2);
+          u32 copy_from = copy_to ? copy_to - 1 : 0;
+          item = strat ? rand_below(afl, 256) : out_buf[copy_from];
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " OVERWRITE_FIXED-%u-%u",
-                   copy_to, copy_len);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp),
+                   " OVERWRITE-FIXED_%u_%u_%u-%u", strat, item, copy_to,
+                   copy_len);
           strcat(afl->mutation, afl->m_tmp);
 #endif
-          memset(out_buf + copy_to,
-                 rand_below(afl, 2) ? rand_below(afl, 256)
-                                    : out_buf[rand_below(afl, temp_len)],
-                 copy_len);
+          memset(out_buf + copy_to, item, copy_len);
 
           break;
 
         }
 
-        case 52: {
+        case MUT_BYTEADD: {
 
           /* Increase byte by 1. */
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ADDBYTE_");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " BYTEADD_");
           strcat(afl->mutation, afl->m_tmp);
 #endif
           out_buf[rand_below(afl, temp_len)]++;
@@ -2639,12 +2665,12 @@ havoc_stage:
 
         }
 
-        case 53: {
+        case MUT_BYTESUB: {
 
           /* Decrease byte by 1. */
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " SUBBYTE_");
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " BYTESUB_");
           strcat(afl->mutation, afl->m_tmp);
 #endif
           out_buf[rand_below(afl, temp_len)]--;
@@ -2652,9 +2678,9 @@ havoc_stage:
 
         }
 
-        case 54: {
+        case MUT_FLIP8: {
 
-          /* Flip byte. */
+          /* Flip byte with a XOR 0xff. This is the same as NEG. */
 
 #ifdef INTROSPECTION
           snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP8_");
@@ -2665,9 +2691,9 @@ havoc_stage:
 
         }
 
-        case 55 ... 56: {
+        case MUT_SWITCH: {
 
-          if (temp_len < 4) { break; }
+          if (unlikely(temp_len < 4)) { break; }  // no retry
 
           /* Switch bytes. */
 
@@ -2677,7 +2703,7 @@ havoc_stage:
 
             switch_to = rand_below(afl, temp_len);
 
-          } while (switch_from == switch_to);
+          } while (unlikely(switch_from == switch_to));
 
           if (switch_from < switch_to) {
 
@@ -2694,7 +2720,7 @@ havoc_stage:
           switch_len = choose_block_len(afl, MIN(switch_len, to_end));
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " SWITCH-%s-%u-%u-%u",
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " SWITCH-%s_%u_%u_%u",
                    "switch", switch_from, switch_to, switch_len);
           strcat(afl->mutation, afl->m_tmp);
 #endif
@@ -2717,12 +2743,11 @@ havoc_stage:
 
         }
 
-        // MAX_HAVOC_ENTRY = 64
-        case 57 ... MAX_HAVOC_ENTRY: {
+        case MUT_DEL: {
 
           /* Delete bytes. */
 
-          if (temp_len < 2) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
 
           /* Don't delete too much. */
 
@@ -2730,7 +2755,7 @@ havoc_stage:
           u32 del_from = rand_below(afl, temp_len - del_len + 1);
 
 #ifdef INTROSPECTION
-          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL-%u-%u", del_from,
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL_%u_%u", del_from,
                    del_len);
           strcat(afl->mutation, afl->m_tmp);
 #endif
@@ -2743,135 +2768,401 @@ havoc_stage:
 
         }
 
-        default:
+        case MUT_SHUFFLE: {
 
-          r -= (MAX_HAVOC_ENTRY + 1);
+          /* Shuffle bytes. */
 
-          if (afl->extras_cnt) {
+          if (unlikely(temp_len < 4)) { break; }  // no retry
 
-            if (r < 2) {
+          u32 len = choose_block_len(afl, temp_len - 1);
+          u32 off = rand_below(afl, temp_len - len + 1);
 
-              /* Use the dictionary. */
+#ifdef INTROSPECTION
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " SHUFFLE_%u", len);
+          strcat(afl->mutation, afl->m_tmp);
+#endif
+
+          for (u32 i = len - 1; i > 0; i--) {
+
+            u32 j;
+            do {
+
+              j = rand_below(afl, i + 1);
+
+            } while (unlikely(i == j));
 
-              u32 use_extra = rand_below(afl, afl->extras_cnt);
-              u32 extra_len = afl->extras[use_extra].len;
+            unsigned char temp = out_buf[off + i];
+            out_buf[off + i] = out_buf[off + j];
+            out_buf[off + j] = temp;
 
-              if (extra_len > temp_len) { break; }
+          }
+
+          break;
+
+        }
+
+        case MUT_DELONE: {
+
+          /* Delete bytes. */
+
+          if (unlikely(temp_len < 2)) { break; }  // no retry
+
+          /* Don't delete too much. */
+
+          u32 del_len = 1;
+          u32 del_from = rand_below(afl, temp_len - del_len + 1);
 
-              u32 insert_at = rand_below(afl, temp_len - extra_len + 1);
 #ifdef INTROSPECTION
-              snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA_OVERWRITE-%u-%u",
-                       insert_at, extra_len);
-              strcat(afl->mutation, afl->m_tmp);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DELONE_%u", del_from);
+          strcat(afl->mutation, afl->m_tmp);
 #endif
-              memcpy(out_buf + insert_at, afl->extras[use_extra].data,
-                     extra_len);
+          memmove(out_buf + del_from, out_buf + del_from + del_len,
+                  temp_len - del_from - del_len);
 
-              break;
+          temp_len -= del_len;
+
+          break;
+
+        }
 
-            } else if (r < 4) {
+        case MUT_INSERTONE: {
 
-              u32 use_extra = rand_below(afl, afl->extras_cnt);
-              u32 extra_len = afl->extras[use_extra].len;
-              if (temp_len + extra_len >= MAX_FILE) { break; }
+          if (unlikely(temp_len < 2)) { break; }  // no retry
+
+          u32 clone_len = 1;
+          u32 clone_to = rand_below(afl, temp_len);
+          u32 strat = rand_below(afl, 2);
+          u32 clone_from = clone_to ? clone_to - 1 : 0;
+          item = strat ? rand_below(afl, 256) : out_buf[clone_from];
 
-              u8 *ptr = afl->extras[use_extra].data;
-              u32 insert_at = rand_below(afl, temp_len + 1);
 #ifdef INTROSPECTION
-              snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA_INSERT-%u-%u",
-                       insert_at, extra_len);
-              strcat(afl->mutation, afl->m_tmp);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INSERTONE_%u_%u", strat,
+                   clone_to);
+          strcat(afl->mutation, afl->m_tmp);
 #endif
+          u8 *new_buf =
+              afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len);
+          if (unlikely(!new_buf)) { PFATAL("alloc"); }
 
-              out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len);
-              if (unlikely(!out_buf)) { PFATAL("alloc"); }
+          /* Head */
 
-              /* Tail */
-              memmove(out_buf + insert_at + extra_len, out_buf + insert_at,
-                      temp_len - insert_at);
+          memcpy(new_buf, out_buf, clone_to);
 
-              /* Inserted part */
-              memcpy(out_buf + insert_at, ptr, extra_len);
-              temp_len += extra_len;
+          /* Inserted part */
 
-              break;
+          memset(new_buf + clone_to, item, clone_len);
 
-            } else {
+          /* Tail */
+          memcpy(new_buf + clone_to + clone_len, out_buf + clone_to,
+                 temp_len - clone_to);
 
-              r -= 4;
+          out_buf = new_buf;
+          afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch));
+          temp_len += clone_len;
 
-            }
+          break;
+
+        }
+
+        case MUT_ASCIINUM: {
+
+          if (unlikely(temp_len < 4)) { break; }  // no retry
+
+          u32 off = rand_below(afl, temp_len), off2 = off, cnt = 0;
+
+          while (off2 + cnt < temp_len && !isdigit(out_buf[off2 + cnt])) {
+
+            ++cnt;
 
           }
 
-          if (afl->a_extras_cnt) {
+          // none found, wrap
+          if (off2 + cnt == temp_len) {
 
-            u32 r_cmp = 2;
+            off2 = 0;
+            cnt = 0;
 
-            if (unlikely(afl->cmplog_binary && afl->queue_cur->is_ascii)) {
+            while (cnt < off && !isdigit(out_buf[off2 + cnt])) {
 
-              r_cmp = MUTATE_ASCII_DICT >> 1;
+              ++cnt;
 
             }
 
-            if (r < r_cmp) {
+            if (cnt == off) {
 
-              /* Use the dictionary. */
+              if (temp_len < 8) {
 
-              u32 use_extra = rand_below(afl, afl->a_extras_cnt);
-              u32 extra_len = afl->a_extras[use_extra].len;
+                break;
 
-              if (extra_len > temp_len) { break; }
+              } else {
 
-              u32 insert_at = rand_below(afl, temp_len - extra_len + 1);
-#ifdef INTROSPECTION
-              snprintf(afl->m_tmp, sizeof(afl->m_tmp),
-                       " AUTO_EXTRA_OVERWRITE-%u-%u", insert_at, extra_len);
-              strcat(afl->mutation, afl->m_tmp);
-#endif
-              memcpy(out_buf + insert_at, afl->a_extras[use_extra].data,
-                     extra_len);
+                goto retry_havoc_step;
+
+              }
+
+            }
+
+          }
+
+          off = off2 + cnt;
+          off2 = off + 1;
+
+          while (off2 < temp_len && isdigit(out_buf[off2])) {
+
+            ++off2;
+
+          }
 
+          s64 val = out_buf[off] - '0';
+          for (u32 i = off + 1; i < off2; ++i) {
+
+            val = (val * 10) + out_buf[i] - '0';
+
+          }
+
+          if (off && out_buf[off - 1] == '-') { val = -val; }
+
+          u32 strat = rand_below(afl, 8);
+          switch (strat) {
+
+            case 0:
+              val++;
+              break;
+            case 1:
+              val--;
+              break;
+            case 2:
+              val *= 2;
+              break;
+            case 3:
+              val /= 2;
               break;
+            case 4:
+              if (likely(val && (u64)val < 0x19999999)) {
 
-            } else if (r < (r_cmp << 1)) {
+                val = (u64)rand_next(afl) % (u64)((u64)val * 10);
+
+              } else {
 
-              u32 use_extra = rand_below(afl, afl->a_extras_cnt);
-              u32 extra_len = afl->a_extras[use_extra].len;
-              if (temp_len + extra_len >= MAX_FILE) { break; }
+                val = rand_below(afl, 256);
+
+              }
+
+              break;
+            case 5:
+              val += rand_below(afl, 256);
+              break;
+            case 6:
+              val -= rand_below(afl, 256);
+              break;
+            case 7:
+              val = ~(val);
+              break;
+
+          }
 
-              u8 *ptr = afl->a_extras[use_extra].data;
-              u32 insert_at = rand_below(afl, temp_len + 1);
 #ifdef INTROSPECTION
-              snprintf(afl->m_tmp, sizeof(afl->m_tmp),
-                       " AUTO_EXTRA_INSERT-%u-%u", insert_at, extra_len);
-              strcat(afl->mutation, afl->m_tmp);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ASCIINUM_%u_%u_%u",
+                   afl->queue_cur->is_ascii, strat, off);
+          strcat(afl->mutation, afl->m_tmp);
 #endif
+          // fprintf(stderr, "val: %u-%u = %ld\n", off, off2, val);
+
+          char buf[20];
+          snprintf(buf, sizeof(buf), "%" PRId64, val);
 
-              out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len);
-              if (unlikely(!out_buf)) { PFATAL("alloc"); }
+          // fprintf(stderr, "BEFORE: %s\n", out_buf);
 
-              /* Tail */
-              memmove(out_buf + insert_at + extra_len, out_buf + insert_at,
-                      temp_len - insert_at);
+          u32 old_len = off2 - off;
+          u32 new_len = strlen(buf);
 
-              /* Inserted part */
-              memcpy(out_buf + insert_at, ptr, extra_len);
-              temp_len += extra_len;
+          if (old_len == new_len) {
+
+            memcpy(out_buf + off, buf, new_len);
+
+          } else {
+
+            u8 *new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch),
+                                      temp_len + new_len - old_len);
+            if (unlikely(!new_buf)) { PFATAL("alloc"); }
+
+            /* Head */
+
+            memcpy(new_buf, out_buf, off);
+
+            /* Inserted part */
+
+            memcpy(new_buf + off, buf, new_len);
+
+            /* Tail */
+            memcpy(new_buf + off + new_len, out_buf + off2, temp_len - off2);
+
+            out_buf = new_buf;
+            afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch));
+            temp_len += (new_len - old_len);
+
+          }
+
+          // fprintf(stderr, "AFTER : %s\n", out_buf);
+          break;
+
+        }
+
+        case MUT_INSERTASCIINUM: {
+
+          u32 len = 1 + rand_below(afl, 8);
+          u32 pos = rand_below(afl, temp_len);
+          /* Insert ascii number. */
+          if (unlikely(temp_len < pos + len)) {
+
+            if (unlikely(temp_len < 8)) {
 
               break;
 
             } else {
 
-              r -= (r_cmp << 1);
+              goto retry_havoc_step;
 
             }
 
           }
 
-          /* Splicing otherwise if we are still here.
-             Overwrite bytes with a randomly selected chunk from another
-             testcase or insert that chunk. */
+#ifdef INTROSPECTION
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INSERTASCIINUM_");
+          strcat(afl->mutation, afl->m_tmp);
+#endif
+          u64  val = rand_next(afl);
+          char buf[20];
+          snprintf(buf, sizeof(buf), "%llu", val);
+          memcpy(out_buf + pos, buf, len);
+
+          break;
+
+        }
+
+        case MUT_EXTRA_OVERWRITE: {
+
+          if (unlikely(!afl->extras_cnt)) { goto retry_havoc_step; }
+
+          /* Use the dictionary. */
+
+          u32 use_extra = rand_below(afl, afl->extras_cnt);
+          u32 extra_len = afl->extras[use_extra].len;
+
+          if (unlikely(extra_len > temp_len)) { goto retry_havoc_step; }
+
+          u32 insert_at = rand_below(afl, temp_len - extra_len + 1);
+#ifdef INTROSPECTION
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA-OVERWRITE_%u_%u",
+                   insert_at, extra_len);
+          strcat(afl->mutation, afl->m_tmp);
+#endif
+          memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len);
+
+          break;
+
+        }
+
+        case MUT_EXTRA_INSERT: {
+
+          if (unlikely(!afl->extras_cnt)) { goto retry_havoc_step; }
+
+          u32 use_extra = rand_below(afl, afl->extras_cnt);
+          u32 extra_len = afl->extras[use_extra].len;
+          if (unlikely(temp_len + extra_len >= MAX_FILE)) {
+
+            goto retry_havoc_step;
+
+          }
+
+          u8 *ptr = afl->extras[use_extra].data;
+          u32 insert_at = rand_below(afl, temp_len + 1);
+#ifdef INTROSPECTION
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA-INSERT_%u_%u",
+                   insert_at, extra_len);
+          strcat(afl->mutation, afl->m_tmp);
+#endif
+
+          out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len);
+          if (unlikely(!out_buf)) { PFATAL("alloc"); }
+
+          /* Tail */
+          memmove(out_buf + insert_at + extra_len, out_buf + insert_at,
+                  temp_len - insert_at);
+
+          /* Inserted part */
+          memcpy(out_buf + insert_at, ptr, extra_len);
+          temp_len += extra_len;
+
+          break;
+
+        }
+
+        case MUT_AUTO_EXTRA_OVERWRITE: {
+
+          if (unlikely(!afl->a_extras_cnt)) { goto retry_havoc_step; }
+
+          /* Use the dictionary. */
+
+          u32 use_extra = rand_below(afl, afl->a_extras_cnt);
+          u32 extra_len = afl->a_extras[use_extra].len;
+
+          if (unlikely(extra_len > temp_len)) { goto retry_havoc_step; }
+
+          u32 insert_at = rand_below(afl, temp_len - extra_len + 1);
+#ifdef INTROSPECTION
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp),
+                   " AUTO-EXTRA-OVERWRITE_%u_%u", insert_at, extra_len);
+          strcat(afl->mutation, afl->m_tmp);
+#endif
+          memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len);
+
+          break;
+
+        }
+
+        case MUT_AUTO_EXTRA_INSERT: {
+
+          if (unlikely(!afl->a_extras_cnt)) { goto retry_havoc_step; }
+
+          u32 use_extra = rand_below(afl, afl->a_extras_cnt);
+          u32 extra_len = afl->a_extras[use_extra].len;
+          if (unlikely(temp_len + extra_len >= MAX_FILE)) {
+
+            goto retry_havoc_step;
+
+          }
+
+          u8 *ptr = afl->a_extras[use_extra].data;
+          u32 insert_at = rand_below(afl, temp_len + 1);
+#ifdef INTROSPECTION
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " AUTO-EXTRA-INSERT_%u_%u",
+                   insert_at, extra_len);
+          strcat(afl->mutation, afl->m_tmp);
+#endif
+
+          out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len);
+          if (unlikely(!out_buf)) { PFATAL("alloc"); }
+
+          /* Tail */
+          memmove(out_buf + insert_at + extra_len, out_buf + insert_at,
+                  temp_len - insert_at);
+
+          /* Inserted part */
+          memcpy(out_buf + insert_at, ptr, extra_len);
+          temp_len += extra_len;
+
+          break;
+
+        }
+
+        case MUT_SPLICE_OVERWRITE: {
+
+          if (unlikely(afl->ready_for_splicing_count <= 1)) {
+
+            goto retry_havoc_step;
+
+          }
 
           /* Pick a random queue entry and seek to it. */
 
@@ -2880,79 +3171,110 @@ havoc_stage:
 
             tid = rand_below(afl, afl->queued_items);
 
-          } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4);
+          } while (unlikely(tid == afl->current_entry ||
+
+                            afl->queue_buf[tid]->len < 4));
 
           /* Get the testcase for splicing. */
           struct queue_entry *target = afl->queue_buf[tid];
           u32                 new_len = target->len;
           u8                 *new_buf = queue_testcase_get(afl, target);
 
-          if ((temp_len >= 2 && r % 2) || temp_len + HAVOC_BLK_XL >= MAX_FILE) {
-
-            /* overwrite mode */
+          /* overwrite mode */
 
-            u32 copy_from, copy_to, copy_len;
+          u32 copy_from, copy_to, copy_len;
 
-            copy_len = choose_block_len(afl, new_len - 1);
-            if (copy_len > temp_len) copy_len = temp_len;
+          copy_len = choose_block_len(afl, new_len - 1);
+          if (copy_len > temp_len) copy_len = temp_len;
 
-            copy_from = rand_below(afl, new_len - copy_len + 1);
-            copy_to = rand_below(afl, temp_len - copy_len + 1);
+          copy_from = rand_below(afl, new_len - copy_len + 1);
+          copy_to = rand_below(afl, temp_len - copy_len + 1);
 
 #ifdef INTROSPECTION
-            snprintf(afl->m_tmp, sizeof(afl->m_tmp),
-                     " SPLICE_OVERWRITE-%u-%u-%u-%s", copy_from, copy_to,
-                     copy_len, target->fname);
-            strcat(afl->mutation, afl->m_tmp);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp),
+                   " SPLICE-OVERWRITE_%u_%u_%u_%s", copy_from, copy_to,
+                   copy_len, target->fname);
+          strcat(afl->mutation, afl->m_tmp);
 #endif
-            memmove(out_buf + copy_to, new_buf + copy_from, copy_len);
+          memmove(out_buf + copy_to, new_buf + copy_from, copy_len);
 
-          } else {
+          break;
+
+        }
+
+        case MUT_SPLICE_INSERT: {
+
+          if (unlikely(afl->ready_for_splicing_count <= 1)) {
+
+            goto retry_havoc_step;
+
+          }
+
+          if (unlikely(temp_len + HAVOC_BLK_XL >= MAX_FILE)) {
+
+            goto retry_havoc_step;
+
+          }
+
+          /* Pick a random queue entry and seek to it. */
+
+          u32 tid;
+          do {
+
+            tid = rand_below(afl, afl->queued_items);
+
+          } while (unlikely(tid == afl->current_entry ||
+
+                            afl->queue_buf[tid]->len < 4));
 
-            /* insert mode */
+          /* Get the testcase for splicing. */
+          struct queue_entry *target = afl->queue_buf[tid];
+          u32                 new_len = target->len;
+          u8                 *new_buf = queue_testcase_get(afl, target);
 
-            u32 clone_from, clone_to, clone_len;
+          /* insert mode */
 
-            clone_len = choose_block_len(afl, new_len);
-            clone_from = rand_below(afl, new_len - clone_len + 1);
-            clone_to = rand_below(afl, temp_len + 1);
+          u32 clone_from, clone_to, clone_len;
 
-            u8 *temp_buf = afl_realloc(AFL_BUF_PARAM(out_scratch),
-                                       temp_len + clone_len + 1);
-            if (unlikely(!temp_buf)) { PFATAL("alloc"); }
+          clone_len = choose_block_len(afl, new_len);
+          clone_from = rand_below(afl, new_len - clone_len + 1);
+          clone_to = rand_below(afl, temp_len + 1);
+
+          u8 *temp_buf =
+              afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len + 1);
+          if (unlikely(!temp_buf)) { PFATAL("alloc"); }
 
 #ifdef INTROSPECTION
-            snprintf(afl->m_tmp, sizeof(afl->m_tmp),
-                     " SPLICE_INSERT-%u-%u-%u-%s", clone_from, clone_to,
-                     clone_len, target->fname);
-            strcat(afl->mutation, afl->m_tmp);
+          snprintf(afl->m_tmp, sizeof(afl->m_tmp), " SPLICE-INSERT_%u_%u_%u_%s",
+                   clone_from, clone_to, clone_len, target->fname);
+          strcat(afl->mutation, afl->m_tmp);
 #endif
-            /* Head */
+          /* Head */
 
-            memcpy(temp_buf, out_buf, clone_to);
+          memcpy(temp_buf, out_buf, clone_to);
 
-            /* Inserted part */
-
-            memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len);
+          /* Inserted part */
 
-            /* Tail */
-            memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to,
-                   temp_len - clone_to);
+          memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len);
 
-            out_buf = temp_buf;
-            afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch));
-            temp_len += clone_len;
+          /* Tail */
+          memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to,
+                 temp_len - clone_to);
 
-          }
+          out_buf = temp_buf;
+          afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch));
+          temp_len += clone_len;
 
           break;
 
-          // end of default
+        }
 
       }
 
     }
 
+    }
+
     if (common_fuzz_stuff(afl, out_buf, temp_len)) { goto abandon_entry; }
 
     /* out_buf might have been mangled a bit, so let's restore it to its
@@ -3038,7 +3360,9 @@ retry_splicing:
 
       tid = rand_below(afl, afl->queued_items);
 
-    } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4);
+    } while (
+
+        unlikely(tid == afl->current_entry || afl->queue_buf[tid]->len < 4));
 
     /* Get the testcase */
     afl->splicing_with = tid;
@@ -3078,6 +3402,25 @@ retry_splicing:
 
   ret_val = 0;
 
+#ifdef INTROSPECTION
+
+  afl->havoc_prof->queued_det_stage =
+      before_havoc_findings - before_det_findings;
+  afl->havoc_prof->queued_havoc_stage =
+      afl->queued_items - before_havoc_findings;
+  afl->havoc_prof->total_queued_det += afl->havoc_prof->queued_det_stage;
+  afl->havoc_prof->edge_det_stage = before_havoc_edges - before_det_edges;
+  afl->havoc_prof->edge_havoc_stage =
+      count_non_255_bytes(afl, afl->virgin_bits) - before_havoc_edges;
+  afl->havoc_prof->total_det_edge += afl->havoc_prof->edge_det_stage;
+  afl->havoc_prof->det_stage_time = before_havoc_time - before_det_time;
+  afl->havoc_prof->havoc_stage_time = get_cur_time() - before_havoc_time;
+  afl->havoc_prof->total_det_time += afl->havoc_prof->det_stage_time;
+
+  plot_profile_data(afl, afl->queue_cur);
+
+#endif
+
 /* we are through with this queue entry - for this iteration */
 abandon_entry:
 
@@ -3092,7 +3435,12 @@ abandon_entry:
     --afl->pending_not_fuzzed;
     afl->queue_cur->was_fuzzed = 1;
     afl->reinit_table = 1;
-    if (afl->queue_cur->favored) { --afl->pending_favored; }
+    if (afl->queue_cur->favored) {
+
+      --afl->pending_favored;
+      afl->smallest_favored = -1;
+
+    }
 
   }
 
@@ -3348,13 +3696,13 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) {
    * SIMPLE BITFLIP (+dictionary construction) *
    *********************************************/
 
-#define FLIP_BIT(_ar, _b)                   \
-  do {                                      \
-                                            \
-    u8 *_arf = (u8 *)(_ar);                 \
-    u32 _bf = (_b);                         \
-    _arf[(_bf) >> 3] ^= (128 >> ((_bf)&7)); \
-                                            \
+#define FLIP_BIT(_ar, _b)                     \
+  do {                                        \
+                                              \
+    u8 *_arf = (u8 *)(_ar);                   \
+    u32 _bf = (_b);                           \
+    _arf[(_bf) >> 3] ^= (128 >> ((_bf) & 7)); \
+                                              \
   } while (0)
 
   /* Single walking bit. */
@@ -5555,7 +5903,13 @@ pacemaker_fuzzing:
 
             --afl->pending_not_fuzzed;
             afl->queue_cur->was_fuzzed = 1;
-            if (afl->queue_cur->favored) { --afl->pending_favored; }
+            afl->reinit_table = 1
+            if (afl->queue_cur->favored) {
+
+              --afl->pending_favored;
+              afl->smallest_favored = -1;
+
+            }
 
           }
 
diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c
index 7dad0770..873b25e2 100644
--- a/src/afl-fuzz-python.c
+++ b/src/afl-fuzz-python.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -249,6 +249,8 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) {
         PyObject_GetAttrString(py_module, "queue_get");
     py_functions[PY_FUNC_FUZZ_SEND] =
         PyObject_GetAttrString(py_module, "fuzz_send");
+    py_functions[PY_FUNC_POST_RUN] =
+        PyObject_GetAttrString(py_module, "post_run");
     py_functions[PY_FUNC_SPLICE_OPTOUT] =
         PyObject_GetAttrString(py_module, "splice_optout");
     if (py_functions[PY_FUNC_SPLICE_OPTOUT]) { afl->custom_splice_optout = 1; }
@@ -468,6 +470,12 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl,
 
   }
 
+  if (py_functions[PY_FUNC_POST_RUN]) {
+
+    mutator->afl_custom_post_run = post_run_py;
+
+  }
+
   if (py_functions[PY_FUNC_SPLICE_OPTOUT]) {
 
     mutator->afl_custom_splice_optout = splice_optout_py;
@@ -925,6 +933,28 @@ void fuzz_send_py(void *py_mutator, const u8 *buf, size_t buf_size) {
 
 }
 
+void post_run_py(void *py_mutator) {
+
+  PyObject *py_args, *py_value;
+
+  py_args = PyTuple_New(0);
+  py_value = PyObject_CallObject(
+      ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_POST_RUN], py_args);
+  Py_DECREF(py_args);
+
+  if (py_value != NULL) {
+
+    Py_DECREF(py_value);
+
+  } else {
+
+    PyErr_Print();
+    FATAL("Call failed");
+
+  }
+
+}
+
 u8 queue_new_entry_py(void *py_mutator, const u8 *filename_new_queue,
                       const u8 *filename_orig_queue) {
 
diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c
index b10bf749..2318df60 100644
--- a/src/afl-fuzz-queue.c
+++ b/src/afl-fuzz-queue.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at:
@@ -80,6 +80,7 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q,
   if (unlikely(weight < 0.1)) { weight = 0.1; }
   if (unlikely(q->favored)) { weight *= 5; }
   if (unlikely(!q->was_fuzzed)) { weight *= 2; }
+  if (unlikely(q->fs_redundant)) { weight *= 0.8; }
 
   return weight;
 
@@ -369,9 +370,9 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) {
 
     s32 fd;
 
-    fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, DEFAULT_PERMISSION);
-    if (fd < 0) { PFATAL("Unable to create '%s'", fn); }
-    close(fd);
+    if (unlikely(afl->afl_env.afl_disable_redundant)) { q->disabled = 1; }
+    fd = permissive_create(afl, fn);
+    if (fd >= 0) { close(fd); }
 
   } else {
 
@@ -612,7 +613,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
 
   }
 
-  if (likely(q->len > 4)) afl->ready_for_splicing_count++;
+  if (likely(q->len > 4)) { ++afl->ready_for_splicing_count; }
 
   ++afl->queued_items;
   ++afl->active_items;
@@ -663,6 +664,8 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
 
   }
 
+  q->skipdet_e = (struct skipdet_entry *)ck_alloc(sizeof(struct skipdet_entry));
+
 }
 
 /* Destroy the entire queue. */
@@ -678,6 +681,15 @@ void destroy_queue(afl_state_t *afl) {
     q = afl->queue_buf[i];
     ck_free(q->fname);
     ck_free(q->trace_mini);
+    if (q->skipdet_e) {
+
+      if (q->skipdet_e->done_inf_map) ck_free(q->skipdet_e->done_inf_map);
+      if (q->skipdet_e->skip_eff_map) ck_free(q->skipdet_e->skip_eff_map);
+
+      ck_free(q->skipdet_e);
+
+    }
+
     ck_free(q);
 
   }
@@ -701,13 +713,20 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) {
   u64 fav_factor;
   u64 fuzz_p2;
 
-  if (unlikely(afl->schedule >= FAST && afl->schedule < RARE))
+  if (likely(afl->schedule >= FAST && afl->schedule < RARE)) {
+
     fuzz_p2 = 0;  // Skip the fuzz_p2 comparison
-  else if (unlikely(afl->schedule == RARE))
+
+  } else if (unlikely(afl->schedule == RARE)) {
+
     fuzz_p2 = next_pow2(afl->n_fuzz[q->n_fuzz_entry]);
-  else
+
+  } else {
+
     fuzz_p2 = q->fuzz_level;
 
+  }
+
   if (unlikely(afl->schedule >= RARE) || unlikely(afl->fixed_seed)) {
 
     fav_factor = q->len << 2;
@@ -729,47 +748,36 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) {
         /* Faster-executing or smaller test cases are favored. */
         u64 top_rated_fav_factor;
         u64 top_rated_fuzz_p2;
-        if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE))
-          top_rated_fuzz_p2 =
-              next_pow2(afl->n_fuzz[afl->top_rated[i]->n_fuzz_entry]);
-        else
-          top_rated_fuzz_p2 = afl->top_rated[i]->fuzz_level;
-
-        if (unlikely(afl->schedule >= RARE) || unlikely(afl->fixed_seed)) {
-
-          top_rated_fav_factor = afl->top_rated[i]->len << 2;
 
-        } else {
-
-          top_rated_fav_factor =
-              afl->top_rated[i]->exec_us * afl->top_rated[i]->len;
+        if (likely(afl->schedule >= FAST && afl->schedule < RARE)) {
 
-        }
+          top_rated_fuzz_p2 = 0;  // Skip the fuzz_p2 comparison
 
-        if (fuzz_p2 > top_rated_fuzz_p2) {
+        } else if (unlikely(afl->schedule == RARE)) {
 
-          continue;
+          top_rated_fuzz_p2 =
+              next_pow2(afl->n_fuzz[afl->top_rated[i]->n_fuzz_entry]);
 
-        } else if (fuzz_p2 == top_rated_fuzz_p2) {
+        } else {
 
-          if (fav_factor > top_rated_fav_factor) { continue; }
+          top_rated_fuzz_p2 = afl->top_rated[i]->fuzz_level;
 
         }
 
         if (unlikely(afl->schedule >= RARE) || unlikely(afl->fixed_seed)) {
 
-          if (fav_factor > afl->top_rated[i]->len << 2) { continue; }
+          top_rated_fav_factor = afl->top_rated[i]->len << 2;
 
         } else {
 
-          if (fav_factor >
-              afl->top_rated[i]->exec_us * afl->top_rated[i]->len) {
+          top_rated_fav_factor =
+              afl->top_rated[i]->exec_us * afl->top_rated[i]->len;
 
-            continue;
+        }
 
-          }
+        if (likely(fuzz_p2 > top_rated_fuzz_p2)) { continue; }
 
-        }
+        if (likely(fav_factor > top_rated_fav_factor)) { continue; }
 
         /* Looks like we're going to win. Decrease ref count for the
            previous winner, discard its afl->fsrv.trace_bits[] if necessary. */
@@ -834,6 +842,8 @@ void cull_queue(afl_state_t *afl) {
   /* Let's see if anything in the bitmap isn't captured in temp_v.
      If yes, and if it has a afl->top_rated[] contender, let's use it. */
 
+  afl->smallest_favored = -1;
+
   for (i = 0; i < afl->fsrv.map_size; ++i) {
 
     if (afl->top_rated[i] && (temp_v[i >> 3] & (1 << (i & 7)))) {
@@ -857,7 +867,16 @@ void cull_queue(afl_state_t *afl) {
         afl->top_rated[i]->favored = 1;
         ++afl->queued_favored;
 
-        if (!afl->top_rated[i]->was_fuzzed) { ++afl->pending_favored; }
+        if (!afl->top_rated[i]->was_fuzzed) {
+
+          ++afl->pending_favored;
+          if (unlikely(afl->smallest_favored < 0)) {
+
+            afl->smallest_favored = (s64)afl->top_rated[i]->id;
+
+          }
+
+        }
 
       }
 
@@ -875,6 +894,8 @@ void cull_queue(afl_state_t *afl) {
 
   }
 
+  afl->reinit_table = 1;
+
 }
 
 /* Calculate case desirability score to adjust the length of havoc fuzzing.
diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c
index 6e4a655b..9316da71 100644
--- a/src/afl-fuzz-redqueen.c
+++ b/src/afl-fuzz-redqueen.c
@@ -7,7 +7,7 @@
    Forkserver design by Jann Horn <jannhorn@googlemail.com>
 
    Now maintained by by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
@@ -28,8 +28,9 @@
 #include "afl-fuzz.h"
 #include "cmplog.h"
 
-//#define _DEBUG
-//#define CMPLOG_INTROSPECTION
+// #define _DEBUG
+// #define USE_HASHMAP
+// #define CMPLOG_INTROSPECTION
 
 // CMP attribute enum
 enum {
@@ -40,7 +41,7 @@ enum {
   IS_FP = 8,       // is a floating point, not an integer
   /* --- below are internal settings, not from target cmplog */
   IS_FP_MOD = 16,    // arithemtic changed floating point
-  IS_INT_MOD = 32,   // arithmetic changed interger
+  IS_INT_MOD = 32,   // arithmetic changed integer
   IS_TRANSFORM = 64  // transformed integer
 
 };
@@ -87,6 +88,13 @@ static u32 hshape;
 static u64 screen_update;
 static u64 last_update;
 
+#ifdef USE_HASHMAP
+// hashmap functions
+void hashmap_reset();
+bool hashmap_search_and_add(uint8_t type, uint64_t key);
+bool hashmap_search_and_add_ptr(uint8_t type, u8 *key);
+#endif
+
 static struct range *add_range(struct range *ranges, u32 start, u32 end) {
 
   struct range *r = ck_alloc_nozero(sizeof(struct range));
@@ -129,7 +137,6 @@ static struct range *pop_biggest_range(struct range **ranges) {
 }
 
 #ifdef _DEBUG
-// static int  logging = 0;
 static void dump(char *txt, u8 *buf, u32 len) {
 
   u32 i;
@@ -140,6 +147,7 @@ static void dump(char *txt, u8 *buf, u32 len) {
 
 }
 
+/*
 static void dump_file(char *path, char *name, u32 counter, u8 *buf, u32 len) {
 
   char fn[4096];
@@ -155,6 +163,8 @@ static void dump_file(char *path, char *name, u32 counter, u8 *buf, u32 len) {
 
 }
 
+*/
+
 #endif
 
 static u8 get_exec_checksum(afl_state_t *afl, u8 *buf, u32 len, u64 *cksum) {
@@ -379,7 +389,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len,
 
     }
 
-    if (++afl->stage_cur % screen_update == 0) { show_stats(afl); };
+    if (unlikely(++afl->stage_cur % screen_update == 0)) { show_stats(afl); };
 
   }
 
@@ -571,7 +581,6 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) {
 
 }
 
-//#ifdef CMPLOG_SOLVE_TRANSFORM
 static int strntoll(const char *str, size_t sz, char **end, int base,
                     long long *out) {
 
@@ -656,7 +665,6 @@ static int is_hex(const char *str) {
 
 }
 
-#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
 // tests 4 bytes at location
 static int is_base64(const char *str) {
 
@@ -732,12 +740,14 @@ static u32 from_base64(u8 *src, u8 *dst, u32 dst_len) {
 
 }
 
-static void to_base64(u8 *src, u8 *dst, u32 dst_len) {
+static u32 to_base64(u8 *src, u8 *dst, u32 dst_len) {
 
   u32 i, j, v;
-  u32 len = (dst_len >> 2) * 3;
+  //  u32 len = (dst_len >> 2) * 3;
+  u32 len = (dst_len / 3) * 4;
+  if (dst_len % 3) len += 4;
 
-  for (i = 0, j = 0; i < len; i += 3, j += 4) {
+  for (i = 0, j = 0; j < len; i += 3, j += 4) {
 
     v = src[i];
     v = i + 1 < len ? v << 8 | src[i + 1] : v << 8;
@@ -745,7 +755,8 @@ static void to_base64(u8 *src, u8 *dst, u32 dst_len) {
 
     dst[j] = base64_encode_table[(v >> 18) & 0x3F];
     dst[j + 1] = base64_encode_table[(v >> 12) & 0x3F];
-    if (i + 1 < len) {
+
+    if (i + 1 < dst_len) {
 
       dst[j + 2] = base64_encode_table[(v >> 6) & 0x3F];
 
@@ -755,7 +766,7 @@ static void to_base64(u8 *src, u8 *dst, u32 dst_len) {
 
     }
 
-    if (i + 2 < len) {
+    if (i + 2 < dst_len) {
 
       dst[j + 3] = base64_encode_table[v & 0x3F];
 
@@ -767,12 +778,18 @@ static void to_base64(u8 *src, u8 *dst, u32 dst_len) {
 
   }
 
+  dst[len] = 0;
+  return len;
+
 }
 
+#ifdef WORD_SIZE_64
+static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h,
+                               u128 pattern, u128 repl, u128 o_pattern,
+                               u128 changed_val, u8 attr, u32 idx,
+                               u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf,
+                               u32 len, u8 do_reverse, u8 lvl, u8 *status);
 #endif
-
-//#endif
-
 static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
                               u64 pattern, u64 repl, u64 o_pattern,
                               u64 changed_val, u8 attr, u32 idx, u32 taint_len,
@@ -786,53 +803,89 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
   u64 *o_buf_64 = (u64 *)&orig_buf[idx];
   u32 *o_buf_32 = (u32 *)&orig_buf[idx];
   u16 *o_buf_16 = (u16 *)&orig_buf[idx];
-  u8  *o_buf_8 = &orig_buf[idx];
+  // u8  *o_buf_8 = &orig_buf[idx];
 
   u32 its_len = MIN(len - idx, taint_len);
 
-  if (afl->fsrv.total_execs - last_update > screen_update) {
+  if (unlikely(afl->fsrv.total_execs - last_update > screen_update)) {
 
     show_stats(afl);
     last_update = afl->fsrv.total_execs;
 
   }
 
-  // fprintf(stderr,
-  //         "Encode: %llx->%llx into %llx(<-%llx) at idx=%u "
-  //         "taint_len=%u shape=%u attr=%u\n",
-  //         o_pattern, pattern, repl, changed_val, idx, taint_len,
-  //         hshape, attr);
+  /*
+  fprintf(stderr,
+          "Encode: %llx->%llx into %llx(<-%llx) at idx=%u "
+          "taint_len=%u shape=%u attr=%u\n",
+          o_pattern, pattern, repl, changed_val, idx, taint_len,
+          hshape, attr);
+  */
+
+  u8 bytes;
+
+  switch (hshape) {
 
-  //#ifdef CMPLOG_SOLVE_TRANSFORM
-  // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3
+    case 0:
+    case 1:
+      bytes = 1;
+      break;
+    case 2:
+      bytes = 2;
+      break;
+    case 3:
+    case 4:
+      bytes = 4;
+      break;
+    default:
+      bytes = 8;
+
+  }
+
+  // necessary for preventing heap access overflow
+  bytes = MIN(bytes, len - idx);
+  if (unlikely(bytes <= 1)) { return 0; }
+
+  //  reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3
   if (afl->cmplog_enable_transform && (lvl & LVL3)) {
 
     u8                *endptr;
     u8                 use_num = 0, use_unum = 0;
-    unsigned long long unum;
-    long long          num;
+    unsigned long long unum = 0;
+    long long          num = 0;
+
+    // if (afl->queue_cur->is_ascii) {
 
-    if (afl->queue_cur->is_ascii) {
+    // we first check if our input are ascii numbers that are transformed to
+    // an integer and used for comparison:
 
-      endptr = buf_8;
-      if (strntoll(buf_8, len - idx, (char **)&endptr, 0, &num)) {
+    endptr = buf_8;
+    if (strntoll(buf_8, len - idx, (char **)&endptr, 0, &num)) {
 
-        if (!strntoull(buf_8, len - idx, (char **)&endptr, 0, &unum))
-          use_unum = 1;
+      if (!strntoull(buf_8, len - idx, (char **)&endptr, 0, &unum)) {
 
-      } else
+        use_unum = 1;
 
-        use_num = 1;
+      }
+
+    } else {
+
+      use_num = 1;
 
     }
 
+    //}
+
 #ifdef _DEBUG
     if (idx == 0)
-      fprintf(stderr, "ASCII is=%u use_num=%u use_unum=%u idx=%u %llx==%llx\n",
-              afl->queue_cur->is_ascii, use_num, use_unum, idx, num, pattern);
+      fprintf(stderr,
+              "ASCII is=%u use_num=%u>%lld use_unum=%u>%llu idx=%u "
+              "pattern=0x%llx\n",
+              afl->queue_cur->is_ascii, use_num, num, use_unum, unum, idx,
+              pattern);
 #endif
 
-    // num is likely not pattern as atoi("AAA") will be zero...
+    // atoi("AAA") == 0 so !num means we have to investigate
     if (use_num && ((u64)num == pattern || !num)) {
 
       u8     tmp_buf[32];
@@ -881,29 +934,6 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
     if (pattern != o_pattern && repl == changed_val && attr <= IS_EQUAL) {
 
       u64 b_val, o_b_val, mask;
-      u8  bytes;
-
-      switch (hshape) {
-
-        case 0:
-        case 1:
-          bytes = 1;
-          break;
-        case 2:
-          bytes = 2;
-          break;
-        case 3:
-        case 4:
-          bytes = 4;
-          break;
-        default:
-          bytes = 8;
-
-      }
-
-      // necessary for preventing heap access overflow
-      bytes = MIN(bytes, len - idx);
-
       switch (bytes) {
 
         case 0:                        // cannot happen
@@ -961,10 +991,12 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
       // test for arithmetic, eg. "if ((user_val - 0x1111) == 0x1234) ..."
       s64 diff = pattern - b_val;
       s64 o_diff = o_pattern - o_b_val;
-      /* fprintf(stderr, "DIFF1 idx=%03u shape=%02u %llx-%llx=%lx\n", idx,
-                 hshape, o_pattern, o_b_val, o_diff);
-         fprintf(stderr, "DIFF1 %016llx %llx-%llx=%lx\n", repl, pattern,
-                 b_val, diff); */
+      /*
+             fprintf(stderr, "DIFF1 idx=%03u shape=%02u %llx-%llx=%lx\n", idx,
+                       hshape, o_pattern, o_b_val, o_diff);
+               fprintf(stderr, "DIFF1 %016llx %llx-%llx=%lx\n", repl, pattern,
+                       b_val, diff);
+      */
       if (diff == o_diff && diff) {
 
         // this could be an arithmetic transformation
@@ -1120,7 +1152,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
 
   }
 
-  //#endif
+  // #endif
 
   // we only allow this for ascii2integer (above) so leave if this is the case
   if (unlikely(pattern == o_pattern)) { return 0; }
@@ -1243,6 +1275,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
 
     }
 
+    /*
     if (*status != 1) {  // u8
 
       // if (its_len >= 1)
@@ -1267,15 +1300,145 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
 
     }
 
+    */
+
   }
 
-  // here we add and subract 1 from the value, but only if it is not an
+  // If 'S' is set for cmplog mode then we try a scale encoding of the value.
+  // Currently we can only handle bytes up to 1 << 55 on 32 bit and 1 << 119
+  // on 64 bit systems.
+  // Caveat: This implementation here works only on little endian systems.
+
+  if (attr < IS_FP && (afl->cmplog_enable_scale || lvl >= LVL3) &&
+      repl == changed_val) {
+
+    u8  do_call = 1;
+    u64 new_val = repl << 2;
+    u32 ilen = 0;
+
+    if (changed_val <= 255) {
+
+      ilen = 1;
+
+    } else if (new_val <= 65535) {
+
+      new_val += 1;  // two byte mode
+      ilen = 2;
+
+    } else if (new_val <= 4294967295) {
+
+      new_val += 2;  // four byte mode
+      ilen = 4;
+
+    } else {
+
+#ifndef WORD_SIZE_64
+      if (repl <= 0x00ffffffffffffff) {
+
+        new_val = repl << 8;
+        u8  scale_len = 0;
+        u64 tmp_val = repl;
+        while (tmp_val) {
+
+          tmp_val >>= 8;
+          ++scale_len;
+
+        }  // scale_len will be >= 4;
+
+        if (scale_len >= 4) {
+
+          scale_len -= 4;
+
+        } else {
+
+          scale_len = 0;
+
+        };
+
+        new_val += (scale_len << 2) + 3;
+        ilen = scale_len + 5;
+
+      } else {
+
+        do_call = 0;
+
+      }
+
+#else
+      {
+
+        u128 new_vall = ((u128)repl) << 8;
+        u8   scale_len = 0;
+        u128 tmp_val = (u128)repl;
+
+        while (tmp_val) {
+
+          tmp_val >>= 8;
+          ++scale_len;
+
+        }  // scale_len will be >= 4;
+
+        if (scale_len >= 4) {
+
+          scale_len -= 4;
+
+        } else {
+
+          scale_len = 0;
+
+        };
+
+        new_vall += (scale_len << 2) + 3;
+        ilen = scale_len + 5;
+
+        if (ilen <= its_len && ilen > 1) {
+
+          u8 tmpbuf[32];
+          memcpy(tmpbuf, buf + idx, ilen);
+          memcpy(buf + idx, (char *)&new_vall, ilen);
+
+          if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+  #ifdef CMPLOG_COMBINE
+          if (*status == 1) { memcpy(cbuf + idx, (char *)&new_vall, ilen); }
+  #endif
+          memcpy(buf + idx, tmpbuf, ilen);
+
+        };
+
+        do_call = 0;
+
+      }
+
+#endif
+
+    }
+
+    if (do_call) {
+
+      if (ilen <= its_len && ilen > 1) {
+
+        u8 tmpbuf[32];
+        memcpy(tmpbuf, buf + idx, ilen);
+        memcpy(buf + idx, (char *)&new_val, ilen);
+
+        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+#ifdef CMPLOG_COMBINE
+        if (*status == 1) { memcpy(cbuf + idx, (char *)&new_val, ilen); }
+#endif
+        memcpy(buf + idx, tmpbuf, ilen);
+
+      };
+
+    }
+
+  }
+
+  // here we add and subtract 1 from the value, but only if it is not an
   // == or != comparison
   // Bits: 1 = Equal, 2 = Greater, 4 = Lesser, 8 = Float
   //       16 = modified float, 32 = modified integer (modified = wont match
   //                                                   in original buffer)
 
-  //#ifdef CMPLOG_SOLVE_ARITHMETIC
   if (!afl->cmplog_enable_arith || lvl < LVL3 || attr == IS_TRANSFORM) {
 
     return 0;
@@ -1440,8 +1603,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
 
   }
 
-  //#endif                                           /*
-  // CMPLOG_SOLVE_ARITHMETIC
+  // #endif                                           /*
+  //  CMPLOG_SOLVE_ARITHMETIC
 
   return 0;
 
@@ -1455,7 +1618,7 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h,
                                u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf,
                                u32 len, u8 do_reverse, u8 lvl, u8 *status) {
 
-  if (afl->fsrv.total_execs - last_update > screen_update) {
+  if (unlikely(afl->fsrv.total_execs - last_update > screen_update)) {
 
     show_stats(afl);
     last_update = afl->fsrv.total_execs;
@@ -1536,6 +1699,77 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h,
 
     }
 
+    // Scale encoding only works on little endian systems
+
+    if (attr < IS_FP && attr < 32 &&
+        (afl->cmplog_enable_scale || lvl >= LVL3)) {
+
+      u128 new_val = repl << 2;
+      u128 max_scale = (u128)1 << 120;
+      u32  ilen = 0;
+      u8   do_call = 1;
+
+      if (new_val <= 255) {
+
+        ilen = 1;
+
+      } else if (new_val <= 65535) {
+
+        new_val += 1;  // two byte mode
+        ilen = 2;
+
+      } else if (new_val <= 4294967295) {
+
+        new_val += 2;  // four byte mode
+        ilen = 4;
+
+      } else if (repl < max_scale) {
+
+        new_val = (u128)repl << 8;
+        u8   scale_len = 0;
+        u128 tmp_val = (u128)repl;
+        while (tmp_val) {
+
+          tmp_val >>= 8;
+          ++scale_len;
+
+        }  // scale_len will be >= 4;
+
+        if (scale_len >= 4) {
+
+          scale_len -= 4;
+
+        } else {
+
+          scale_len = 0;
+
+        };
+
+        new_val += (scale_len << 2) + 3;
+        ilen = scale_len + 5;
+
+      } else {
+
+        do_call = 0;
+
+      }
+
+      if (do_call && ilen <= its_len) {
+
+        u8 tmpbuf[32];
+        memcpy(tmpbuf, buf + idx, ilen);
+        memcpy(buf + idx, (char *)&new_val, ilen);
+
+        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+  #ifdef CMPLOG_COMBINE
+        if (*status == 1) { memcpy(cbuf + idx, (char *)&new_val, ilen); }
+  #endif
+        memcpy(buf + idx, tmpbuf, ilen);
+
+      };
+
+    }
+
   }
 
   return 0;
@@ -1606,7 +1840,7 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) {
   for (k = 0; k < size; ++k) {
 
   #else
-  u32    off = 16 - size;
+  u32 off = 16 - size;
   for (k = 16 - size; k < 16; ++k) {
 
   #endif
@@ -1659,6 +1893,8 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
 
   hshape = SHAPE_BYTES(h->shape);
 
+  if (hshape < 2) { return 0; }
+
   if (h->hits > CMP_MAP_H) {
 
     loggeds = CMP_MAP_H;
@@ -1721,6 +1957,19 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
 
     }
 
+#ifdef USE_HASHMAP
+    // TODO: add attribute? not sure
+    if (hshape <= 8 && hashmap_search_and_add(hshape - 1, o->v0) &&
+        hashmap_search_and_add(hshape - 1, orig_o->v0) &&
+        hashmap_search_and_add(hshape - 1, o->v1) &&
+        hashmap_search_and_add(hshape - 1, orig_o->v1)) {
+
+      continue;
+
+    }
+
+#endif
+
 #ifdef _DEBUG
     fprintf(stderr, "Handling: %llx->%llx vs %llx->%llx attr=%u shape=%u\n",
             orig_o->v0, o->v0, orig_o->v1, o->v1, h->attribute, hshape);
@@ -1948,11 +2197,11 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 #ifndef CMPLOG_COMBINE
   (void)(cbuf);
 #endif
-  //#ifndef CMPLOG_SOLVE_TRANSFORM
-  //  (void)(changed_val);
-  //#endif
+  // #ifndef CMPLOG_SOLVE_TRANSFORM
+  //   (void)(changed_val);
+  // #endif
 
-  if (afl->fsrv.total_execs - last_update > screen_update) {
+  if (unlikely(afl->fsrv.total_execs - last_update > screen_update)) {
 
     show_stats(afl);
     last_update = afl->fsrv.total_execs;
@@ -1988,29 +2237,35 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 
   if (l0 >= 0x80 || ol0 >= 0x80) {
 
-    l0 -= 0x80;
-    l1 -= 0x80;
-    ol0 -= 0x80;
-    ol1 -= 0x80;
+    if (l0 >= 0x80) { l0 -= 0x80; }
+    if (l1 >= 0x80) { l1 -= 0x80; }
+    if (ol0 >= 0x80) { ol0 -= 0x80; }
+    if (ol1 >= 0x80) { ol1 -= 0x80; }
 
   }
 
-  if (l0 == 0 || l1 == 0 || ol0 == 0 || ol1 == 0 || l0 > 31 || l1 > 31 ||
-      ol0 > 31 || ol1 > 31) {
+  if (l0 == 0 || l1 == 0 || ol0 == 0 || ol1 == 0 || l0 > 32 || l1 > 32 ||
+      ol0 > 32 || ol1 > 32) {
 
     l0 = ol0 = hshape;
 
   }
 
   u8  lmax = MAX(l0, ol0);
-  u8  save[40];
+  u8  save[80];
   u32 saved_idx = idx, pre, from = 0, to = 0, i, j;
   u32 its_len = MIN(MIN(lmax, hshape), len - idx);
   its_len = MIN(its_len, taint_len);
   u32 saved_its_len = its_len;
 
+  // fprintf(stderr, "its_len=%u repl=%s\n", its_len, repl);
+
+  if (its_len <= 1) { return 0; }
+
   if (lvl & LVL3) {
 
+    if (memcmp(changed_val, repl, its_len) != 0) { return 0; }
+
     u32 max_to = MIN(4U, idx);
     if (!(lvl & LVL1) && max_to) { from = 1; }
     to = max_to;
@@ -2021,27 +2276,32 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
   (void)(j);
 
 #ifdef _DEBUG
-  fprintf(stderr, "RTN T idx=%u lvl=%02x is_txt=%u shape=%u/%u ", idx, lvl,
-          o->v0_len >= 0x80 ? 1 : 0, hshape, l0);
-  for (j = 0; j < 8; j++)
-    fprintf(stderr, "%02x", orig_buf[idx + j]);
-  fprintf(stderr, " -> ");
-  for (j = 0; j < 8; j++)
-    fprintf(stderr, "%02x", o_pattern[j]);
-  fprintf(stderr, " <= ");
-  for (j = 0; j < 8; j++)
-    fprintf(stderr, "%02x", repl[j]);
-  fprintf(stderr, "\n");
-  fprintf(stderr, "                ");
-  for (j = 0; j < 8; j++)
-    fprintf(stderr, "%02x", buf[idx + j]);
-  fprintf(stderr, " -> ");
-  for (j = 0; j < 8; j++)
-    fprintf(stderr, "%02x", pattern[j]);
-  fprintf(stderr, " <= ");
-  for (j = 0; j < 8; j++)
-    fprintf(stderr, "%02x", changed_val[j]);
-  fprintf(stderr, "\n");
+  if (idx == 0) {
+
+    fprintf(stderr, "RTN T idx=%u lvl=%02x is_txt=%u shape=%u/%u ", idx, lvl,
+            o->v0_len >= 0x80 ? 1 : 0, hshape, l0);
+    for (j = 0; j < 8; j++)
+      fprintf(stderr, "%02x", orig_buf[idx + j]);
+    fprintf(stderr, " -> ");
+    for (j = 0; j < 8; j++)
+      fprintf(stderr, "%02x", o_pattern[j]);
+    fprintf(stderr, " <= ");
+    for (j = 0; j < 8; j++)
+      fprintf(stderr, "%02x", repl[j]);
+    fprintf(stderr, "\n");
+    fprintf(stderr, "                ");
+    for (j = 0; j < 8; j++)
+      fprintf(stderr, "%02x", buf[idx + j]);
+    fprintf(stderr, " -> ");
+    for (j = 0; j < 8; j++)
+      fprintf(stderr, "%02x", pattern[j]);
+    fprintf(stderr, " <= ");
+    for (j = 0; j < 8; j++)
+      fprintf(stderr, "%02x", changed_val[j]);
+    fprintf(stderr, "\n");
+
+  }
+
 #endif
 
   // Try to match the replace value up to 4 bytes before the current idx.
@@ -2050,6 +2310,9 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
   //     if (memcmp(user_val, "TEST-VALUE") == 0) ...
   // We only do this in lvl 3, otherwise we only do direct matching
 
+  // fprintf(stderr, "XXXX FROMB64 saved_idx=%u its_len=%u from=%u to=%u FROMHEX
+  // repl=%s\n", saved_idx, saved_its_len, from, to, repl);
+
   for (pre = from; pre <= to; pre++) {
 
     if (*status != 1 && (!pre || !memcmp(buf + saved_idx - pre, repl, pre))) {
@@ -2059,7 +2322,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 
       for (i = 0; i < its_len; ++i) {
 
-        if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) ||
+        if ((pattern[i] != buf[idx + i] || o_pattern[i] != orig_buf[idx + i]) ||
             *status == 1) {
 
           break;
@@ -2089,12 +2352,10 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
   if (afl->cmplog_enable_transform && (lvl & LVL3)) {
 
     u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, fromhex = 0;
-#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
     u32 tob64 = 0, fromb64 = 0;
-#endif
     u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_up = 0;
     u32 to_0 = 0, to_x = 0, to_slash = 0, to_up = 0;
-    u8  xor_val[32], arith_val[32], tmp[48];
+    u8  xor_val[64], arith_val[64], tmp[64];
 
     idx = saved_idx;
     its_len = saved_its_len;
@@ -2144,7 +2405,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 
       }
 
-      if (i < 16 && is_hex(repl + (i << 1))) {
+      if (afl->cmplog_enable_xtreme_transform && i < 16 &&
+          is_hex(repl + (i << 1))) {
 
         ++tohex;
 
@@ -2163,9 +2425,9 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 
       }
 
-      if ((i % 2)) {
+      if (afl->cmplog_enable_xtreme_transform && (i % 2) == 1) {
 
-        if (len > idx + i + 1 && is_hex(orig_buf + idx + i)) {
+        if (len > idx + i + 1 && is_hex(orig_buf + idx + i - 1)) {
 
           fromhex += 2;
 
@@ -2187,20 +2449,23 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 
       }
 
-#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
-      if (i % 3 == 2 && i < 24) {
+      if (afl->cmplog_enable_xtreme_transform) {
 
-        if (is_base64(repl + ((i / 3) << 2))) tob64 += 3;
+        if (i % 3 == 2 && i < 24) {
 
-      }
+          if (is_base64(repl + ((i / 3) << 2))) tob64 += 3;
 
-      if (i % 4 == 3 && i < 24) {
+        }
 
-        if (is_base64(orig_buf + idx + i - 3)) fromb64 += 4;
+        // fprintf(stderr, "X FROMB64 idx=%u i=%u repl=%s\n", saved_idx, i,
+        // repl);
+        if (i % 4 == 3 && i < 24) {
 
-      }
+          if (is_base64(orig_buf + idx + i - 3)) fromb64 += 4;
 
-#endif
+        }
+
+      }
 
       if ((o_pattern[i] ^ orig_buf[idx + i]) == xor_val[i] && xor_val[i]) {
 
@@ -2229,45 +2494,70 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
       }
 
 #ifdef _DEBUG
-      fprintf(stderr,
-              "RTN idx=%u loop=%u xor=%u arith=%u tolower=%u toupper=%u "
-              "tohex=%u fromhex=%u to_0=%u to_slash=%u to_x=%u "
-              "from_0=%u from_slash=%u from_x=%u\n",
-              idx, i, xor, arith, tolower, toupper, tohex, fromhex, to_0,
-              to_slash, to_x, from_0, from_slash, from_x);
-  #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
-      fprintf(stderr, "RTN idx=%u loop=%u tob64=%u from64=%u\n", tob64,
-              fromb64);
-  #endif
+      if (idx == 0) {
+
+        fprintf(stderr, "RTN Z %s %s %s %s repl=%s\n", buf, pattern, orig_buf,
+                o_pattern, repl);
+        fprintf(
+            stderr,
+            "RTN Z idx=%u len=%u loop=%u xor=%u arith=%u tolower=%u toupper=%u "
+            "tohex=%u fromhex=%u to_0=%u to_slash=%u to_x=%u "
+            "from_0=%u from_slash=%u from_x=%u\n",
+            idx, its_len, i, xor, arith, tolower, toupper, tohex, fromhex, to_0,
+            to_slash, to_x, from_0, from_slash, from_x);
+        if (afl->cmplog_enable_xtreme_transform) {
+
+          fprintf(stderr, "RTN Z idx=%u loop=%u tob64=%u from64=%u\n", idx, i,
+                  tob64, fromb64);
+
+        }
+
+      }
+
 #endif
 
-#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
-      // input is base64 and converted to binary? convert repl to base64!
-      if ((i % 4) == 3 && i < 24 && fromb64 > i) {
+      if (afl->cmplog_enable_xtreme_transform) {
 
-        to_base64(repl, tmp, i + 1);
-        memcpy(buf + idx, tmp, i + 1);
-        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
-        // fprintf(stderr, "RTN ATTEMPT fromb64 %u result %u\n", fromb64,
-        // *status);
+        // input is base64 and converted to binary? convert repl to base64!
+        // fprintf(stderr, "FROMB64 idx=%u i=%u %% 4 == 3 && i < 24 &&
+        // fromb64=%u > i, repl=%s\n", saved_idx, i, fromb64, repl);
+        if ((i % 4) == 3 && i < 24 && fromb64 > i) {
 
-      }
+          for (u32 hlen = i; hlen + saved_idx < len && hlen <= its_len;
+               ++hlen) {
 
-      // input is converted to base64? decode repl with base64!
-      if ((i % 3) == 2 && i < 24 && tob64 > i) {
+            u32 res = to_base64(repl, tmp, hlen);
+            // fprintf(stderr, "FROMB64 GOGO! idx=%u repl=%s tmp[%u]=%s
+            // hlen=%u\n", saved_idx, repl, res, tmp, hlen);
+            if (res + saved_idx < len) {
 
-        u32 olen = from_base64(repl, tmp, i + 1);
-        memcpy(buf + idx, tmp, olen);
-        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
-        // fprintf(stderr, "RTN ATTEMPT tob64 %u idx=%u result %u\n", tob64,
-        // idx, *status);
+              memcpy(buf + idx, tmp, res);
+              if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+              // fprintf(stderr, "RTN ATTEMPT FROMB64 idx=%u fromb64 %u %s %s
+              // result %u\n",       saved_idx,      fromb64,      tmp, repl,
+              // *status);
 
-      }
+            }
 
-#endif
+          }
+
+        }
+
+        // input is converted to base64? decode repl with base64!
+        if ((i % 3) == 2 && i < 24 && tob64 > i) {
+
+          u32 olen = from_base64(repl, tmp, i + 1);
+          memcpy(buf + idx, tmp, olen);
+          if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+          // fprintf(stderr, "RTN ATTEMPT tob64 %u idx=%u result %u\n", tob64,
+          // idx, *status);
+
+        }
+
+      }
 
       // input is converted to hex? convert repl to binary!
-      if (i < 16 && tohex > i) {
+      if (afl->cmplog_enable_xtreme_transform && i < 16 && tohex > i) {
 
         u32 off;
         if (to_slash + to_x + to_0 == 2) {
@@ -2292,8 +2582,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
       }
 
       // input is hex and converted to binary? convert repl to hex!
-      if (i && (i % 2) && i < 16 && fromhex &&
-          fromhex + from_slash + from_x + from_0 > i) {
+      if (afl->cmplog_enable_xtreme_transform && (i % 2) == 1 && i < 16 &&
+          fromhex && fromhex + from_slash + from_x + from_0 > i) {
 
         u8 off = 0;
         if (from_slash && from_x) {
@@ -2328,31 +2618,37 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 
         }
 
-        if (to_up == 1) {
+        for (u32 hlen = i; hlen <= (i << 1) && hlen + idx < len; hlen += i) {
 
-          for (j = 0; j <= (i >> 1); j++) {
+          if (to_up == 1) {
 
-            tmp[off + (j << 1)] = hex_table_up[repl[j] >> 4];
-            tmp[off + (j << 1) + 1] = hex_table_up[repl[j] % 16];
+            for (j = 0; j <= (hlen >> 1); j++) {
 
-          }
+              tmp[off + (j << 1)] = hex_table_up[repl[j] >> 4];
+              tmp[off + (j << 1) + 1] = hex_table_up[repl[j] % 16];
 
-        } else {
+            }
+
+          } else {
 
-          for (j = 0; j <= (i >> 1); j++) {
+            for (j = 0; j <= (hlen >> 1); j++) {
 
-            tmp[off + (j << 1)] = hex_table_low[repl[j] >> 4];
-            tmp[off + (j << 1) + 1] = hex_table_low[repl[j] % 16];
+              tmp[off + (j << 1)] = hex_table_low[repl[j] >> 4];
+              tmp[off + (j << 1) + 1] = hex_table_low[repl[j] % 16];
+
+            }
 
           }
 
-        }
+          u32 tmp_l = hlen + 1 + off;
+          memcpy(buf + idx, tmp, tmp_l);
+          if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
+          tmp[tmp_l] = 0;
+          // fprintf(stderr, "RTN ATTEMPT idx=%u len=%u fromhex %u %s %s result
+          // %u\n", idx, len, fromhex, tmp, repl, *status);
+          memcpy(buf + idx, save, tmp_l);
 
-        memcpy(buf + idx, tmp, i + 1 + off);
-        if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
-        // fprintf(stderr, "RTN ATTEMPT fromhex %u result %u\n", fromhex,
-        // *status);
-        memcpy(buf + idx, save, i + 1 + off);
+        }
 
       }
 
@@ -2401,11 +2697,9 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 
       if ((i >= 7 &&
            (i >= xor&&i >= arith &&i >= tolower &&i >= toupper &&i > tohex &&i >
-                (fromhex + from_0 + from_x + from_slash + 1)
-#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
-            && i > tob64 + 3 && i > fromb64 + 4
-#endif
-            )) ||
+                (fromhex + from_0 + from_x + from_slash + 1) &&
+            (afl->cmplog_enable_xtreme_transform && i > tob64 + 3 &&
+             i > fromb64 + 4))) ||
           repl[i] != changed_val[i] || *status == 1) {
 
         break;
@@ -2418,8 +2712,6 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
 
   }
 
-  //#endif
-
   return 0;
 
 }
@@ -2429,11 +2721,13 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
 
   struct tainted    *t;
   struct cmp_header *h = &afl->shm.cmp_map->headers[key];
-  u32                i, j, idx, have_taint = 1, taint_len, loggeds;
+  u32                i, idx, have_taint = 1, taint_len, loggeds;
   u8                 status = 0, found_one = 0;
 
   hshape = SHAPE_BYTES(h->shape);
 
+  if (hshape < 2) { return 0; }
+
   if (h->hits > CMP_MAP_RTN_H) {
 
     loggeds = CMP_MAP_RTN_H;
@@ -2452,36 +2746,52 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
     struct cmpfn_operands *orig_o =
         &((struct cmpfn_operands *)afl->orig_cmp_map->log[key])[i];
 
-    // opt not in the paper
-    for (j = 0; j < i; ++j) {
+    /*
+        // opt not in the paper
+        for (j = 0; j < i; ++j) {
 
-      if (!memcmp(&((struct cmpfn_operands *)afl->shm.cmp_map->log[key])[j], o,
-                  sizeof(struct cmpfn_operands))) {
+          if (!memcmp(&((struct cmpfn_operands *)afl->shm.cmp_map->log[key])[j],
+       o, sizeof(struct cmpfn_operands))) {
 
-        goto rtn_fuzz_next_iter;
+            goto rtn_fuzz_next_iter;
 
-      }
+          }
 
-    }
+        }
 
-    /*
+    */
+
+#ifdef _DEBUG
+    u32                j;
     struct cmp_header *hh = &afl->orig_cmp_map->headers[key];
-    fprintf(stderr, "RTN N hits=%u id=%u shape=%u attr=%u v0=", h->hits, h->id,
-            hshape, h->attribute);
+    fprintf(stderr, "RTN N hits=%u shape=%u attr=%u v0=", h->hits, hshape,
+            h->attribute);
     for (j = 0; j < 8; j++)
       fprintf(stderr, "%02x", o->v0[j]);
     fprintf(stderr, " v1=");
     for (j = 0; j < 8; j++)
       fprintf(stderr, "%02x", o->v1[j]);
-    fprintf(stderr, "\nRTN O hits=%u id=%u shape=%u attr=%u o0=", hh->hits,
-            hh->id, hshape, hh->attribute);
+    fprintf(stderr, "\nRTN O hits=%u shape=%u attr=%u o0=", hh->hits, hshape,
+            hh->attribute);
     for (j = 0; j < 8; j++)
       fprintf(stderr, "%02x", orig_o->v0[j]);
     fprintf(stderr, " o1=");
     for (j = 0; j < 8; j++)
       fprintf(stderr, "%02x", orig_o->v1[j]);
     fprintf(stderr, "\n");
-    */
+#endif
+
+#ifdef USE_HASHMAP
+    if (hshape <= 8 && hashmap_search_and_add_ptr(hshape - 1, o->v0) &&
+        hashmap_search_and_add_ptr(hshape - 1, orig_o->v0) &&
+        hashmap_search_and_add_ptr(hshape - 1, o->v1) &&
+        hashmap_search_and_add_ptr(hshape - 1, orig_o->v1)) {
+
+      continue;
+
+    }
+
+#endif
 
     t = taint;
     while (t->next) {
@@ -2515,7 +2825,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
       status = 0;
 
 #ifdef _DEBUG
-      int w;
+      u32 w;
       fprintf(stderr, "key=%u idx=%u len=%u o0=", key, idx, hshape);
       for (w = 0; w < hshape; ++w)
         fprintf(stderr, "%02x", orig_o->v0[w]);
@@ -2592,6 +2902,8 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
       // shape_len), check_if_text_buf((u8 *)&o->v1, shape_len), v0_len,
       // o->v0, v1_len, o->v1);
 
+      // Note that this check differs from the line 1901, for RTN we are more
+      // opportunistic for adding to the dictionary than cmps
       if (!memcmp(o->v0, orig_o->v0, v0_len) ||
           (!found_one || check_if_text_buf((u8 *)&o->v0, v0_len) == v0_len))
         maybe_add_auto(afl, o->v0, v0_len);
@@ -2603,7 +2915,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
 
     }
 
-  rtn_fuzz_next_iter:
+    // rtn_fuzz_next_iter:
     afl->stage_cur++;
 
   }
@@ -2747,6 +3059,10 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) {
 
   // Start insertion loop
 
+#ifdef USE_HASHMAP
+  hashmap_reset();
+#endif
+
   u64 orig_hit_cnt, new_hit_cnt;
   u64 orig_execs = afl->fsrv.total_execs;
   orig_hit_cnt = afl->queued_items + afl->saved_crashes;
@@ -2816,12 +3132,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) {
 
       }
 
-    } else if ((lvl & LVL1)
-
-               //#ifdef CMPLOG_SOLVE_TRANSFORM
-               || ((lvl & LVL3) && afl->cmplog_enable_transform)
-               //#endif
-    ) {
+    } else if ((lvl & LVL1) || ((lvl & LVL3) && afl->cmplog_enable_transform)) {
 
       if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) {
 
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index ac4fb4a9..b62db1ea 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -5,12 +5,12 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com> and
                         Dominik Maier <mail@dmnk.co>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -60,6 +60,23 @@ fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) {
 
   fsrv_run_result_t res = afl_fsrv_run_target(fsrv, timeout, &afl->stop_soon);
 
+  /* If post_run() function is defined in custom mutator, the function will be
+     called each time after AFL++ executes the target program. */
+
+  if (unlikely(afl->custom_mutators_count)) {
+
+    LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
+
+      if (unlikely(el->afl_custom_post_run)) {
+
+        el->afl_custom_post_run(el->data);
+
+      }
+
+    });
+
+  }
+
 #ifdef PROFILING
   clock_gettime(CLOCK_REALTIME, &spec);
   time_spent_start = (spec.tv_sec * 1000000000) + spec.tv_nsec;
@@ -152,20 +169,16 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) {
 
     }
 
-    if (unlikely(afl->custom_mutators_count)) {
-
-      LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
-
-        if (el->afl_custom_fuzz_send) {
+    LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
 
-          el->afl_custom_fuzz_send(el->data, *mem, new_size);
-          sent = 1;
+      if (el->afl_custom_fuzz_send) {
 
-        }
+        el->afl_custom_fuzz_send(el->data, *mem, new_size);
+        sent = 1;
 
-      });
+      }
 
-    }
+    });
 
     if (likely(!sent)) {
 
@@ -186,7 +199,7 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) {
 
     }
 
-  } else {
+  } else {                                   /* !afl->custom_mutators_count */
 
     if (unlikely(len < afl->min_length && !fix)) {
 
@@ -198,27 +211,8 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) {
 
     }
 
-    if (unlikely(afl->custom_mutators_count)) {
-
-      LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
-
-        if (el->afl_custom_fuzz_send) {
-
-          el->afl_custom_fuzz_send(el->data, *mem, len);
-          sent = 1;
-
-        }
-
-      });
-
-    }
-
-    if (likely(!sent)) {
-
-      /* boring uncustom. */
-      afl_fsrv_write_to_testcase(&afl->fsrv, *mem, len);
-
-    }
+    /* boring uncustom. */
+    afl_fsrv_write_to_testcase(&afl->fsrv, *mem, len);
 
   }
 
@@ -415,6 +409,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
   u32 use_tmout = afl->fsrv.exec_tmout;
   u8 *old_sn = afl->stage_name;
 
+  u64 calibration_start_us = get_cur_time_us();
   if (unlikely(afl->shm.cmplog_mode)) { q->exec_cksum = 0; }
 
   /* Be a bit more generous about timeouts when resuming sessions, or when
@@ -510,6 +505,10 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
 
     fault = fuzz_run_target(afl, &afl->fsrv, use_tmout);
 
+    // update the time spend in calibration after each execution, as those may
+    // be slow
+    update_calibration_time(afl, &calibration_start_us);
+
     /* afl->stop_soon is set by the handler for Ctrl+C. When it's pressed,
        we want to bail out quickly. */
 
@@ -607,6 +606,8 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
   }
 
   q->exec_us = diff_us / afl->stage_max;
+  if (unlikely(!q->exec_us)) { q->exec_us = 1; }
+
   q->bitmap_size = count_bytes(afl, afl->fsrv.trace_bits);
   q->handicap = handicap;
   q->cal_failed = 0;
@@ -656,6 +657,7 @@ abort_calibration:
 
   if (!first_run) { show_stats(afl); }
 
+  update_calibration_time(afl, &calibration_start_us);
   return fault;
 
 }
@@ -675,11 +677,16 @@ void sync_fuzzers(afl_state_t *afl) {
   afl->stage_max = afl->stage_cur = 0;
   afl->cur_depth = 0;
 
+  u64 sync_start_us = get_cur_time_us();
   /* Look at the entries created for every other fuzzer in the sync directory.
    */
 
   while ((sd_ent = readdir(sd))) {
 
+    // since sync can take substantial amounts of time, update time spend every
+    // iteration
+    update_sync_time(afl, &sync_start_us);
+
     u8  qd_synced_path[PATH_MAX], qd_path[PATH_MAX];
     u32 min_accept = 0, next_min_accept = 0;
 
@@ -766,6 +773,8 @@ void sync_fuzzers(afl_state_t *afl) {
     afl->stage_cur = 0;
     afl->stage_max = 0;
 
+    show_stats(afl);
+
     /* For every file queued by this fuzzer, parse ID and see if we have
        looked at it before; exec a test case if not. */
 
@@ -817,15 +826,15 @@ void sync_fuzzers(afl_state_t *afl) {
         /* See what happens. We rely on save_if_interesting() to catch major
            errors and save the test case. */
 
-        (void)write_to_testcase(afl, (void **)&mem, st.st_size, 1);
+        u32 new_len = write_to_testcase(afl, (void **)&mem, st.st_size, 1);
 
         fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
 
         if (afl->stop_soon) { goto close_sync; }
 
         afl->syncing_party = sd_ent->d_name;
-        afl->queued_imported +=
-            save_if_interesting(afl, mem, st.st_size, fault);
+        afl->queued_imported += save_if_interesting(afl, mem, new_len, fault);
+        show_stats(afl);
         afl->syncing_party = 0;
 
         munmap(mem, st.st_size);
@@ -867,6 +876,9 @@ void sync_fuzzers(afl_state_t *afl) {
 
   if (afl->foreign_sync_cnt) read_foreign_testcases(afl, 0);
 
+  // add time in sync one last time
+  update_sync_time(afl, &sync_start_us);
+
   afl->last_sync_time = get_cur_time();
   afl->last_sync_cycle = afl->queue_cycle;
 
@@ -878,8 +890,9 @@ void sync_fuzzers(afl_state_t *afl) {
 
 u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
 
+  u8  needs_write = 0, fault = 0;
   u32 orig_len = q->len;
-
+  u64 trim_start_us = get_cur_time_us();
   /* Custom mutator trimmer */
   if (afl->custom_mutators_count) {
 
@@ -903,11 +916,15 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
 
     }
 
-    if (custom_trimmed) return trimmed_case;
+    if (custom_trimmed) {
+
+      fault = trimmed_case;
+      goto abort_trimming;
+
+    }
 
   }
 
-  u8  needs_write = 0, fault = 0;
   u32 trim_exec = 0;
   u32 remove_len;
   u32 len_p2;
@@ -918,7 +935,12 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
      detected, it will still work to some extent, so we don't check for
      this. */
 
-  if (q->len < 5) { return 0; }
+  if (unlikely(q->len < 5)) {
+
+    fault = 0;
+    goto abort_trimming;
+
+  }
 
   afl->stage_name = afl->stage_name_buf;
   afl->bytes_trim_in += q->len;
@@ -952,6 +974,8 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
 
       fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
 
+      update_trim_time(afl, &trim_start_us);
+
       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?
@@ -978,7 +1002,6 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
 
         /* 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) {
 
           needs_write = 1;
@@ -993,7 +1016,6 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
       }
 
       /* 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;
 
@@ -1008,6 +1030,68 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
 
   if (needs_write) {
 
+    // run afl_custom_post_process
+
+    if (unlikely(afl->custom_mutators_count) &&
+        likely(!afl->afl_env.afl_post_process_keep_original)) {
+
+      ssize_t new_size = q->len;
+      u8     *new_mem = in_buf;
+      u8     *new_buf = NULL;
+
+      LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
+
+        if (el->afl_custom_post_process) {
+
+          new_size = el->afl_custom_post_process(el->data, new_mem, new_size,
+                                                 &new_buf);
+
+          if (unlikely(!new_buf || new_size <= 0)) {
+
+            new_size = 0;
+            new_buf = new_mem;
+
+          } else {
+
+            new_mem = new_buf;
+
+          }
+
+        }
+
+      });
+
+      if (unlikely(!new_size)) {
+
+        new_size = q->len;
+        new_mem = in_buf;
+
+      }
+
+      if (unlikely(new_size < afl->min_length)) {
+
+        new_size = afl->min_length;
+
+      } else if (unlikely(new_size > afl->max_length)) {
+
+        new_size = afl->max_length;
+
+      }
+
+      q->len = new_size;
+
+      if (new_mem != in_buf && new_mem != NULL) {
+
+        new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), new_size);
+        if (unlikely(!new_buf)) { PFATAL("alloc"); }
+        memcpy(new_buf, new_mem, new_size);
+
+        in_buf = new_buf;
+
+      }
+
+    }
+
     s32 fd;
 
     if (unlikely(afl->no_unlink)) {
@@ -1045,8 +1129,9 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
   }
 
 abort_trimming:
-
   afl->bytes_trim_out += q->len;
+  update_trim_time(afl, &trim_start_us);
+
   return fault;
 
 }
@@ -1110,4 +1195,3 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
   return 0;
 
 }
-
diff --git a/src/afl-fuzz-skipdet.c b/src/afl-fuzz-skipdet.c
new file mode 100644
index 00000000..e52d59a3
--- /dev/null
+++ b/src/afl-fuzz-skipdet.c
@@ -0,0 +1,403 @@
+
+
+#include "afl-fuzz.h"
+
+void flip_range(u8 *input, u32 pos, u32 size) {
+
+  for (u32 i = 0; i < size; i++)
+    input[pos + i] ^= 0xFF;
+
+  return;
+
+}
+
+#define MAX_EFF_TIMEOUT (10 * 60 * 1000)
+#define MAX_DET_TIMEOUT (15 * 60 * 1000)
+u8 is_det_timeout(u64 cur_ms, u8 is_flip) {
+
+  if (is_flip) {
+
+    if (unlikely(get_cur_time() - cur_ms > MAX_EFF_TIMEOUT)) return 1;
+
+  } else {
+
+    if (unlikely(get_cur_time() - cur_ms > MAX_DET_TIMEOUT)) return 1;
+
+  }
+
+  return 0;
+
+}
+
+/* decide if the seed should be deterministically fuzzed */
+
+u8 should_det_fuzz(afl_state_t *afl, struct queue_entry *q) {
+
+  if (!afl->skipdet_g->virgin_det_bits) {
+
+    afl->skipdet_g->virgin_det_bits =
+        (u8 *)ck_alloc(sizeof(u8) * afl->fsrv.map_size);
+
+  }
+
+  if (!q->favored || q->passed_det) return 0;
+  if (!q->trace_mini) return 0;
+
+  if (!afl->skipdet_g->last_cov_undet)
+    afl->skipdet_g->last_cov_undet = get_cur_time();
+
+  if (get_cur_time() - afl->skipdet_g->last_cov_undet >= THRESHOLD_DEC_TIME) {
+
+    if (afl->skipdet_g->undet_bits_threshold >= 2) {
+
+      afl->skipdet_g->undet_bits_threshold *= 0.75;
+      afl->skipdet_g->last_cov_undet = get_cur_time();
+
+    }
+
+  }
+
+  u32 new_det_bits = 0;
+
+  for (u32 i = 0; i < afl->fsrv.map_size; i++) {
+
+    if (unlikely(q->trace_mini[i >> 3] & (1 << (i & 7)))) {
+
+      if (!afl->skipdet_g->virgin_det_bits[i]) { new_det_bits++; }
+
+    }
+
+  }
+
+  if (!afl->skipdet_g->undet_bits_threshold)
+    afl->skipdet_g->undet_bits_threshold = new_det_bits * 0.05;
+
+  if (new_det_bits >= afl->skipdet_g->undet_bits_threshold) {
+
+    afl->skipdet_g->last_cov_undet = get_cur_time();
+    q->skipdet_e->undet_bits = new_det_bits;
+
+    for (u32 i = 0; i < afl->fsrv.map_size; i++) {
+
+      if (unlikely(q->trace_mini[i >> 3] & (1 << (i & 7)))) {
+
+        if (!afl->skipdet_g->virgin_det_bits[i])
+          afl->skipdet_g->virgin_det_bits[i] = 1;
+
+      }
+
+    }
+
+    return 1;
+
+  }
+
+  return 0;
+
+}
+
+/*
+  consists of two stages that
+  return 0 if exec failed.
+*/
+
+u8 skip_deterministic_stage(afl_state_t *afl, u8 *orig_buf, u8 *out_buf,
+                            u32 len, u64 before_det_time) {
+
+  u64 orig_hit_cnt, new_hit_cnt;
+
+  if (afl->queue_cur->skipdet_e->done_eff) return 1;
+
+  if (!should_det_fuzz(afl, afl->queue_cur)) return 1;
+
+  /* Add check to make sure that for seeds without too much undet bits,
+     we ignore them */
+
+  /******************
+   * SKIP INFERENCE *
+   ******************/
+
+  afl->stage_short = "inf";
+  afl->stage_name = "inference";
+  afl->stage_cur = 0;
+  orig_hit_cnt = afl->queued_items + afl->saved_crashes;
+
+  u8 *inf_eff_map = (u8 *)ck_alloc(sizeof(u8) * len);
+  memset(inf_eff_map, 1, sizeof(u8) * len);
+
+  if (common_fuzz_stuff(afl, orig_buf, len)) { return 0; }
+
+  u64 prev_cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
+  u64 _prev_cksum = prev_cksum;
+
+  if (MINIMAL_BLOCK_SIZE * 8 < len) {
+
+    // u64 size_skiped = 0, quick_skip_exec = total_execs, quick_skip_time =
+    // get_cur_time();
+    u64 pre_inf_exec = afl->fsrv.total_execs, pre_inf_time = get_cur_time();
+
+    /* if determine stage time / input size is too small, just go ahead */
+
+    u32 pos = 0, cur_block_size = MINIMAL_BLOCK_SIZE, max_block_size = len / 8;
+
+    while (pos < len - 1) {
+
+      cur_block_size = MINIMAL_BLOCK_SIZE;
+
+      while (cur_block_size < max_block_size) {
+
+        u32 flip_block_size =
+            (cur_block_size + pos < len) ? cur_block_size : len - 1 - pos;
+
+        afl->stage_cur += 1;
+
+        flip_range(out_buf, pos, flip_block_size);
+
+        if (common_fuzz_stuff(afl, out_buf, len)) return 0;
+
+        flip_range(out_buf, pos, flip_block_size);
+
+        u64 cksum =
+            hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
+
+        // printf("Now trying range %d with %d, %s.\n", pos, cur_block_size,
+        //     (cksum == prev_cksum) ? (u8*)"Yes" : (u8*) "Not");
+
+        /* continue until we fail or exceed length */
+        if (cksum == _prev_cksum) {
+
+          cur_block_size *= 2;
+
+          if (cur_block_size >= len - 1 - pos) break;
+
+        } else {
+
+          break;
+
+        }
+
+      }
+
+      if (cur_block_size == MINIMAL_BLOCK_SIZE) {
+
+        /* we failed early on*/
+
+        pos += cur_block_size;
+
+      } else {
+
+        u32 cur_skip_len = (cur_block_size / 2 + pos < len)
+                               ? (cur_block_size / 2)
+                               : (len - pos - 1);
+
+        memset(inf_eff_map + pos, 0, cur_skip_len);
+
+        afl->skipdet_g->inf_prof->inf_skipped_bytes += cur_skip_len;
+
+        pos += cur_skip_len;
+
+      }
+
+    }
+
+    afl->skipdet_g->inf_prof->inf_execs_cost +=
+        (afl->fsrv.total_execs - pre_inf_exec);
+    afl->skipdet_g->inf_prof->inf_time_cost += (get_cur_time() - pre_inf_time);
+    // PFATAL("Done, now have %d bytes skipped, with exec %lld, time %lld.\n",
+    // afl->inf_skipped_bytes, afl->inf_execs_cost, afl->inf_time_cost);
+
+  } else
+
+    memset(inf_eff_map, 1, len);
+
+  new_hit_cnt = afl->queued_items + afl->saved_crashes;
+
+  afl->stage_finds[STAGE_INF] += new_hit_cnt - orig_hit_cnt;
+  afl->stage_cycles[STAGE_INF] += afl->stage_cur;
+
+  /****************************
+   * Quick Skip Effective Map *
+   ****************************/
+
+  /* Quick Effective Map Calculation */
+
+  afl->stage_short = "quick";
+  afl->stage_name = "quick eff";
+  afl->stage_cur = 0;
+  afl->stage_max = 32 * 1024;
+
+  orig_hit_cnt = afl->queued_items + afl->saved_crashes;
+
+  u32 before_skip_inf = afl->queued_items;
+
+  /* clean all the eff bytes, since previous eff bytes are already fuzzed */
+  u8 *skip_eff_map = afl->queue_cur->skipdet_e->skip_eff_map,
+     *done_inf_map = afl->queue_cur->skipdet_e->done_inf_map;
+
+  if (!skip_eff_map) {
+
+    skip_eff_map = (u8 *)ck_alloc(sizeof(u8) * len);
+    afl->queue_cur->skipdet_e->skip_eff_map = skip_eff_map;
+
+  } else {
+
+    memset(skip_eff_map, 0, sizeof(u8) * len);
+
+  }
+
+  /* restore the starting point */
+  if (!done_inf_map) {
+
+    done_inf_map = (u8 *)ck_alloc(sizeof(u8) * len);
+    afl->queue_cur->skipdet_e->done_inf_map = done_inf_map;
+
+  } else {
+
+    for (afl->stage_cur = 0; afl->stage_cur < len; afl->stage_cur++) {
+
+      if (done_inf_map[afl->stage_cur] == 0) break;
+
+    }
+
+  }
+
+  /* depending on the seed's performance, we could search eff bytes
+     for multiple rounds */
+
+  u8 eff_round_continue = 1, eff_round_done = 0, done_eff = 0, repeat_eff = 0,
+     fuzz_nearby = 0, *non_eff_bytes = 0;
+
+  u64 before_eff_execs = afl->fsrv.total_execs;
+
+  if (getenv("REPEAT_EFF")) repeat_eff = 1;
+  if (getenv("FUZZ_NEARBY")) fuzz_nearby = 1;
+
+  if (fuzz_nearby) {
+
+    non_eff_bytes = (u8 *)ck_alloc(sizeof(u8) * len);
+
+    // clean exec cksum
+    if (common_fuzz_stuff(afl, out_buf, len)) { return 0; }
+    prev_cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
+
+  }
+
+  do {
+
+    eff_round_continue = 0;
+    afl->stage_max = 32 * 1024;
+
+    for (; afl->stage_cur < afl->stage_max && afl->stage_cur < len;
+         ++afl->stage_cur) {
+
+      afl->stage_cur_byte = afl->stage_cur;
+
+      if (!inf_eff_map[afl->stage_cur_byte] ||
+          skip_eff_map[afl->stage_cur_byte])
+        continue;
+
+      if (is_det_timeout(before_det_time, 1)) { goto cleanup_skipdet; }
+
+      u8 orig = out_buf[afl->stage_cur_byte], replace = rand_below(afl, 256);
+
+      while (replace == orig) {
+
+        replace = rand_below(afl, 256);
+
+      }
+
+      out_buf[afl->stage_cur_byte] = replace;
+
+      before_skip_inf = afl->queued_items;
+
+      if (common_fuzz_stuff(afl, out_buf, len)) { return 0; }
+
+      out_buf[afl->stage_cur_byte] = orig;
+
+      if (fuzz_nearby) {
+
+        if (prev_cksum ==
+            hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST)) {
+
+          non_eff_bytes[afl->stage_cur_byte] = 1;
+
+        }
+
+      }
+
+      if (afl->queued_items != before_skip_inf) {
+
+        skip_eff_map[afl->stage_cur_byte] = 1;
+        afl->queue_cur->skipdet_e->quick_eff_bytes += 1;
+
+        if (afl->stage_max < MAXIMUM_QUICK_EFF_EXECS) { afl->stage_max *= 2; }
+
+        if (afl->stage_max == MAXIMUM_QUICK_EFF_EXECS && repeat_eff)
+          eff_round_continue = 1;
+
+      }
+
+      done_inf_map[afl->stage_cur_byte] = 1;
+
+    }
+
+    afl->stage_cur = 0;
+    done_eff = 1;
+
+    if (++eff_round_done >= 8) break;
+
+  } while (eff_round_continue);
+
+  new_hit_cnt = afl->queued_items + afl->saved_crashes;
+
+  afl->stage_finds[STAGE_QUICK] += new_hit_cnt - orig_hit_cnt;
+  afl->stage_cycles[STAGE_QUICK] += (afl->fsrv.total_execs - before_eff_execs);
+
+cleanup_skipdet:
+
+  if (fuzz_nearby) {
+
+    u8 *nearby_bytes = (u8 *)ck_alloc(sizeof(u8) * len);
+
+    u32 i = 3;
+    while (i < len) {
+
+      // assume DWORD size, from i - 3 -> i + 3
+      if (skip_eff_map[i]) {
+
+        u32 fill_length = (i + 3 < len) ? 7 : len - i + 2;
+        memset(nearby_bytes + i - 3, 1, fill_length);
+        i += 3;
+
+      } else
+
+        i += 1;
+
+    }
+
+    for (i = 0; i < len; i++) {
+
+      if (nearby_bytes[i] && !non_eff_bytes[i]) skip_eff_map[i] = 1;
+
+    }
+
+    ck_free(nearby_bytes);
+    ck_free(non_eff_bytes);
+
+  }
+
+  if (done_eff) {
+
+    afl->queue_cur->skipdet_e->continue_inf = 0;
+    afl->queue_cur->skipdet_e->done_eff = 1;
+
+  } else {
+
+    afl->queue_cur->skipdet_e->continue_inf = 1;
+
+  }
+
+  return 1;
+
+}
+
diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c
index 5e736029..333d57b2 100644
--- a/src/afl-fuzz-state.c
+++ b/src/afl-fuzz-state.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -28,10 +28,6 @@
 #include "afl-fuzz.h"
 #include "envs.h"
 
-s8  interesting_8[] = {INTERESTING_8};
-s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
-s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
-
 char *power_names[POWER_SCHEDULES_NUM] = {"explore", "mmopt", "exploit",
                                           "fast",    "coe",   "lin",
                                           "quad",    "rare",  "seek"};
@@ -89,9 +85,8 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) {
   afl->w_end = 0.3;
   afl->g_max = 5000;
   afl->period_pilot_tmp = 5000.0;
-  afl->schedule = FAST;                 /* Power schedule (default: FAST)   */
+  afl->schedule = EXPLORE;              /* Power schedule (default: EXPLORE)*/
   afl->havoc_max_mult = HAVOC_MAX_MULT;
-
   afl->clear_screen = 1;                /* Window resized?                  */
   afl->havoc_div = 1;                   /* Cycle count divisor for havoc    */
   afl->stage_name = "init";             /* Name of the current fuzz stage   */
@@ -103,11 +98,12 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) {
   afl->stats_update_freq = 1;
   afl->stats_file_update_freq_msecs = STATS_UPDATE_SEC * 1000;
   afl->stats_avg_exec = 0;
-  afl->skip_deterministic = 1;
+  afl->skip_deterministic = 0;
   afl->sync_time = SYNC_TIME;
   afl->cmplog_lvl = 2;
   afl->min_length = 1;
   afl->max_length = MAX_FILE;
+  afl->switch_fuzz_mode = STRATEGY_SWITCH_TIME * 1000;
 #ifndef NO_SPLICING
   afl->use_splicing = 1;
 #endif
@@ -140,6 +136,14 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) {
   afl->fsrv.child_pid = -1;
   afl->fsrv.out_dir_fd = -1;
 
+  /* Init SkipDet */
+  afl->skipdet_g =
+      (struct skipdet_global *)ck_alloc(sizeof(struct skipdet_global));
+  afl->skipdet_g->inf_prof =
+      (struct inf_profile *)ck_alloc(sizeof(struct inf_profile));
+  afl->havoc_prof =
+      (struct havoc_profile *)ck_alloc(sizeof(struct havoc_profile));
+
   init_mopt_globals(afl);
 
   list_append(&afl_states, afl);
@@ -199,6 +203,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
             afl->afl_env.afl_exit_on_time =
                 (u8 *)get_afl_env(afl_environment_variables[i]);
 
+          } else if (!strncmp(env, "AFL_CRASHING_SEEDS_AS_NEW_CRASH",
+
+                              afl_environment_variable_len)) {
+
+            afl->afl_env.afl_crashing_seeds_as_new_crash =
+                atoi((u8 *)get_afl_env(afl_environment_variables[i]));
+
           } else if (!strncmp(env, "AFL_NO_AFFINITY",
 
                               afl_environment_variable_len)) {
@@ -261,6 +272,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
             afl->afl_env.afl_import_first =
                 get_afl_env(afl_environment_variables[i]) ? 1 : 0;
 
+          } else if (!strncmp(env, "AFL_FINAL_SYNC",
+
+                              afl_environment_variable_len)) {
+
+            afl->afl_env.afl_final_sync =
+                get_afl_env(afl_environment_variables[i]) ? 1 : 0;
+
           } else if (!strncmp(env, "AFL_CUSTOM_MUTATOR_ONLY",
 
                               afl_environment_variable_len)) {
@@ -275,6 +293,16 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
             afl->afl_env.afl_cmplog_only_new =
                 get_afl_env(afl_environment_variables[i]) ? 1 : 0;
 
+          } else if (!strncmp(env, "AFL_DISABLE_REDUNDANT",
+
+                              afl_environment_variable_len) ||
+                     !strncmp(env, "AFL_NO_REDUNDANT",
+
+                              afl_environment_variable_len)) {
+
+            afl->afl_env.afl_disable_redundant =
+                get_afl_env(afl_environment_variables[i]) ? 1 : 0;
+
           } else if (!strncmp(env, "AFL_NO_STARTUP_CALIBRATION",
 
                               afl_environment_variable_len)) {
@@ -301,6 +329,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
             afl->afl_env.afl_ignore_problems =
                 get_afl_env(afl_environment_variables[i]) ? 1 : 0;
 
+          } else if (!strncmp(env, "AFL_IGNORE_SEED_PROBLEMS",
+
+                              afl_environment_variable_len)) {
+
+            afl->afl_env.afl_ignore_seed_problems =
+                get_afl_env(afl_environment_variables[i]) ? 1 : 0;
+
           } else if (!strncmp(env, "AFL_IGNORE_TIMEOUTS",
 
                               afl_environment_variable_len)) {
@@ -594,6 +629,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
 
             }
 
+          } else if (!strncmp(env, "AFL_SHA1_FILENAMES",
+
+                              afl_environment_variable_len)) {
+
+            afl->afl_env.afl_sha1_filenames =
+                get_afl_env(afl_environment_variables[i]) ? 1 : 0;
+
           }
 
         } else {
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c
index 07157bf7..ffe56cde 100644
--- a/src/afl-fuzz-stats.c
+++ b/src/afl-fuzz-stats.c
@@ -5,11 +5,12 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
-                        Andrea Fioraldi <andreafioraldi@gmail.com>
+                     Dominik Meier <mail@dmnk.co>,
+                     Andrea Fioraldi <andreafioraldi@gmail.com>, and
+                     Heiko Eissfeldt <heiko.eissfeldt@hexco.de>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -27,6 +28,50 @@
 #include "envs.h"
 #include <limits.h>
 
+static char fuzzing_state[4][12] = {"started :-)", "in progress", "final phase",
+                                    "finished..."};
+
+char *get_fuzzing_state(afl_state_t *afl) {
+
+  u64 cur_ms = get_cur_time();
+  u64 last_find = cur_ms - afl->last_find_time;
+  u64 cur_run_time = cur_ms - afl->start_time;
+  u64 cur_total_run_time = afl->prev_run_time + cur_run_time;
+
+  if (unlikely(afl->non_instrumented_mode)) {
+
+    return fuzzing_state[1];
+
+  } else if (unlikely(cur_run_time < 60 * 3 * 1000 ||
+
+                      cur_total_run_time < 60 * 5 * 1000)) {
+
+    return fuzzing_state[0];
+
+  } else {
+
+    u64 last_find_100 = 100 * last_find;
+    u64 percent_cur = last_find_100 / cur_run_time;
+    u64 percent_total = last_find_100 / cur_total_run_time;
+
+    if (unlikely(percent_cur >= 80 && percent_total >= 80)) {
+
+      return fuzzing_state[3];
+
+    } else if (unlikely(percent_cur >= 55 && percent_total >= 55)) {
+
+      return fuzzing_state[2];
+
+    } else {
+
+      return fuzzing_state[1];
+
+    }
+
+  }
+
+}
+
 /* Write fuzzer setup file */
 
 void write_setup_file(afl_state_t *afl, u32 argc, char **argv) {
@@ -89,6 +134,12 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) {
 
 }
 
+static bool starts_with(char *key, char *line) {
+
+  return strncmp(key, line, strlen(key)) == 0;
+
+}
+
 /* load some of the existing stats file when resuming.*/
 void load_stats_file(afl_state_t *afl) {
 
@@ -131,58 +182,84 @@ void load_stats_file(afl_state_t *afl) {
       strcpy(keystring, lstartptr);
       lptr++;
       char *nptr;
-      switch (lineno) {
-
-        case 3:
-          if (!strcmp(keystring, "run_time          "))
-            afl->prev_run_time = 1000 * strtoull(lptr, &nptr, 10);
-          break;
-        case 5:
-          if (!strcmp(keystring, "cycles_done       "))
-            afl->queue_cycle =
-                strtoull(lptr, &nptr, 10) ? strtoull(lptr, &nptr, 10) + 1 : 0;
-          break;
-        case 7:
-          if (!strcmp(keystring, "execs_done        "))
-            afl->fsrv.total_execs = strtoull(lptr, &nptr, 10);
-          break;
-        case 10:
-          if (!strcmp(keystring, "corpus_count      ")) {
-
-            u32 corpus_count = strtoul(lptr, &nptr, 10);
-            if (corpus_count != afl->queued_items) {
-
-              WARNF(
-                  "queue/ has been modified -- things might not work, you're "
-                  "on your own!");
-
-            }
-
-          }
-
-          break;
-        case 12:
-          if (!strcmp(keystring, "corpus_found      "))
-            afl->queued_discovered = strtoul(lptr, &nptr, 10);
-          break;
-        case 13:
-          if (!strcmp(keystring, "corpus_imported   "))
-            afl->queued_imported = strtoul(lptr, &nptr, 10);
-          break;
-        case 14:
-          if (!strcmp(keystring, "max_depth         "))
-            afl->max_depth = strtoul(lptr, &nptr, 10);
-          break;
-        case 21:
-          if (!strcmp(keystring, "saved_crashes    "))
-            afl->saved_crashes = strtoull(lptr, &nptr, 10);
-          break;
-        case 22:
-          if (!strcmp(keystring, "saved_hangs      "))
-            afl->saved_hangs = strtoull(lptr, &nptr, 10);
-          break;
-        default:
-          break;
+      if (starts_with("run_time", keystring)) {
+
+        afl->prev_run_time = 1000 * strtoull(lptr, &nptr, 10);
+
+      }
+
+      if (starts_with("cycles_done", keystring)) {
+
+        afl->queue_cycle =
+            strtoull(lptr, &nptr, 10) ? strtoull(lptr, &nptr, 10) + 1 : 0;
+
+      }
+
+      if (starts_with("calibration_time", keystring)) {
+
+        afl->calibration_time_us = strtoull(lptr, &nptr, 10) * 1000000;
+
+      }
+
+      if (starts_with("sync_time", keystring)) {
+
+        afl->sync_time_us = strtoull(lptr, &nptr, 10) * 1000000;
+
+      }
+
+      if (starts_with("trim_time", keystring)) {
+
+        afl->trim_time_us = strtoull(lptr, &nptr, 10) * 1000000;
+
+      }
+
+      if (starts_with("execs_done", keystring)) {
+
+        afl->fsrv.total_execs = strtoull(lptr, &nptr, 10);
+
+      }
+
+      if (starts_with("corpus_count", keystring)) {
+
+        u32 corpus_count = strtoul(lptr, &nptr, 10);
+        if (corpus_count != afl->queued_items) {
+
+          WARNF(
+              "queue/ has been modified -- things might not work, you're "
+              "on your own!");
+          sleep(3);
+
+        }
+
+      }
+
+      if (starts_with("corpus_found", keystring)) {
+
+        afl->queued_discovered = strtoul(lptr, &nptr, 10);
+
+      }
+
+      if (starts_with("corpus_imported", keystring)) {
+
+        afl->queued_imported = strtoul(lptr, &nptr, 10);
+
+      }
+
+      if (starts_with("max_depth", keystring)) {
+
+        afl->max_depth = strtoul(lptr, &nptr, 10);
+
+      }
+
+      if (starts_with("saved_crashes", keystring)) {
+
+        afl->saved_crashes = strtoull(lptr, &nptr, 10);
+
+      }
+
+      if (starts_with("saved_hangs", keystring)) {
+
+        afl->saved_hangs = strtoull(lptr, &nptr, 10);
 
       }
 
@@ -206,11 +283,13 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
 #endif
 
   u64   cur_time = get_cur_time();
-  u8    fn[PATH_MAX];
+  u8    fn_tmp[PATH_MAX];
+  u8    fn_final[PATH_MAX];
   FILE *f;
 
-  snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir);
-  f = create_ffile(fn);
+  snprintf(fn_tmp, PATH_MAX, "%s/.fuzzer_stats_tmp", afl->out_dir);
+  snprintf(fn_final, PATH_MAX, "%s/fuzzer_stats", afl->out_dir);
+  f = create_ffile(fn_tmp);
 
   /* Keep last values in case we're called from another context
      where exec/sec stats and such are not readily available. */
@@ -242,6 +321,9 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
 #ifndef __HAIKU__
   if (getrusage(RUSAGE_CHILDREN, &rus)) { rus.ru_maxrss = 0; }
 #endif
+  u64 runtime_ms = afl->prev_run_time + cur_time - afl->start_time;
+  u64 overhead_ms = (afl->calibration_time_us + afl->sync_time_us + afl->trim_time_us) / 1000;
+  if (!runtime_ms) { runtime_ms = 1; }
 
   fprintf(
       f,
@@ -252,6 +334,10 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
       "cycles_done       : %llu\n"
       "cycles_wo_finds   : %llu\n"
       "time_wo_finds     : %llu\n"
+      "fuzz_time         : %llu\n"
+      "calibration_time  : %llu\n"
+      "sync_time         : %llu\n"
+      "trim_time         : %llu\n"
       "execs_done        : %llu\n"
       "execs_per_sec     : %0.02f\n"
       "execs_ps_last_min : %0.02f\n"
@@ -289,18 +375,18 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
       "\n"
       "target_mode       : %s%s%s%s%s%s%s%s%s%s\n"
       "command_line      : %s\n",
-      (afl->start_time - afl->prev_run_time) / 1000, cur_time / 1000,
-      (afl->prev_run_time + cur_time - afl->start_time) / 1000, (u32)getpid(),
+      (afl->start_time /*- afl->prev_run_time*/) / 1000, cur_time / 1000,
+      runtime_ms / 1000, (u32)getpid(),
       afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds,
       afl->longest_find_time > cur_time - afl->last_find_time
           ? afl->longest_find_time / 1000
           : ((afl->start_time == 0 || afl->last_find_time == 0)
                  ? 0
                  : (cur_time - afl->last_find_time) / 1000),
-      afl->fsrv.total_execs,
-      afl->fsrv.total_execs /
-          ((double)(afl->prev_run_time + get_cur_time() - afl->start_time) /
-           1000),
+      (runtime_ms - MIN(runtime_ms, overhead_ms)) / 1000,
+      afl->calibration_time_us / 1000000, afl->sync_time_us / 1000000,
+      afl->trim_time_us / 1000000, afl->fsrv.total_execs,
+      afl->fsrv.total_execs / ((double)(runtime_ms) / 1000),
       afl->last_avg_execs_saved, afl->queued_items, afl->queued_favored,
       afl->queued_discovered, afl->queued_imported, afl->queued_variable,
       afl->max_depth, afl->current_entry, afl->pending_favored,
@@ -368,6 +454,7 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
   }
 
   fclose(f);
+  rename(fn_tmp, fn_final);
 
 }
 
@@ -456,6 +543,44 @@ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
 
 }
 
+/* Log deterministic stage efficiency */
+
+void plot_profile_data(afl_state_t *afl, struct queue_entry *q) {
+
+  u64 current_ms = get_cur_time() - afl->start_time;
+
+  u32    current_edges = count_non_255_bytes(afl, afl->virgin_bits);
+  double det_finding_rate = (double)afl->havoc_prof->total_det_edge * 100.0 /
+                            (double)current_edges,
+         det_time_rate = (double)afl->havoc_prof->total_det_time * 100.0 /
+                         (double)current_ms;
+
+  u32 ndet_bits = 0;
+  for (u32 i = 0; i < afl->fsrv.map_size; i++) {
+
+    if (afl->skipdet_g->virgin_det_bits[i]) ndet_bits += 1;
+
+  }
+
+  double det_fuzzed_rate = (double)ndet_bits * 100.0 / (double)current_edges;
+
+  fprintf(afl->fsrv.det_plot_file,
+          "[%02lld:%02lld:%02lld] fuzz %d (%d), find %d/%d among %d(%02.2f) "
+          "and spend %lld/%lld(%02.2f), cover %02.2f yet, %d/%d undet bits, "
+          "continue %d.\n",
+          current_ms / 1000 / 3600, (current_ms / 1000 / 60) % 60,
+          (current_ms / 1000) % 60, afl->current_entry, q->fuzz_level,
+          afl->havoc_prof->edge_det_stage, afl->havoc_prof->edge_havoc_stage,
+          current_edges, det_finding_rate,
+          afl->havoc_prof->det_stage_time / 1000,
+          afl->havoc_prof->havoc_stage_time / 1000, det_time_rate,
+          det_fuzzed_rate, q->skipdet_e->undet_bits,
+          afl->skipdet_g->undet_bits_threshold, q->skipdet_e->continue_inf);
+
+  fflush(afl->fsrv.det_plot_file);
+
+}
+
 /* Check terminal dimensions after resize. */
 
 static void check_term_size(afl_state_t *afl) {
@@ -505,9 +630,9 @@ void show_stats_normal(afl_state_t *afl) {
 
   cur_ms = get_cur_time();
 
-  if (afl->most_time_key) {
+  if (afl->most_time_key && afl->queue_cycle) {
 
-    if (afl->most_time * 1000 < cur_ms - afl->start_time) {
+    if (afl->most_time * 1000 + afl->sync_time_us / 1000 < cur_ms - afl->start_time) {
 
       afl->most_time_key = 2;
       afl->stop_soon = 2;
@@ -516,7 +641,7 @@ void show_stats_normal(afl_state_t *afl) {
 
   }
 
-  if (afl->most_execs_key == 1) {
+  if (afl->most_execs_key == 1 && afl->queue_cycle) {
 
     if (afl->most_execs <= afl->fsrv.total_execs) {
 
@@ -734,10 +859,29 @@ void show_stats_normal(afl_state_t *afl) {
   if (unlikely(!banner[0])) {
 
     char *si = "";
+    char *fuzzer_name;
+
     if (afl->sync_id) { si = afl->sync_id; }
     memset(banner, 0, sizeof(banner));
-    banner_len = (afl->crash_mode ? 20 : 18) + strlen(VERSION) + strlen(si) +
-                 strlen(afl->power_name) + 4 + 6;
+
+    banner_len = strlen(VERSION) + strlen(si) + strlen(afl->power_name) + 4 + 6;
+
+    if (afl->crash_mode) {
+
+      fuzzer_name = "peruvian were-rabbit";
+
+    } else {
+
+      fuzzer_name = "american fuzzy lop";
+      if (banner_len + strlen(fuzzer_name) + strlen(afl->use_banner) > 75) {
+
+        fuzzer_name = "AFL";
+
+      }
+
+    }
+
+    banner_len += strlen(fuzzer_name);
 
     if (strlen(afl->use_banner) + banner_len > 75) {
 
@@ -754,19 +898,18 @@ void show_stats_normal(afl_state_t *afl) {
     if (afl->fsrv.nyx_mode) {
 
       snprintf(banner + banner_pad, sizeof(banner) - banner_pad,
-               "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s] - Nyx",
-               afl->crash_mode ? cPIN "peruvian were-rabbit"
-                               : cYEL "american fuzzy lop",
-               si, afl->use_banner, afl->power_name);
+               "%s%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN
+               "[%s] - Nyx",
+               afl->crash_mode ? cPIN : cYEL, fuzzer_name, si, afl->use_banner,
+               afl->power_name);
 
     } else {
 
 #endif
       snprintf(banner + banner_pad, sizeof(banner) - banner_pad,
-               "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s]",
-               afl->crash_mode ? cPIN "peruvian were-rabbit"
-                               : cYEL "american fuzzy lop",
-               si, afl->use_banner, afl->power_name);
+               "%s%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s]",
+               afl->crash_mode ? cPIN : cYEL, fuzzer_name, si, afl->use_banner,
+               afl->power_name);
 
 #ifdef __linux__
 
@@ -774,6 +917,10 @@ void show_stats_normal(afl_state_t *afl) {
 
 #endif
 
+    if (banner_pad)
+      for (u32 i = 0; i < banner_pad; ++i)
+        strcat(banner, " ");
+
   }
 
   SAYF("\n%s\n", banner);
@@ -995,7 +1142,7 @@ void show_stats_normal(afl_state_t *afl) {
 
   sprintf(tmp, "%s (%s%s saved)", u_stringify_int(IB(0), afl->total_tmouts),
           u_stringify_int(IB(1), afl->saved_tmouts),
-          (afl->saved_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");
+          (afl->saved_tmouts >= KEEP_UNIQUE_HANG) ? "+" : "");
 
   SAYF(bSTG bV bSTOP "  total tmouts : " cRST "%-20s" bSTG bV "\n", tmp);
 
@@ -1010,7 +1157,7 @@ void show_stats_normal(afl_state_t *afl) {
 
   } else if (likely(afl->skip_deterministic)) {
 
-    strcpy(tmp, "disabled (default, enable with -D)");
+    strcpy(tmp, "disabled (-z switch used)");
 
   } else {
 
@@ -1282,7 +1429,11 @@ void show_stats_normal(afl_state_t *afl) {
   }
 
   /* Last line */
-  SAYF(SET_G1 "\n" bSTG bLB bH30 bH20 bH2 bRB bSTOP cRST RESET_G1);
+
+  SAYF(SET_G1 "\n" bSTG bLB bH cCYA          bSTOP " strategy:" cPIN
+              " %s " bSTG bH10 cCYA          bSTOP " state:" cPIN
+              " %s " bSTG bH2 bRB bSTOP cRST RESET_G1,
+       afl->fuzz_mode == 0 ? "explore" : "exploit", get_fuzzing_state(afl));
 
 #undef IB
 
@@ -1309,9 +1460,9 @@ void show_stats_pizza(afl_state_t *afl) {
 
   cur_ms = get_cur_time();
 
-  if (afl->most_time_key) {
+  if (afl->most_time_key && afl->queue_cycle) {
 
-    if (afl->most_time * 1000 < cur_ms - afl->start_time) {
+    if (afl->most_time * 1000 + afl->sync_time_us / 1000 < cur_ms - afl->start_time) {
 
       afl->most_time_key = 2;
       afl->stop_soon = 2;
@@ -1320,7 +1471,7 @@ void show_stats_pizza(afl_state_t *afl) {
 
   }
 
-  if (afl->most_execs_key == 1) {
+  if (afl->most_execs_key == 1 && afl->queue_cycle) {
 
     if (afl->most_execs <= afl->fsrv.total_execs) {
 
@@ -1823,7 +1974,7 @@ void show_stats_pizza(afl_state_t *afl) {
 
   sprintf(tmp, "%s (%s%s saved)", u_stringify_int(IB(0), afl->total_tmouts),
           u_stringify_int(IB(1), afl->saved_tmouts),
-          (afl->saved_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");
+          (afl->saved_tmouts >= KEEP_UNIQUE_HANG) ? "+" : "");
 
   SAYF(bSTG bV bSTOP "                    burned pizzas : " cRST "%-20s" bSTG bV
                      "\n",
@@ -2260,7 +2411,12 @@ void show_init_stats(afl_state_t *afl) {
       stringify_int(IB(0), min_us), stringify_int(IB(1), max_us),
       stringify_int(IB(2), avg_us));
 
-  if (afl->timeout_given != 1) {
+  if (afl->timeout_given == 3) {
+
+    ACTF("Applying timeout settings from resumed session (%u ms).",
+         afl->fsrv.exec_tmout);
+
+  } else if (afl->timeout_given != 1) {
 
     /* Figure out the appropriate timeout. The basic idea is: 5x average or
        1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second.
@@ -2302,11 +2458,6 @@ void show_init_stats(afl_state_t *afl) {
 
     afl->timeout_given = 1;
 
-  } else if (afl->timeout_given == 3) {
-
-    ACTF("Applying timeout settings from resumed session (%u ms).",
-         afl->fsrv.exec_tmout);
-
   } else {
 
     ACTF("-t option specified. We'll use an exec timeout of %u ms.",
@@ -2329,3 +2480,26 @@ void show_init_stats(afl_state_t *afl) {
 
 }
 
+void update_calibration_time(afl_state_t *afl, u64 *time) {
+
+  u64 cur = get_cur_time_us();
+  afl->calibration_time_us += cur - *time;
+  *time = cur;
+
+}
+
+void update_trim_time(afl_state_t *afl, u64 *time) {
+
+  u64 cur = get_cur_time_us();
+  afl->trim_time_us += cur - *time;
+  *time = cur;
+
+}
+
+void update_sync_time(afl_state_t *afl, u64 *time) {
+
+  u64 cur = get_cur_time_us();
+  afl->sync_time_us += cur - *time;
+  *time = cur;
+
+}
diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c
index e835c8ea..2e42ea9b 100644
--- a/src/afl-fuzz-statsd.c
+++ b/src/afl-fuzz-statsd.c
@@ -223,7 +223,7 @@ int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) {
   char tags[MAX_TAG_LEN * 2] = {0};
   if (afl->statsd_tags_format) {
 
-    snprintf(tags, MAX_TAG_LEN * 2, afl->statsd_tags_format, afl->use_banner,
+    snprintf(tags, MAX_TAG_LEN * 2, afl->statsd_tags_format, afl->sync_id,
              VERSION);
 
   }
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 4339ddd2..70ab983c 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -5,11 +5,12 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
-                        Andrea Fioraldi <andreafioraldi@gmail.com>
+                     Dominik Meier <mail@dmnk.co>,
+                     Andrea Fioraldi <andreafioraldi@gmail.com>, and
+                     Heiko Eissfeldt <heiko.eissfeldt@hexco.de>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -125,12 +126,20 @@ static void usage(u8 *argv0, int more_help) {
 
       "Required parameters:\n"
       "  -i dir        - input directory with test cases (or '-' to resume, "
-      "also see AFL_AUTORESUME)\n"
+      "also see \n"
+      "                  AFL_AUTORESUME)\n"
       "  -o dir        - output directory for fuzzer findings\n\n"
 
       "Execution control settings:\n"
+      "  -P strategy   - set fix mutation strategy: explore (focus on new "
+      "coverage),\n"
+      "                  exploit (focus on triggering crashes). You can also "
+      "set a\n"
+      "                  number of seconds after without any finds it switches "
+      "to\n"
+      "                  exploit mode, and back on new coverage (default: %u)\n"
       "  -p schedule   - power schedules compute a seed's performance score:\n"
-      "                  fast(default), explore, exploit, seek, rare, mmopt, "
+      "                  explore(default), fast, exploit, seek, rare, mmopt, "
       "coe, lin\n"
       "                  quad -- see docs/FAQ.md for more information\n"
       "  -f file       - location read by the fuzzed program (default: stdin "
@@ -157,25 +166,29 @@ static void usage(u8 *argv0, int more_help) {
       "\n"
 
       "Mutator settings:\n"
+      "  -a type       - target input format, \"text\" or \"binary\" (default: "
+      "generic)\n"
       "  -g minlength  - set min length of generated fuzz input (default: 1)\n"
       "  -G maxlength  - set max length of generated fuzz input (default: "
       "%lu)\n"
-      "  -D            - enable deterministic fuzzing (once per queue entry)\n"
       "  -L minutes    - use MOpt(imize) mode and set the time limit for "
       "entering the\n"
       "                  pacemaker mode (minutes of no new finds). 0 = "
       "immediately,\n"
       "                  -1 = immediately and together with normal mutation.\n"
+      "                  Note: this option is usually not very effective\n"
       "  -c program    - enable CmpLog by specifying a binary compiled for "
       "it.\n"
       "                  if using QEMU/FRIDA or the fuzzing target is "
       "compiled\n"
-      "                  for CmpLog then just use -c 0.\n"
+      "                  for CmpLog then use '-c 0'. To disable Cmplog use '-c "
+      "-'.\n"
       "  -l cmplog_opts - CmpLog configuration values (e.g. \"2ATR\"):\n"
       "                  1=small files, 2=larger files (default), 3=all "
       "files,\n"
       "                  A=arithmetic solving, T=transformational solving,\n"
-      "                  R=random colorization bytes.\n\n"
+      "                  X=extreme transform solving, R=random colorization "
+      "bytes.\n\n"
       "Fuzzing behavior settings:\n"
       "  -Z            - sequential queue selection instead of weighted "
       "random\n"
@@ -187,7 +200,8 @@ static void usage(u8 *argv0, int more_help) {
 
       "Test settings:\n"
       "  -s seed       - use a fixed seed for the RNG\n"
-      "  -V seconds    - fuzz for a specified time then terminate\n"
+      "  -V seconds    - fuzz for a specified time then terminate (fuzz time "
+      "only!)\n"
       "  -E execs      - fuzz for an approx. no. of total executions then "
       "terminate\n"
       "                  Note: not precise and can have several more "
@@ -200,7 +214,8 @@ static void usage(u8 *argv0, int more_help) {
       "  -F path       - sync to a foreign fuzzer queue directory (requires "
       "-M, can\n"
       "                  be specified up to %u times)\n"
-      // "  -d            - skip deterministic fuzzing in -M mode\n"
+      "  -z            - skip the enhanced deterministic fuzzing\n"
+      "                  (note that the old -d and -D flags are ignored.)\n"
       "  -T text       - text banner to show on the screen\n"
       "  -I command    - execute this command/script when a new crash is "
       "found\n"
@@ -212,7 +227,8 @@ static void usage(u8 *argv0, int more_help) {
       "  -e ext        - file extension for the fuzz test input file (if "
       "needed)\n"
       "\n",
-      argv0, EXEC_TIMEOUT, MEM_LIMIT, MAX_FILE, FOREIGN_SYNCS_MAX);
+      argv0, STRATEGY_SWITCH_TIME, EXEC_TIMEOUT, MEM_LIMIT, MAX_FILE,
+      FOREIGN_SYNCS_MAX);
 
   if (more_help > 1) {
 
@@ -248,10 +264,12 @@ static void usage(u8 *argv0, int more_help) {
       "AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n"
       "AFL_DEBUG: extra debugging output for Python mode trimming\n"
       "AFL_DEBUG_CHILD: do not suppress stdout/stderr from target\n"
+      "AFL_DISABLE_REDUNDANT: disable any queue item that is redundant\n"
       "AFL_DISABLE_TRIM: disable the trimming of test cases\n"
       "AFL_DUMB_FORKSRV: use fork server without feedback from target\n"
       "AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n"
       "AFL_EXIT_ON_TIME: exit when no new coverage is found within the specified time\n"
+      "AFL_EXIT_ON_SEED_ISSUES: exit on any kind of seed issues\n"
       "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60\n"
       "                      minutes and a cycle without finds)\n"
       "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n"
@@ -262,11 +280,14 @@ static void usage(u8 *argv0, int more_help) {
       "AFL_IGNORE_PROBLEMS: do not abort fuzzing if an incorrect setup is detected\n"
       "AFL_IGNORE_PROBLEMS_COVERAGE: if set in addition to AFL_IGNORE_PROBLEMS - also\n"
       "                              ignore those libs for coverage\n"
+      "AFL_IGNORE_SEED_PROBLEMS: skip over crashes and timeouts in the seeds instead of\n"
+      "                          exiting\n"
       "AFL_IGNORE_TIMEOUTS: do not process or save any timeouts\n"
       "AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n"
       "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n"
       "AFL_INPUT_LEN_MIN/AFL_INPUT_LEN_MAX: like -g/-G set min/max fuzz length produced\n"
-      "AFL_PIZZA_MODE: 1 - enforce pizza mode, 0 - disable for April 1st\n"
+      "AFL_PIZZA_MODE: 1 - enforce pizza mode, -1 - disable for April 1st,\n"
+      "                0 (default) - activate on April 1st\n"
       "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc.\n"
       "                 (default: SIGKILL)\n"
       "AFL_FORK_SERVER_KILL_SIGNAL: Kill signal for the fork server on termination\n"
@@ -285,8 +306,14 @@ static void usage(u8 *argv0, int more_help) {
       "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n"
       "AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)\n"
       "AFL_NO_STARTUP_CALIBRATION: no initial seed calibration, start fuzzing at once\n"
+      "AFL_NO_WARN_INSTABILITY: no warn about instability issues on startup calibration\n"
       "AFL_NO_UI: switch status screen off\n"
-
+      "AFL_NYX_AUX_SIZE: size of the Nyx auxiliary buffer. Must be a multiple of 4096.\n"
+      "                  Increase this value in case the crash reports are truncated.\n"
+      "                  Default value is 4096.\n"
+      "AFL_NYX_DISABLE_SNAPSHOT_MODE: disable snapshot mode (must be supported by the agent)\n"
+      "AFL_NYX_LOG: output NYX hprintf messages to another file\n"
+      "AFL_NYX_REUSE_SNAPSHOT: reuse an existing Nyx root snapshot\n"
       DYN_COLOR
 
       "AFL_PATH: path to AFL support binaries\n"
@@ -295,8 +322,8 @@ static void usage(u8 *argv0, int more_help) {
 
       PERSISTENT_MSG
 
-      "AFL_POST_PROCESS_KEEP_ORIGINAL: save the file as it was prior post-processing to the queue,\n"
-      "                                but execute the post-processed one\n"
+      "AFL_POST_PROCESS_KEEP_ORIGINAL: save the file as it was prior post-processing to\n"
+      "                                the queue, but execute the post-processed one\n"
       "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
       "AFL_TARGET_ENV: pass extra environment variables to target\n"
       "AFL_SHUFFLE_QUEUE: reorder the input queue randomly on startup\n"
@@ -307,18 +334,18 @@ static void usage(u8 *argv0, int more_help) {
       "AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)\n"
       "AFL_STATSD_PORT: change default statsd port (default: 8125)\n"
       "AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n"
-      "                        Supported formats are: 'dogstatsd', 'librato',\n"
-      "                        'signalfx' and 'influxdb'\n"
+      "                        suported formats: dogstatsd, librato, signalfx, influxdb\n"
       "AFL_SYNC_TIME: sync time between fuzzing instances (in minutes)\n"
+      "AFL_FINAL_SYNC: sync a final time when exiting (will delay the exit!)\n"
       "AFL_NO_CRASH_README: do not create a README in the crashes directory\n"
       "AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)\n"
       "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n"
       "AFL_EARLY_FORKSERVER: force an early forkserver in an afl-clang-fast/\n"
       "                      afl-clang-lto/afl-gcc-fast target\n"
-      "AFL_PERSISTENT: enforce persistent mode (if __AFL_LOOP is in a shared lib\n"
-      "AFL_DEFER_FORKSRV: enforced deferred forkserver (__AFL_INIT is in a .so)\n"
-      "AFL_FUZZER_STATS_UPDATE_INTERVAL: interval to update fuzzer_stats file in seconds, "
-      "(default: 60, minimum: 1)\n"
+      "AFL_PERSISTENT: enforce persistent mode (if __AFL_LOOP is in a shared lib)\n"
+      "AFL_DEFER_FORKSRV: enforced deferred forkserver (__AFL_INIT is in a shared lib)\n"
+      "AFL_FUZZER_STATS_UPDATE_INTERVAL: interval to update fuzzer_stats file in\n"
+      "                                  seconds (default: 60, minimum: 1)\n"
       "\n"
     );
 
@@ -357,6 +384,10 @@ static void usage(u8 *argv0, int more_help) {
   SAYF("Compiled with NO_SPLICING.\n");
 #endif
 
+#ifdef FANCY_BOXES_NO_UTF
+  SAYF("Compiled without UTF-8 support for line rendering in status screen.\n");
+#endif
+
 #ifdef PROFILING
   SAYF("Compiled with PROFILING.\n");
 #endif
@@ -373,6 +404,12 @@ static void usage(u8 *argv0, int more_help) {
   SAYF("Compiled with _AFL_DOCUMENT_MUTATIONS.\n");
 #endif
 
+#ifdef _AFL_SPECIAL_PERFORMANCE
+  SAYF(
+      "Compiled with special performance options for this specific system, it "
+      "might not work on other platforms!\n");
+#endif
+
   SAYF("For additional help please consult %s/README.md :)\n\n", doc_path);
 
   exit(1);
@@ -458,6 +495,22 @@ int main(int argc, char **argv_orig, char **envp) {
   struct timeval  tv;
   struct timezone tz;
 
+  doc_path = access(DOC_PATH, F_OK) != 0 ? (u8 *)"docs" : (u8 *)DOC_PATH;
+
+  if (argc > 1 && strcmp(argv_orig[1], "--version") == 0) {
+
+    printf("afl-fuzz" VERSION "\n");
+    exit(0);
+
+  }
+
+  if (argc > 1 && strcmp(argv_orig[1], "--help") == 0) {
+
+    usage(argv_orig[0], 1);
+    exit(0);
+
+  }
+
   #if defined USE_COLOR && defined ALWAYS_COLORED
   if (getenv("AFL_NO_COLOR") || getenv("AFL_NO_COLOUR")) {
 
@@ -487,21 +540,72 @@ int main(int argc, char **argv_orig, char **envp) {
   SAYF(cCYA "afl-fuzz" VERSION cRST
             " based on afl by Michal Zalewski and a large online community\n");
 
-  doc_path = access(DOC_PATH, F_OK) != 0 ? (u8 *)"docs" : (u8 *)DOC_PATH;
-
   gettimeofday(&tv, &tz);
   rand_set_seed(afl, tv.tv_sec ^ tv.tv_usec ^ getpid());
 
   afl->shmem_testcase_mode = 1;  // we always try to perform shmem fuzzing
 
-  while (
-      (opt = getopt(
-           argc, argv,
-           "+Ab:B:c:CdDe:E:hi:I:f:F:g:G:l:L:m:M:nNOo:p:RQs:S:t:T:UV:WXx:YZ")) >
-      0) {
+  // still available: HjJkKqruvwz
+  while ((opt = getopt(argc, argv,
+                       "+a:Ab:B:c:CdDe:E:f:F:g:G:hi:I:l:L:m:M:nNo:Op:P:QRs:S:t:"
+                       "T:UV:WXx:YzZ")) > 0) {
 
     switch (opt) {
 
+      case 'a':
+
+        if (!stricmp(optarg, "text") || !stricmp(optarg, "ascii") ||
+            !stricmp(optarg, "txt") || !stricmp(optarg, "asc")) {
+
+          afl->input_mode = 1;
+
+        } else if (!stricmp(optarg, "bin") || !stricmp(optarg, "binary")) {
+
+          afl->input_mode = 2;
+
+        } else if (!stricmp(optarg, "def") || !stricmp(optarg, "default")) {
+
+          afl->input_mode = 0;
+
+        } else {
+
+          FATAL("-a input mode needs to be \"text\" or \"binary\".");
+
+        }
+
+        break;
+
+      case 'P':
+        if (!stricmp(optarg, "explore") || !stricmp(optarg, "exploration")) {
+
+          afl->fuzz_mode = 0;
+          afl->switch_fuzz_mode = 0;
+
+        } else if (!stricmp(optarg, "exploit") ||
+
+                   !stricmp(optarg, "exploitation")) {
+
+          afl->fuzz_mode = 1;
+          afl->switch_fuzz_mode = 0;
+
+        } else {
+
+          if ((afl->switch_fuzz_mode = (u32)atoi(optarg)) > INT_MAX) {
+
+            FATAL(
+                "Parameter for option -P must be \"explore\", \"exploit\" or a "
+                "number!");
+
+          } else {
+
+            afl->switch_fuzz_mode *= 1000;
+
+          }
+
+        }
+
+        break;
+
       case 'g':
         afl->min_length = atoi(optarg);
         break;
@@ -534,8 +638,23 @@ int main(int argc, char **argv_orig, char **envp) {
 
       case 'c': {
 
-        afl->shm.cmplog_mode = 1;
-        afl->cmplog_binary = ck_strdup(optarg);
+        if (strcmp(optarg, "-") == 0) {
+
+          if (afl->shm.cmplog_mode) {
+
+            ACTF("Disabling cmplog again because of '-c -'.");
+            afl->shm.cmplog_mode = 0;
+            afl->cmplog_binary = NULL;
+
+          }
+
+        } else {
+
+          afl->shm.cmplog_mode = 1;
+          afl->cmplog_binary = ck_strdup(optarg);
+
+        }
+
         break;
 
       }
@@ -845,12 +964,15 @@ int main(int argc, char **argv_orig, char **envp) {
 
       break;
 
-      case 'D':                                    /* enforce deterministic */
+      case 'd':
+      case 'D':                                        /* old deterministic */
 
-        afl->skip_deterministic = 0;
+        WARNF(
+            "Parameters -d and -D are deprecated, a new enhanced deterministic "
+            "fuzzing is active by default, to disable it use -z");
         break;
 
-      case 'd':                                       /* skip deterministic */
+      case 'z':                                         /* no deterministic */
 
         afl->skip_deterministic = 1;
         break;
@@ -1056,10 +1178,18 @@ int main(int argc, char **argv_orig, char **envp) {
             case 'A':
               afl->cmplog_enable_arith = 1;
               break;
+            case 's':
+            case 'S':
+              afl->cmplog_enable_scale = 1;
+              break;
             case 't':
             case 'T':
               afl->cmplog_enable_transform = 1;
               break;
+            case 'x':
+            case 'X':
+              afl->cmplog_enable_xtreme_transform = 1;
+              break;
             case 'r':
             case 'R':
               afl->cmplog_random_colorization = 1;
@@ -1108,6 +1238,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
         }
 
+        afl->old_seed_selection = 1;
         u64 limit_time_puppet2 = afl->limit_time_puppet * 60 * 1000;
 
         if ((s32)limit_time_puppet2 < afl->limit_time_puppet) {
@@ -1221,6 +1352,10 @@ int main(int argc, char **argv_orig, char **envp) {
 
         }
 
+        WARNF(
+            "Note that the MOpt mode is not maintained and is not as effective "
+            "as normal havoc mode.");
+
       } break;
 
       case 'h':
@@ -1242,6 +1377,12 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  if (afl->sync_id && strcmp(afl->sync_id, "addseeds") == 0) {
+
+    FATAL("-M/-S name 'addseeds' is a reserved name, choose something else");
+
+  }
+
   if (afl->is_main_node == 1 && afl->schedule != FAST &&
       afl->schedule != EXPLORE) {
 
@@ -1296,11 +1437,11 @@ int main(int argc, char **argv_orig, char **envp) {
   }
 
   #endif
+
+  // silently disable deterministic mutation if custom mutators are used
   if (!afl->skip_deterministic && afl->afl_env.afl_custom_mutator_only) {
 
-    FATAL(
-        "Using -D determinstic fuzzing is incompatible with "
-        "AFL_CUSTOM_MUTATOR_ONLY!");
+    afl->skip_deterministic = 1;
 
   }
 
@@ -1396,9 +1537,9 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (afl->sync_id) {
 
-    if (strlen(afl->sync_id) > 24) {
+    if (strlen(afl->sync_id) > 50) {
 
-      FATAL("sync_id max length is 24 characters");
+      FATAL("sync_id max length is 50 characters");
 
     }
 
@@ -1424,7 +1565,11 @@ int main(int argc, char **argv_orig, char **envp) {
 
   setenv("__AFL_OUT_DIR", afl->out_dir, 1);
 
-  if (get_afl_env("AFL_DISABLE_TRIM")) { afl->disable_trim = 1; }
+  if (get_afl_env("AFL_DISABLE_TRIM") || get_afl_env("AFL_NO_TRIM")) {
+
+    afl->disable_trim = 1;
+
+  }
 
   if (getenv("AFL_NO_UI") && getenv("AFL_FORCE_UI")) {
 
@@ -1436,8 +1581,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (!afl->use_banner) { afl->use_banner = argv[optind]; }
 
-  if (afl->shm.cmplog_mode &&
-      (!strcmp("-", afl->cmplog_binary) || !strcmp("0", afl->cmplog_binary))) {
+  if (afl->shm.cmplog_mode && strcmp("0", afl->cmplog_binary) == 0) {
 
     afl->cmplog_binary = strdup(argv[optind]);
 
@@ -1622,6 +1766,34 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  // Marker: ADD_TO_INJECTIONS
+  if (getenv("AFL_LLVM_INJECTIONS_ALL") || getenv("AFL_LLVM_INJECTIONS_SQL") ||
+      getenv("AFL_LLVM_INJECTIONS_LDAP") || getenv("AFL_LLVM_INJECTIONS_XSS")) {
+
+    OKF("Adding injection tokens to dictionary.");
+    if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
+        getenv("AFL_LLVM_INJECTIONS_SQL")) {
+
+      add_extra(afl, "'\"\"'", 4);
+
+    }
+
+    if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
+        getenv("AFL_LLVM_INJECTIONS_LDAP")) {
+
+      add_extra(afl, "*)(1=*))(|", 10);
+
+    }
+
+    if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
+        getenv("AFL_LLVM_INJECTIONS_XSS")) {
+
+      add_extra(afl, "1\"><\"", 5);
+
+    }
+
+  }
+
   OKF("Generating fuzz data with a length of min=%u max=%u", afl->min_length,
       afl->max_length);
   u32 min_alloc = MAX(64U, afl->min_length);
@@ -1633,6 +1805,7 @@ int main(int argc, char **argv_orig, char **envp) {
   afl_realloc(AFL_BUF_PARAM(ex), min_alloc);
 
   afl->fsrv.use_fauxsrv = afl->non_instrumented_mode == 1 || afl->no_forkserver;
+  afl->fsrv.max_length = afl->max_length;
 
   #ifdef __linux__
   if (!afl->fsrv.nyx_mode) {
@@ -1657,6 +1830,10 @@ int main(int argc, char **argv_orig, char **envp) {
   check_cpu_governor(afl);
   #endif
 
+  #ifdef __APPLE__
+  setenv("DYLD_NO_PIE", "1", 0);
+  #endif
+
   if (getenv("LD_PRELOAD")) {
 
     WARNF(
@@ -1746,16 +1923,6 @@ int main(int argc, char **argv_orig, char **envp) {
   check_if_tty(afl);
   if (afl->afl_env.afl_force_ui) { afl->not_on_tty = 0; }
 
-  if (afl->afl_env.afl_custom_mutator_only) {
-
-    /* This ensures we don't proceed to havoc/splice */
-    afl->custom_only = 1;
-
-    /* Ensure we also skip all deterministic steps */
-    afl->skip_deterministic = 1;
-
-  }
-
   get_core_count(afl);
 
   atexit(at_exit);
@@ -1766,6 +1933,15 @@ int main(int argc, char **argv_orig, char **envp) {
   bind_to_free_cpu(afl);
   #endif                                                   /* HAVE_AFFINITY */
 
+  #ifdef __linux__
+  if (afl->fsrv.nyx_mode && afl->fsrv.nyx_bind_cpu_id == 0xFFFFFFFF) {
+
+    afl->fsrv.nyx_bind_cpu_id = 0;
+
+  }
+
+  #endif
+
   #ifdef __HAIKU__
   /* Prioritizes performance over power saving */
   set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
@@ -1816,7 +1992,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
-  {
+  if (!getenv("AFL_CUSTOM_INFO_PROGRAM_ARGV")) {
 
     u8 envbuf[8096] = "", tmpbuf[8096] = "";
     for (s32 i = optind + 1; i < argc; ++i) {
@@ -1847,10 +2023,41 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
-  setenv("AFL_CUSTOM_INFO_OUT", afl->out_dir, 1);  // same as __AFL_OUT_DIR
+  if (!getenv("AFL_CUSTOM_INFO_OUT")) {
+
+    setenv("AFL_CUSTOM_INFO_OUT", afl->out_dir, 1);  // same as __AFL_OUT_DIR
+
+  }
 
   setup_custom_mutators(afl);
 
+  if (afl->afl_env.afl_custom_mutator_only) {
+
+    if (!afl->custom_mutators_count) {
+
+      if (afl->shm.cmplog_mode) {
+
+        WARNF(
+            "No custom mutator loaded, using AFL_CUSTOM_MUTATOR_ONLY is "
+            "pointless and only allowed now to allow experiments with CMPLOG.");
+
+      } else {
+
+        FATAL(
+            "No custom mutator loaded but AFL_CUSTOM_MUTATOR_ONLY specified.");
+
+      }
+
+    }
+
+    /* This ensures we don't proceed to havoc/splice */
+    afl->custom_only = 1;
+
+    /* Ensure we also skip all deterministic steps */
+    afl->skip_deterministic = 1;
+
+  }
+
   if (afl->limit_time_sig > 0 && afl->custom_mutators_count) {
 
     if (afl->custom_only) {
@@ -1874,6 +2081,17 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  /* Simply code if AFL_TMPDIR is used or not */
+  if (!afl->afl_env.afl_tmpdir) {
+
+    afl->tmp_dir = afl->out_dir;
+
+  } else {
+
+    afl->tmp_dir = afl->afl_env.afl_tmpdir;
+
+  }
+
   write_setup_file(afl, argc, argv);
 
   setup_cmdline_file(afl, argv + optind);
@@ -1886,8 +2104,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (!afl->timeout_given) { find_timeout(afl); }  // only for resumes!
 
-  if ((afl->tmp_dir = afl->afl_env.afl_tmpdir) != NULL &&
-      !afl->in_place_resume) {
+  if (afl->afl_env.afl_tmpdir && !afl->in_place_resume) {
 
     char tmpfile[PATH_MAX];
 
@@ -1912,10 +2129,6 @@ int main(int argc, char **argv_orig, char **envp) {
 
     }
 
-  } else {
-
-    afl->tmp_dir = afl->out_dir;
-
   }
 
   /* If we don't have a file name chosen yet, use a safe default. */
@@ -1987,7 +2200,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
     }
 
-    afl->fsrv.persistent_record_dir = alloc_printf("%s/crashes", afl->out_dir);
+    afl->fsrv.persistent_record_dir = alloc_printf("%s", afl->out_dir);
 
   }
 
@@ -2253,7 +2466,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   } else {
 
-    ACTF("skipping initial seed calibration due option override");
+    ACTF("skipping initial seed calibration due option override!");
     usleep(1000);
 
   }
@@ -2294,10 +2507,18 @@ int main(int argc, char **argv_orig, char **envp) {
 
       for (entry = 0; entry < afl->queued_items; ++entry)
         if (!afl->queue_buf[entry]->disabled)
-          if (afl->queue_buf[entry]->exec_us > max_ms)
-            max_ms = afl->queue_buf[entry]->exec_us;
+          if ((afl->queue_buf[entry]->exec_us / 1000) > max_ms)
+            max_ms = afl->queue_buf[entry]->exec_us / 1000;
+
+      // Add 20% as a safety margin, capped to exec_tmout given in -t option
+      max_ms *= 1.2;
+      if (max_ms > afl->fsrv.exec_tmout) max_ms = afl->fsrv.exec_tmout;
+
+      // Ensure that there is a sensible timeout even for very fast binaries
+      if (max_ms < 5) max_ms = 5;
 
       afl->fsrv.exec_tmout = max_ms;
+      afl->timeout_given = 1;
 
     }
 
@@ -2330,8 +2551,6 @@ int main(int argc, char **argv_orig, char **envp) {
   }
 
   // (void)nice(-20);  // does not improve the speed
-  // real start time, we reset, so this works correctly with -V
-  afl->start_time = get_cur_time();
 
   #ifdef INTROSPECTION
   u32 prev_saved_crashes = 0, prev_saved_tmouts = 0;
@@ -2352,6 +2571,9 @@ int main(int argc, char **argv_orig, char **envp) {
   OKF("Writing mutation introspection to '%s'", ifn);
   #endif
 
+  // real start time, we reset, so this works correctly with -V
+  afl->start_time = get_cur_time();
+
   while (likely(!afl->stop_soon)) {
 
     cull_queue(afl);
@@ -2590,22 +2812,52 @@ int main(int argc, char **argv_orig, char **envp) {
 
       if (likely(!afl->old_seed_selection)) {
 
-        if (unlikely(prev_queued_items < afl->queued_items ||
-                     afl->reinit_table)) {
+        if (likely(afl->pending_favored && afl->smallest_favored >= 0)) {
 
-          // we have new queue entries since the last run, recreate alias table
-          prev_queued_items = afl->queued_items;
-          create_alias_table(afl);
+          afl->current_entry = afl->smallest_favored;
 
-        }
+          /*
 
-        do {
+                    } else {
 
-          afl->current_entry = select_next_queue_entry(afl);
+                      for (s32 iter = afl->queued_items - 1; iter >= 0; --iter)
+             {
 
-        } while (unlikely(afl->current_entry >= afl->queued_items));
+                        if (unlikely(afl->queue_buf[iter]->favored &&
+                                     !afl->queue_buf[iter]->was_fuzzed)) {
 
-        afl->queue_cur = afl->queue_buf[afl->current_entry];
+                          afl->current_entry = iter;
+                          break;
+
+                        }
+
+                      }
+
+          */
+
+          afl->queue_cur = afl->queue_buf[afl->current_entry];
+
+        } else {
+
+          if (unlikely(prev_queued_items < afl->queued_items ||
+                       afl->reinit_table)) {
+
+            // we have new queue entries since the last run, recreate alias
+            // table
+            prev_queued_items = afl->queued_items;
+            create_alias_table(afl);
+
+          }
+
+          do {
+
+            afl->current_entry = select_next_queue_entry(afl);
+
+          } while (unlikely(afl->current_entry >= afl->queued_items));
+
+          afl->queue_cur = afl->queue_buf[afl->current_entry];
+
+        }
 
       }
 
@@ -2667,13 +2919,34 @@ int main(int argc, char **argv_orig, char **envp) {
 
     } while (skipped_fuzz && afl->queue_cur && !afl->stop_soon);
 
+    u64 cur_time = get_cur_time();
+
+    if (likely(afl->switch_fuzz_mode && afl->fuzz_mode == 0 &&
+               !afl->non_instrumented_mode) &&
+        unlikely(cur_time > (likely(afl->last_find_time) ? afl->last_find_time
+                                                         : afl->start_time) +
+                                afl->switch_fuzz_mode)) {
+
+      if (afl->afl_env.afl_no_ui) {
+
+        ACTF(
+            "No new coverage found for %llu seconds, switching to exploitation "
+            "strategy.",
+            afl->switch_fuzz_mode / 1000);
+
+      }
+
+      afl->fuzz_mode = 1;
+
+    }
+
     if (likely(!afl->stop_soon && afl->sync_id)) {
 
       if (likely(afl->skip_deterministic)) {
 
         if (unlikely(afl->is_main_node)) {
 
-          if (unlikely(get_cur_time() >
+          if (unlikely(cur_time >
                        (afl->sync_time >> 1) + afl->last_sync_time)) {
 
             if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) {
@@ -2686,7 +2959,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
         } else {
 
-          if (unlikely(get_cur_time() > afl->sync_time + afl->last_sync_time)) {
+          if (unlikely(cur_time > afl->sync_time + afl->last_sync_time)) {
 
             if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); }
 
@@ -2769,6 +3042,16 @@ stop_fuzzing:
        time_spent_working / afl->fsrv.total_execs);
   #endif
 
+  if (afl->afl_env.afl_final_sync) {
+
+    SAYF(cYEL "[!] " cRST
+              "\nPerforming final sync, this make take some time ...\n");
+    sync_fuzzers(afl);
+    write_bitmap(afl);
+    SAYF(cYEL "[!] " cRST "Done!\n\n");
+
+  }
+
   if (afl->is_main_node) {
 
     u8 path[PATH_MAX];
@@ -2780,6 +3063,11 @@ stop_fuzzing:
   if (frida_afl_preload) { ck_free(frida_afl_preload); }
 
   fclose(afl->fsrv.plot_file);
+
+  #ifdef INTROSPECTION
+  fclose(afl->fsrv.det_plot_file);
+  #endif
+
   destroy_queue(afl);
   destroy_extras(afl);
   destroy_custom_mutators(afl);
@@ -2795,7 +3083,7 @@ stop_fuzzing:
   afl_fsrv_deinit(&afl->fsrv);
 
   /* remove tmpfile */
-  if (afl->tmp_dir != NULL && !afl->in_place_resume && afl->fsrv.out_file) {
+  if (!afl->in_place_resume && afl->fsrv.out_file) {
 
     (void)unlink(afl->fsrv.out_file);
 
diff --git a/src/afl-gotcpu.c b/src/afl-gotcpu.c
index 4f851099..6a3bd037 100644
--- a/src/afl-gotcpu.c
+++ b/src/afl-gotcpu.c
@@ -5,11 +5,11 @@
    Originally written by Michal Zalewski
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c
index 420dd817..578552ba 100644
--- a/src/afl-ld-lto.c
+++ b/src/afl-ld-lto.c
@@ -5,11 +5,11 @@
   Written by Marc Heuse <mh@mh-sec.de> for AFL++
 
   Maintained by Marc Heuse <mh@mh-sec.de>,
-                Heiko Eißfeldt <heiko.eissfeldt@hexco.de>
+                Heiko Eissfeldt <heiko.eissfeldt@hexco.de>
                 Andrea Fioraldi <andreafioraldi@gmail.com>
                 Dominik Maier <domenukk@gmail.com>
 
-  Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+  Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -23,7 +23,9 @@
 */
 
 #define AFL_MAIN
-#define _GNU_SOURCE
+#ifndef _GNU_SOURCE
+  #define _GNU_SOURCE
+#endif
 
 #include "config.h"
 #include "types.h"
@@ -37,6 +39,7 @@
 #include <time.h>
 #include <ctype.h>
 #include <fcntl.h>
+#include <limits.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -45,11 +48,6 @@
 
 #include <dirent.h>
 
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
-    defined(__DragonFly__)
-  #include <limits.h>
-#endif
-
 #ifdef __APPLE__
   #include <sys/syslimits.h>
 #endif
@@ -280,7 +278,7 @@ int main(int argc, char **argv) {
   if (getenv("AFL_LD_PASSTHROUGH") != NULL) passthrough = 1;
   if (getenv("AFL_REAL_LD") != NULL) real_ld = getenv("AFL_REAL_LD");
 
-  if (!afl_path || !*afl_path) afl_path = "/usr/local/lib/afl";
+  if (!afl_path || !*afl_path) afl_path = AFL_PATH;
 
   setenv("AFL_LD_CALLER", "1", 1);
 
diff --git a/src/afl-performance.c b/src/afl-performance.c
index 04507410..6c6e3c8b 100644
--- a/src/afl-performance.c
+++ b/src/afl-performance.c
@@ -1,31 +1,18 @@
-/*
-   Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org)
-
-   To the extent possible under law, the author has dedicated all copyright
-   and related and neighboring rights to this software to the public domain
-   worldwide. This software is distributed without any warranty.
-
-   See <https://creativecommons.org/publicdomain/zero/1.0/>.
-
-   This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators.
-   It has excellent (sub-ns) speed, a state (256 bits) that is large
-   enough for any parallel application, and it passes all tests we are
-   aware of.
-
-   For generating just floating-point numbers, xoshiro256+ is even faster.
-
-   The state must be seeded so that it is not everywhere zero. If you have
-   a 64-bit seed, we suggest to seed a splitmix64 generator and use its
-   output to fill s[].
-*/
-
 #include <stdint.h>
 #include "afl-fuzz.h"
 #include "types.h"
 
-#define XXH_INLINE_ALL
-#include "xxhash.h"
-#undef XXH_INLINE_ALL
+#ifdef _HAVE_AVX2
+  #define T1HA0_AESNI_AVAILABLE 1
+  #define T1HA_USE_FAST_ONESHOT_READ 1
+  #define T1HA_USE_INDIRECT_FUNCTIONS 1
+  #define T1HA_IA32AES_NAME XXH3_64bits
+  #include "t1ha0_ia32aes_b.h"
+#else
+  #define XXH_INLINE_ALL
+  #include "xxhash.h"
+  #undef XXH_INLINE_ALL
+#endif
 
 void rand_set_seed(afl_state_t *afl, s64 init_seed) {
 
@@ -108,3 +95,313 @@ inline u64 hash64(u8 *key, u32 len, u64 seed) {
 
 }
 
+// Public domain SHA1 implementation copied from:
+// https://github.com/x42/liboauth/blob/7001b8256cd654952ec2515b055d2c5b243be600/src/sha1.c
+
+/* This code is public-domain - it is based on libcrypt
+ * placed in the public domain by Wei Dai and other contributors.
+ */
+// gcc -Wall -DSHA1TEST -o sha1test sha1.c && ./sha1test
+
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __BIG_ENDIAN__
+  #define SHA_BIG_ENDIAN
+#elif defined __LITTLE_ENDIAN__
+/* override */
+#elif defined __BYTE_ORDER
+  #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+    #define SHA_BIG_ENDIAN
+  #endif
+#else                  // ! defined __LITTLE_ENDIAN__
+  #include <endian.h>  // machine/endian.h
+  #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+    #define SHA_BIG_ENDIAN
+  #endif
+#endif
+
+/* header */
+
+#define HASH_LENGTH 20
+#define BLOCK_LENGTH 64
+
+typedef struct sha1nfo {
+
+  uint32_t buffer[BLOCK_LENGTH / 4];
+  uint32_t state[HASH_LENGTH / 4];
+  uint32_t byteCount;
+  uint8_t  bufferOffset;
+  uint8_t  keyBuffer[BLOCK_LENGTH];
+  uint8_t  innerHash[HASH_LENGTH];
+
+} sha1nfo;
+
+/* public API - prototypes - TODO: doxygen*/
+
+/**
+ */
+void sha1_init(sha1nfo *s);
+/**
+ */
+void sha1_writebyte(sha1nfo *s, uint8_t data);
+/**
+ */
+void sha1_write(sha1nfo *s, const char *data, size_t len);
+/**
+ */
+uint8_t *sha1_result(sha1nfo *s);
+/**
+ */
+void sha1_initHmac(sha1nfo *s, const uint8_t *key, int keyLength);
+/**
+ */
+uint8_t *sha1_resultHmac(sha1nfo *s);
+
+/* code */
+#define SHA1_K0 0x5a827999
+#define SHA1_K20 0x6ed9eba1
+#define SHA1_K40 0x8f1bbcdc
+#define SHA1_K60 0xca62c1d6
+
+void sha1_init(sha1nfo *s) {
+
+  s->state[0] = 0x67452301;
+  s->state[1] = 0xefcdab89;
+  s->state[2] = 0x98badcfe;
+  s->state[3] = 0x10325476;
+  s->state[4] = 0xc3d2e1f0;
+  s->byteCount = 0;
+  s->bufferOffset = 0;
+
+}
+
+uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
+
+  return ((number << bits) | (number >> (32 - bits)));
+
+}
+
+void sha1_hashBlock(sha1nfo *s) {
+
+  uint8_t  i;
+  uint32_t a, b, c, d, e, t;
+
+  a = s->state[0];
+  b = s->state[1];
+  c = s->state[2];
+  d = s->state[3];
+  e = s->state[4];
+  for (i = 0; i < 80; i++) {
+
+    if (i >= 16) {
+
+      t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^
+          s->buffer[(i + 2) & 15] ^ s->buffer[i & 15];
+      s->buffer[i & 15] = sha1_rol32(t, 1);
+
+    }
+
+    if (i < 20) {
+
+      t = (d ^ (b & (c ^ d))) + SHA1_K0;
+
+    } else if (i < 40) {
+
+      t = (b ^ c ^ d) + SHA1_K20;
+
+    } else if (i < 60) {
+
+      t = ((b & c) | (d & (b | c))) + SHA1_K40;
+
+    } else {
+
+      t = (b ^ c ^ d) + SHA1_K60;
+
+    }
+
+    t += sha1_rol32(a, 5) + e + s->buffer[i & 15];
+    e = d;
+    d = c;
+    c = sha1_rol32(b, 30);
+    b = a;
+    a = t;
+
+  }
+
+  s->state[0] += a;
+  s->state[1] += b;
+  s->state[2] += c;
+  s->state[3] += d;
+  s->state[4] += e;
+
+}
+
+void sha1_addUncounted(sha1nfo *s, uint8_t data) {
+
+  uint8_t *const b = (uint8_t *)s->buffer;
+#ifdef SHA_BIG_ENDIAN
+  b[s->bufferOffset] = data;
+#else
+  b[s->bufferOffset ^ 3] = data;
+#endif
+  s->bufferOffset++;
+  if (s->bufferOffset == BLOCK_LENGTH) {
+
+    sha1_hashBlock(s);
+    s->bufferOffset = 0;
+
+  }
+
+}
+
+void sha1_writebyte(sha1nfo *s, uint8_t data) {
+
+  ++s->byteCount;
+  sha1_addUncounted(s, data);
+
+}
+
+void sha1_write(sha1nfo *s, const char *data, size_t len) {
+
+  for (; len--;)
+    sha1_writebyte(s, (uint8_t)*data++);
+
+}
+
+void sha1_pad(sha1nfo *s) {
+
+  // Implement SHA-1 padding (fips180-2 §5.1.1)
+
+  // Pad with 0x80 followed by 0x00 until the end of the block
+  sha1_addUncounted(s, 0x80);
+  while (s->bufferOffset != 56)
+    sha1_addUncounted(s, 0x00);
+
+  // Append length in the last 8 bytes
+  sha1_addUncounted(s, 0);  // We're only using 32 bit lengths
+  sha1_addUncounted(s, 0);  // But SHA-1 supports 64 bit lengths
+  sha1_addUncounted(s, 0);  // So zero pad the top bits
+  sha1_addUncounted(s, s->byteCount >> 29);  // Shifting to multiply by 8
+  sha1_addUncounted(
+      s, s->byteCount >> 21);  // as SHA-1 supports bitstreams as well as
+  sha1_addUncounted(s, s->byteCount >> 13);  // byte.
+  sha1_addUncounted(s, s->byteCount >> 5);
+  sha1_addUncounted(s, s->byteCount << 3);
+
+}
+
+uint8_t *sha1_result(sha1nfo *s) {
+
+  // Pad to complete the last block
+  sha1_pad(s);
+
+#ifndef SHA_BIG_ENDIAN
+  // Swap byte order back
+  int i;
+  for (i = 0; i < 5; i++) {
+
+    s->state[i] = (((s->state[i]) << 24) & 0xff000000) |
+                  (((s->state[i]) << 8) & 0x00ff0000) |
+                  (((s->state[i]) >> 8) & 0x0000ff00) |
+                  (((s->state[i]) >> 24) & 0x000000ff);
+
+  }
+
+#endif
+
+  // Return pointer to hash (20 characters)
+  return (uint8_t *)s->state;
+
+}
+
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5c
+
+void sha1_initHmac(sha1nfo *s, const uint8_t *key, int keyLength) {
+
+  uint8_t i;
+  memset(s->keyBuffer, 0, BLOCK_LENGTH);
+  if (keyLength > BLOCK_LENGTH) {
+
+    // Hash long keys
+    sha1_init(s);
+    for (; keyLength--;)
+      sha1_writebyte(s, *key++);
+    memcpy(s->keyBuffer, sha1_result(s), HASH_LENGTH);
+
+  } else {
+
+    // Block length keys are used as is
+    memcpy(s->keyBuffer, key, keyLength);
+
+  }
+
+  // Start inner hash
+  sha1_init(s);
+  for (i = 0; i < BLOCK_LENGTH; i++) {
+
+    sha1_writebyte(s, s->keyBuffer[i] ^ HMAC_IPAD);
+
+  }
+
+}
+
+uint8_t *sha1_resultHmac(sha1nfo *s) {
+
+  uint8_t i;
+  // Complete inner hash
+  memcpy(s->innerHash, sha1_result(s), HASH_LENGTH);
+  // Calculate outer hash
+  sha1_init(s);
+  for (i = 0; i < BLOCK_LENGTH; i++)
+    sha1_writebyte(s, s->keyBuffer[i] ^ HMAC_OPAD);
+  for (i = 0; i < HASH_LENGTH; i++)
+    sha1_writebyte(s, s->innerHash[i]);
+  return sha1_result(s);
+
+}
+
+// End public domain SHA1 implementation
+
+void sha1(const u8 *data, size_t len, u8 *out) {
+
+  sha1nfo s;
+  sha1_init(&s);
+  sha1_write(&s, (const char *)data, len);
+  memcpy(out, sha1_result(&s), HASH_LENGTH);
+
+}
+
+char *sha1_hex(const u8 *data, size_t len) {
+
+  u8 digest[HASH_LENGTH];
+  sha1(data, len, digest);
+  u8 *hex = ck_alloc(HASH_LENGTH * 2 + 1);
+  for (size_t i = 0; i < HASH_LENGTH; ++i) {
+
+    sprintf((char *)(hex + i * 2), "%02x", digest[i]);
+
+  }
+
+  return hex;
+
+}
+
+char *sha1_hex_for_file(const char *fname, u32 len) {
+
+  int fd = open(fname, O_RDONLY);
+  if (fd < 0) { PFATAL("Unable to open '%s'", fname); }
+
+  u32 read_len = MIN(len, (u32)MAX_FILE);
+  u8 *tmp = ck_alloc(read_len);
+  ck_read(fd, tmp, read_len, fname);
+
+  close(fd);
+
+  char *hex = sha1_hex(tmp, read_len);
+  ck_free(tmp);
+  return hex;
+
+}
+
diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c
index a2c81586..8f685633 100644
--- a/src/afl-sharedmem.c
+++ b/src/afl-sharedmem.c
@@ -7,11 +7,11 @@
    Forkserver design by Jann Horn <jannhorn@googlemail.com>
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index 9c029035..7e875040 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -7,12 +7,12 @@
    Forkserver design by Jann Horn <jannhorn@googlemail.com>
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com> and
                         Dominik Maier <mail@dmnk.co>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -111,8 +111,9 @@ static sharedmem_t      *shm_fuzz;
 
 static const u8 count_class_human[256] = {
 
-    [0] = 0, [1] = 1,  [2] = 2,  [3] = 3,  [4] = 4,
-    [8] = 5, [16] = 6, [32] = 7, [128] = 8
+    [0] = 0,          [1] = 1,        [2] = 2,         [3] = 3,
+    [4 ... 7] = 4,    [8 ... 15] = 5, [16 ... 31] = 6, [32 ... 127] = 7,
+    [128 ... 255] = 8
 
 };
 
@@ -177,7 +178,8 @@ fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv,
 void classify_counts(afl_forkserver_t *fsrv) {
 
   u8       *mem = fsrv->trace_bits;
-  const u8 *map = binary_mode ? count_class_binary : count_class_human;
+  const u8 *map = (binary_mode || collect_coverage) ? count_class_binary
+                                                    : count_class_human;
 
   u32 i = map_size;
 
@@ -239,13 +241,7 @@ static void analyze_results(afl_forkserver_t *fsrv) {
   u32 i;
   for (i = 0; i < map_size; i++) {
 
-    if (fsrv->trace_bits[i]) {
-
-      total += fsrv->trace_bits[i];
-      if (fsrv->trace_bits[i] > highest) highest = fsrv->trace_bits[i];
-      if (!coverage_map[i]) { coverage_map[i] = 1; }
-
-    }
+    if (fsrv->trace_bits[i]) { coverage_map[i] |= fsrv->trace_bits[i]; }
 
   }
 
@@ -328,7 +324,7 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) {
 
       if (cmin_mode) {
 
-        fprintf(f, "%u%u\n", fsrv->trace_bits[i], i);
+        fprintf(f, "%u%03u\n", i, fsrv->trace_bits[i]);
 
       } else {
 
@@ -423,9 +419,9 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem,
 
   }
 
-  if (fsrv->trace_bits[0] == 1) {
+  if (fsrv->trace_bits[0]) {
 
-    fsrv->trace_bits[0] = 0;
+    fsrv->trace_bits[0] -= 1;
     have_coverage = true;
 
   } else {
@@ -654,9 +650,9 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) {
 
   }
 
-  if (fsrv->trace_bits[0] == 1) {
+  if (fsrv->trace_bits[0]) {
 
-    fsrv->trace_bits[0] = 0;
+    fsrv->trace_bits[0] -= 1;
     have_coverage = true;
 
   } else {
@@ -1337,6 +1333,8 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  if (collect_coverage) { binary_mode = false; }  // ensure this
+
   if (optind == argc || !out_file) { usage(argv[0]); }
 
   if (in_dir && in_filelist) { FATAL("you can only specify either -i or -I"); }
@@ -1609,6 +1607,7 @@ int main(int argc, char **argv_orig, char **envp) {
   if (in_dir || in_filelist) {
 
     afl->fsrv.dev_urandom_fd = open("/dev/urandom", O_RDONLY);
+    if (afl->fsrv.dev_urandom_fd < 0) { PFATAL("Unable to open /dev/urandom"); }
     afl->afl_env.afl_custom_mutator_library =
         getenv("AFL_CUSTOM_MUTATOR_LIBRARY");
     afl->afl_env.afl_python_module = getenv("AFL_PYTHON_MODULE");
@@ -1674,7 +1673,6 @@ int main(int argc, char **argv_orig, char **envp) {
       if ((coverage_map = (u8 *)malloc(map_size + 64)) == NULL)
         FATAL("coult not grab memory");
       edges_only = false;
-      raw_instr_output = true;
 
     }
 
diff --git a/src/afl-tmin.c b/src/afl-tmin.c
index e7442d1d..23e0ff13 100644
--- a/src/afl-tmin.c
+++ b/src/afl-tmin.c
@@ -7,12 +7,12 @@
    Forkserver design by Jann Horn <jannhorn@googlemail.com>
 
    Now maintained by Marc Heuse <mh@mh-sec.de>,
-                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                         Andrea Fioraldi <andreafioraldi@gmail.com> and
                         Dominik Maier <mail@dmnk.co>
 
    Copyright 2016, 2017 Google Inc. All rights reserved.
-   Copyright 2019-2023 AFLplusplus Project. All rights reserved.
+   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -82,6 +82,8 @@ static u8 crash_mode,                  /* Crash-centric mode?               */
     remove_shm = 1,                    /* remove shmem on exit?             */
     debug;                             /* debug mode                        */
 
+static u32 del_len_limit = 1;          /* Minimum block deletion length     */
+
 static volatile u8 stop_soon;          /* Ctrl-C pressed?                   */
 
 static afl_forkserver_t *fsrv;
@@ -480,7 +482,7 @@ next_del_blksize:
 
   }
 
-  if (del_len > 1 && in_len >= 1) {
+  if (del_len > del_len_limit && in_len >= 1) {
 
     del_len /= 2;
     goto next_del_blksize;
@@ -796,8 +798,9 @@ static void usage(u8 *argv0) {
       "Minimization settings:\n"
 
       "  -e            - solve for edge coverage only, ignore hit counts\n"
-      "  -x            - treat non-zero exit codes as crashes\n\n"
-      "  -H            - minimize a hang (hang mode)\n"
+      "  -l bytes      - set minimum block deletion length to speed up minimization\n"
+      "  -x            - treat non-zero exit codes as crashes\n"
+      "  -H            - minimize a hang (hang mode)\n\n"
 
       "For additional tips, please consult %s/README.md.\n\n"
 
@@ -829,8 +832,9 @@ static void usage(u8 *argv0) {
 
 int main(int argc, char **argv_orig, char **envp) {
 
-  s32    opt;
-  u8     mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0;
+  s32 opt;
+  u8  mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0,
+     del_limit_given = 0;
   char **use_argv;
 
   char **argv = argv_cpy_dup(argc, argv_orig);
@@ -846,7 +850,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   SAYF(cCYA "afl-tmin" VERSION cRST " by Michal Zalewski\n");
 
-  while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeAOQUWXYHh")) > 0) {
+  while ((opt = getopt(argc, argv, "+i:o:f:m:t:l:B:xeAOQUWXYHh")) > 0) {
 
     switch (opt) {
 
@@ -1055,6 +1059,24 @@ int main(int argc, char **argv_orig, char **envp) {
         read_bitmap(optarg, mask_bitmap, map_size);
         break;
 
+      case 'l':
+        if (del_limit_given) { FATAL("Multiple -l options not supported"); }
+        del_limit_given = 1;
+
+        if (!optarg) { FATAL("Wrong usage of -l"); }
+
+        if (optarg[0] == '-') { FATAL("Dangerously low value of -l"); }
+
+        del_len_limit = atoi(optarg);
+
+        if (del_len_limit < 1 || del_len_limit > TMIN_MAX_FILE) {
+
+          FATAL("Value of -l out of range between 1 and TMIN_MAX_FILE");
+
+        }
+
+        break;
+
       case 'h':
         usage(argv[0]);
         return -1;
diff --git a/src/hashmap.c b/src/hashmap.c
new file mode 100644
index 00000000..a0a9283c
--- /dev/null
+++ b/src/hashmap.c
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include "types.h"
+#define TABLE_SIZE 10007  // Use a prime number for better distribution
+
+typedef struct HashNode {
+
+  uint64_t         key;
+  struct HashNode *next;
+
+} HashNode;
+
+typedef struct HashMap {
+
+  HashNode **table;
+
+} HashMap;
+
+static HashMap *_hashmap;
+
+void hashmap_reset() {
+
+  if (unlikely(!_hashmap)) {
+
+    _hashmap = (HashMap *)malloc(sizeof(HashMap));
+    _hashmap->table = (HashNode **)malloc(sizeof(HashNode *) * TABLE_SIZE);
+    memset((char *)_hashmap->table, 0, sizeof(HashNode *) * TABLE_SIZE);
+
+  } else {
+
+    for (int i = 0; i < TABLE_SIZE; i++) {
+
+      HashNode *node = _hashmap->table[i];
+      while (node) {
+
+        HashNode *temp = node;
+        node = node->next;
+        free(temp);
+
+      }
+
+    }
+
+    memset((char *)_hashmap->table, 0, sizeof(HashNode *) * TABLE_SIZE);
+
+  }
+
+}
+
+static inline unsigned int hash(uint64_t key) {
+
+  return key % TABLE_SIZE;
+
+}
+
+// type must be below 8
+bool hashmap_search_and_add(uint8_t type, uint64_t key) {
+
+  if (unlikely(type >= 8)) return false;
+  uint64_t     val = (key & 0xf8ffffffffffffff) + (type << 56);
+  unsigned int index = hash(val);
+  HashNode    *node = _hashmap->table[index];
+  while (node) {
+
+    if (node->key == val) return true;
+    node = node->next;
+
+  }
+
+  // not found so add it
+  node = (HashNode *)malloc(sizeof(HashNode));
+  node->key = val;
+  node->next = _hashmap->table[index];
+  _hashmap->table[index] = node;
+
+  return false;
+
+}
+
+// type must be below 8
+bool hashmap_search_and_add_ptr(uint8_t type, u8 *key) {
+
+  if (unlikely(type >= 8)) return false;
+  uint64_t key_t = 0;
+  memcpy(((char *)key_t) + (7 - type), key, type + 1);
+  return hashmap_search_and_add(type, key_t);
+
+}
+
+/* below is not used */
+
+void hashmap_insert(uint64_t key) {
+
+  unsigned int index = hash(key);
+  HashNode    *node = (HashNode *)malloc(sizeof(HashNode));
+  node->key = key;
+  node->next = _hashmap->table[index];
+  _hashmap->table[index] = node;
+
+}
+
+bool hashmap_search(uint64_t key) {
+
+  unsigned int index = hash(key);
+  HashNode    *node = _hashmap->table[index];
+  while (node) {
+
+    if (node->key == key) return true;
+    node = node->next;
+
+  }
+
+  return false;
+
+}
+
+void delete(uint64_t key) {
+
+  unsigned int index = hash(key);
+  HashNode    *prev = NULL, *node = _hashmap->table[index];
+  while (node) {
+
+    if (node->key == key) {
+
+      if (prev)
+        prev->next = node->next;
+      else
+        _hashmap->table[index] = node->next;
+      free(node);
+      return;
+
+    }
+
+    prev = node;
+    node = node->next;
+
+  }
+
+}
+
+void freeHashMap(HashMap *map) {
+
+  free(_hashmap->table);
+  free(map);
+
+}
+