about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--TODO.md3
-rw-r--r--custom_mutators/radamsa/libradamsa.c2
-rw-r--r--docs/Changelog.md5
-rw-r--r--docs/env_variables.md2
-rw-r--r--include/afl-fuzz.h15
-rw-r--r--include/envs.h3
-rw-r--r--include/forkserver.h4
-rw-r--r--instrumentation/SanitizerCoverageLTO.so.cc29
-rw-r--r--instrumentation/SanitizerCoveragePCGUARD.so.cc44
-rw-r--r--src/afl-forkserver.c8
-rw-r--r--src/afl-fuzz-init.c2
-rw-r--r--src/afl-fuzz-queue.c9
-rw-r--r--src/afl-fuzz-run.c21
-rw-r--r--src/afl-fuzz-state.c7
-rw-r--r--src/afl-fuzz-stats.c8
-rw-r--r--src/afl-fuzz.c279
-rw-r--r--src/afl-performance.c2
17 files changed, 383 insertions, 60 deletions
diff --git a/TODO.md b/TODO.md
index b36269b4..6f7505a6 100644
--- a/TODO.md
+++ b/TODO.md
@@ -2,7 +2,8 @@
 
 ## Must
 
- - fast restart of afl-fuzz if cmdline + target hash is the same
+ - fast resume:
+   use gzopen(fn, "rb"/"wb9-/f/h", gzwrite, gzread and gzclose
  - check for null ptr for xml/curl/g_ string transform functions
  - hardened_usercopy=0 page_alloc.shuffle=0
  - add value_profile but only enable after 15 minutes without finds
diff --git a/custom_mutators/radamsa/libradamsa.c b/custom_mutators/radamsa/libradamsa.c
index e6838752..1dcf91d8 100644
--- a/custom_mutators/radamsa/libradamsa.c
+++ b/custom_mutators/radamsa/libradamsa.c
@@ -3707,7 +3707,7 @@ typedef intptr_t     wdiff;
   1024 * 1024 * 8         /* static malloc'd heap size if used as a library */
 #define FBITS 24             /* bits in fixnum, on the way to 24 and beyond */
 #define FMAX                                                       \
-  ((1 << FBITS) - 1)  /* maximum fixnum (and most negative fixnum) \
+  ((1U << FBITS) - 1)  /* maximum fixnum (and most negative fixnum) \
                        */
 #define MAXOBJ 0xffff                /* max words in tuple including header */
 #define MAXPAYL                                                \
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 1590b2df..f146534f 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -4,6 +4,11 @@
   release of the tool. See README.md for the general instruction manual.
 
 ### Version ++4.22a (dev)
+  - afl-fuzz:
+    - fastresume feature added. if you abort fuzzing and resume fuzzing
+      with `-i -` or `AFL_AUTORESUME=1` and the target binary has not changed
+      then a dump will be loaded and the calibration phase skipped.
+      to disable this feature set `AFL_NO_FASTRESUME=1`
   - frida_mode:
     - AFL_FRIDA_PERSISTENT_ADDR can now be be any reachable address not just
       a function entry
diff --git a/docs/env_variables.md b/docs/env_variables.md
index 22e0ce0f..6db31df0 100644
--- a/docs/env_variables.md
+++ b/docs/env_variables.md
@@ -514,6 +514,8 @@ checks or alter some of the more exotic semantics of the tool:
   - `AFL_NO_SNAPSHOT` will advise afl-fuzz not to use the snapshot feature if
     the snapshot lkm is loaded.
 
+  - `AFL_NO_FASTRESUME` will not try to read or write a fast resume file.
+
   - Setting `AFL_NO_UI` inhibits the UI altogether and just periodically prints
     some basic stats. This behavior is also automatically triggered when the
     output from afl-fuzz is redirected to a file or to a pipe.
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index e3e4e246..6d03a74c 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -236,7 +236,6 @@ struct queue_entry {
       custom,                           /* Marker for custom mutators       */
       stats_mutated;                    /* stats: # of mutations performed  */
 
-  u8 *trace_mini;                       /* Trace bytes, if kept             */
   u32 tc_ref;                           /* Trace bytes ref count            */
 
 #ifdef INTROSPECTION
@@ -246,13 +245,11 @@ struct queue_entry {
   double perf_score,                    /* performance score                */
       weight;
 
-  u8 *testcase_buf;                     /* The testcase buffer, if loaded.  */
-
-  u8             *cmplog_colorinput;    /* the result buf of colorization   */
-  struct tainted *taint;                /* Taint information from CmpLog    */
-
-  struct queue_entry *mother;           /* queue entry this based on        */
-
+  struct queue_entry *mother;            /* queue entry this based on        */
+  u8                 *trace_mini;        /* Trace bytes, if kept             */
+  u8                 *testcase_buf;      /* The testcase buffer, if loaded.  */
+  u8                 *cmplog_colorinput; /* the result buf of colorization   */
+  struct tainted     *taint;             /* Taint information from CmpLog    */
   struct skipdet_entry *skipdet_e;
 
 };
@@ -457,7 +454,7 @@ typedef struct afl_env_vars {
       afl_no_startup_calibration, afl_no_warn_instability,
       afl_post_process_keep_original, afl_crashing_seeds_as_new_crash,
       afl_final_sync, afl_ignore_seed_problems, afl_disable_redundant,
-      afl_sha1_filenames, afl_no_sync;
+      afl_sha1_filenames, afl_no_sync, afl_no_fastresume;
 
   u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path,
       *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload,
diff --git a/include/envs.h b/include/envs.h
index 45b080cb..ef522ab4 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -115,7 +115,8 @@ static char *afl_environment_variables[] = {
     "AFL_TRACE_PC", "AFL_USE_ASAN", "AFL_USE_MSAN", "AFL_USE_TRACE_PC",
     "AFL_USE_UBSAN", "AFL_USE_TSAN", "AFL_USE_CFISAN", "AFL_USE_LSAN",
     "AFL_WINE_PATH", "AFL_NO_SNAPSHOT", "AFL_EXPAND_HAVOC_NOW", "AFL_USE_FASAN",
-    "AFL_USE_QASAN", "AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", NULL
+    "AFL_USE_QASAN", "AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE",
+    "AFL_NO_FASTRESUME", NULL
 
 };
 
diff --git a/include/forkserver.h b/include/forkserver.h
index 593e34a2..3fd813a4 100644
--- a/include/forkserver.h
+++ b/include/forkserver.h
@@ -206,6 +206,10 @@ typedef struct afl_forkserver {
   s32                   nyx_log_fd;
 #endif
 
+#ifdef __AFL_CODE_COVERAGE
+  u8 *persistent_trace_bits;                   /* Persistent copy of bitmap */
+#endif
+
 } afl_forkserver_t;
 
 typedef enum fsrv_run_result {
diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc
index a09f28a9..63ea71c1 100644
--- a/instrumentation/SanitizerCoverageLTO.so.cc
+++ b/instrumentation/SanitizerCoverageLTO.so.cc
@@ -214,8 +214,12 @@ class ModuleSanitizerCoverageLTO
 
   void SetNoSanitizeMetadata(Instruction *I) {
 
+#if LLVM_VERSION_MAJOR >= 19
+    I->setNoSanitizeMetadata();
+#else
     I->setMetadata(I->getModule()->getMDKindID("nosanitize"),
                    MDNode::get(*C, None));
+#endif
 
   }
 
@@ -225,7 +229,7 @@ class ModuleSanitizerCoverageLTO
   FunctionCallee SanCovTracePCIndir;
   FunctionCallee SanCovTracePC /*, SanCovTracePCGuard*/;
   Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy,
-      *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy;
+      *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy, *PtrTy;
   Module           *CurModule;
   std::string       CurModuleUniqueId;
   Triple            TargetTriple;
@@ -416,6 +420,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
   Int16Ty = IRB.getInt16Ty();
   Int8Ty = IRB.getInt8Ty();
   Int1Ty = IRB.getInt1Ty();
+  PtrTy = PointerType::getUnqual(*C);
 
   /* AFL++ START */
   char        *ptr;
@@ -1350,7 +1355,7 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
     Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
 
   if (F.empty()) return;
-  if (F.getName().find(".module_ctor") != std::string::npos)
+  if (F.getName().contains(".module_ctor"))
     return;  // Should not instrument sanitizer init functions.
 #if LLVM_VERSION_MAJOR >= 18
   if (F.getName().starts_with("__sanitizer_"))
@@ -1372,6 +1377,10 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
   if (F.hasPersonalityFn() &&
       isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
     return;
+  if (F.hasFnAttribute(Attribute::NoSanitizeCoverage)) return;
+#if LLVM_VERSION_MAJOR >= 19
+  if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) return;
+#endif
   // if (Allowlist && !Allowlist->inSection("coverage", "fun", F.getName()))
   //  return;
   // if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName()))
@@ -2023,16 +2032,20 @@ GlobalVariable *ModuleSanitizerCoverageLTO::CreatePCArray(
 
     if (&F.getEntryBlock() == AllBlocks[i]) {
 
-      PCs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy));
-      PCs.push_back((Constant *)IRB.CreateIntToPtr(
-          ConstantInt::get(IntptrTy, 1), IntptrPtrTy));
+      PCs.push_back((Constant *)IRB.CreatePointerCast(&F, PtrTy));
+      PCs.push_back(
+          (Constant *)IRB.CreateIntToPtr(ConstantInt::get(IntptrTy, 1), PtrTy));
 
     } else {
 
       PCs.push_back((Constant *)IRB.CreatePointerCast(
-          BlockAddress::get(AllBlocks[i]), IntptrPtrTy));
-      PCs.push_back((Constant *)IRB.CreateIntToPtr(
-          ConstantInt::get(IntptrTy, 0), IntptrPtrTy));
+          BlockAddress::get(AllBlocks[i]), PtrTy));
+#if LLVM_VERSION_MAJOR >= 16
+      PCs.push_back(Constant::getNullValue(PtrTy));
+#else
+      PCs.push_back(
+          (Constant *)IRB.CreateIntToPtr(ConstantInt::get(IntptrTy, 0), PtrTy));
+#endif
 
     }
 
diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc
index 01881f28..49fe904b 100644
--- a/instrumentation/SanitizerCoveragePCGUARD.so.cc
+++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc
@@ -161,7 +161,9 @@ class ModuleSanitizerCoverageAFL
 
   void SetNoSanitizeMetadata(Instruction *I) {
 
-#if LLVM_VERSION_MAJOR >= 16
+#if LLVM_VERSION_MAJOR >= 19
+    I->setNoSanitizeMetadata();
+#elif LLVM_VERSION_MAJOR >= 16
     I->setMetadata(LLVMContext::MD_nosanitize, MDNode::get(*C, std::nullopt));
 #else
     I->setMetadata(I->getModule()->getMDKindID("nosanitize"),
@@ -179,7 +181,7 @@ class ModuleSanitizerCoverageAFL
   FunctionCallee  SanCovTraceSwitchFunction;
   GlobalVariable *SanCovLowestStack;
   Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy,
-      *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy;
+      *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy, *PtrTy;
   Module           *CurModule;
   std::string       CurModuleUniqueId;
   Triple            TargetTriple;
@@ -272,13 +274,19 @@ std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd(
   if (!TargetTriple.isOSBinFormatCOFF())
     return std::make_pair(SecStart, SecEnd);
 
-  // Account for the fact that on windows-msvc __start_* symbols actually
-  // point to a uint64_t before the start of the array.
+    // Account for the fact that on windows-msvc __start_* symbols actually
+    // point to a uint64_t before the start of the array.
+#if LLVM_VERSION_MAJOR >= 19
+  auto GEP =
+      IRB.CreatePtrAdd(SecStart, ConstantInt::get(IntptrTy, sizeof(uint64_t)));
+  return std::make_pair(GEP, SecEnd);
+#else
   auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy);
   auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr,
                            ConstantInt::get(IntptrTy, sizeof(uint64_t)));
   return std::make_pair(IRB.CreatePointerCast(GEP, PointerType::getUnqual(Ty)),
                         SecEnd);
+#endif
 
 }
 
@@ -370,6 +378,7 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
   Int16Ty = IRB.getInt16Ty();
   Int8Ty = IRB.getInt8Ty();
   Int1Ty = IRB.getInt1Ty();
+  PtrTy = PointerType::getUnqual(*C);
 
   LLVMContext &Ctx = M.getContext();
   AFLMapPtr =
@@ -572,7 +581,8 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
 
   if (F.empty()) return;
   if (!isInInstrumentList(&F, FMNAME)) return;
-  if (F.getName().find(".module_ctor") != std::string::npos)
+  // if (F.getName().find(".module_ctor") != std::string::npos)
+  if (F.getName().contains(".module_ctor"))
     return;  // Should not instrument sanitizer init functions.
 #if LLVM_VERSION_MAJOR >= 18
   if (F.getName().starts_with("__sanitizer_"))
@@ -595,6 +605,9 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
       isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
     return;
   if (F.hasFnAttribute(Attribute::NoSanitizeCoverage)) return;
+#if LLVM_VERSION_MAJOR >= 19
+  if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) return;
+#endif
   if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
     SplitAllCriticalEdges(
         F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests());
@@ -692,16 +705,16 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreatePCArray(
 
     if (&F.getEntryBlock() == AllBlocks[i]) {
 
-      PCs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy));
-      PCs.push_back((Constant *)IRB.CreateIntToPtr(
-          ConstantInt::get(IntptrTy, 1), IntptrPtrTy));
+      PCs.push_back((Constant *)IRB.CreatePointerCast(&F, PtrTy));
+      PCs.push_back(
+          (Constant *)IRB.CreateIntToPtr(ConstantInt::get(IntptrTy, 1), PtrTy));
 
     } else {
 
       PCs.push_back((Constant *)IRB.CreatePointerCast(
-          BlockAddress::get(AllBlocks[i]), IntptrPtrTy));
+          BlockAddress::get(AllBlocks[i]), PtrTy));
 #if LLVM_VERSION_MAJOR >= 16
-      PCs.push_back(Constant::getNullValue(IntptrPtrTy));
+      PCs.push_back(Constant::getNullValue(PtrTy));
 #else
       PCs.push_back((Constant *)IRB.CreateIntToPtr(
           ConstantInt::get(IntptrTy, 0), IntptrPtrTy));
@@ -711,10 +724,10 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreatePCArray(
 
   }
 
-  auto *PCArray = CreateFunctionLocalArrayInSection(N * 2, F, IntptrPtrTy,
-                                                    SanCovPCsSectionName);
+  auto *PCArray =
+      CreateFunctionLocalArrayInSection(N * 2, F, PtrTy, SanCovPCsSectionName);
   PCArray->setInitializer(
-      ConstantArray::get(ArrayType::get(IntptrPtrTy, N * 2), PCs));
+      ConstantArray::get(ArrayType::get(PtrTy, N * 2), PCs));
   PCArray->setConstant(true);
 
   return PCArray;
@@ -822,7 +835,12 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
         StringRef FuncName = Callee->getName();
         if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue;
 
+#if LLVM_VERSION_MAJOR >= 20
+        // test canary
+        InstrumentationIRBuilder IRB(callInst);
+#else
         IRBuilder<> IRB(callInst);
+#endif
 
         if (!FunctionGuardArray) {
 
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index 71d8570d..a998c10f 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -252,6 +252,10 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
   fsrv->uses_crash_exitcode = false;
   fsrv->uses_asan = false;
 
+#ifdef __AFL_CODE_COVERAGE
+  fsrv->persistent_trace_bits = NULL;
+#endif
+
   fsrv->init_child_func = fsrv_exec_child;
   list_append(&fsrv_list, fsrv);
 
@@ -278,6 +282,10 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) {
   fsrv_to->fsrv_kill_signal = from->fsrv_kill_signal;
   fsrv_to->debug = from->debug;
 
+#ifdef __AFL_CODE_COVERAGE
+  fsrv_to->persistent_trace_bits = from->persistent_trace_bits;
+#endif
+
   // These are forkserver specific.
   fsrv_to->out_dir_fd = -1;
   fsrv_to->child_pid = -1;
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index 7310e49f..98de26dd 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -1019,7 +1019,7 @@ void perform_dry_run(afl_state_t *afl) {
 
           }
 
-          if (!q->was_fuzzed) {
+          if (unlikely(!q->was_fuzzed)) {
 
             q->was_fuzzed = 1;
             afl->reinit_table = 1;
diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c
index f4cb930d..6069f5b9 100644
--- a/src/afl-fuzz-queue.c
+++ b/src/afl-fuzz-queue.c
@@ -401,7 +401,9 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) {
 
   } else {
 
-    if (unlink(fn)) { PFATAL("Unable to remove '%s'", fn); }
+    if (unlink(fn)) {                 /*PFATAL("Unable to remove '%s'", fn);*/
+
+    }
 
   }
 
@@ -699,12 +701,11 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
 
 void destroy_queue(afl_state_t *afl) {
 
-  u32 i;
+  u32                 i;
+  struct queue_entry *q;
 
   for (i = 0; i < afl->queued_items; i++) {
 
-    struct queue_entry *q;
-
     q = afl->queue_buf[i];
     ck_free(q->fname);
     ck_free(q->trace_mini);
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index 6a0da6ab..c234fc42 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -60,6 +60,27 @@ 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);
 
+#ifdef __AFL_CODE_COVERAGE
+  if (unlikely(!fsrv->persistent_trace_bits)) {
+
+    // On the first run, we allocate the persistent map to collect coverage.
+    fsrv->persistent_trace_bits = (u8 *)malloc(fsrv->map_size);
+    memset(fsrv->persistent_trace_bits, 0, fsrv->map_size);
+
+  }
+
+  for (u32 i = 0; i < fsrv->map_size; ++i) {
+
+    if (fsrv->persistent_trace_bits[i] != 255 && fsrv->trace_bits[i]) {
+
+      fsrv->persistent_trace_bits[i]++;
+
+    }
+
+  }
+
+#endif
+
   /* If post_run() function is defined in custom mutator, the function will be
      called each time after AFL++ executes the target program. */
 
diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c
index fbe6d32a..dd684a19 100644
--- a/src/afl-fuzz-state.c
+++ b/src/afl-fuzz-state.c
@@ -286,6 +286,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
             afl->afl_env.afl_no_sync =
                 get_afl_env(afl_environment_variables[i]) ? 1 : 0;
 
+          } else if (!strncmp(env, "AFL_NO_FASTRESUME",
+
+                              afl_environment_variable_len)) {
+
+            afl->afl_env.afl_no_fastresume =
+                get_afl_env(afl_environment_variables[i]) ? 1 : 0;
+
           } else if (!strncmp(env, "AFL_CUSTOM_MUTATOR_ONLY",
 
                               afl_environment_variable_len)) {
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c
index 3a71e158..a20c46d0 100644
--- a/src/afl-fuzz-stats.c
+++ b/src/afl-fuzz-stats.c
@@ -76,7 +76,13 @@ char *get_fuzzing_state(afl_state_t *afl) {
 
 void write_setup_file(afl_state_t *afl, u32 argc, char **argv) {
 
-  u8 fn[PATH_MAX];
+  u8 fn[PATH_MAX], fn2[PATH_MAX];
+
+  snprintf(fn2, PATH_MAX, "%s/target_hash", afl->out_dir);
+  FILE *f2 = create_ffile(fn2);
+  fprintf(f2, "%p\n", (void *)get_binary_hash(afl->fsrv.target_path));
+  fclose(f2);
+
   snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir);
   FILE *f = create_ffile(fn);
   u32   i;
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index a7ddef6e..0ae12fc1 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -181,7 +181,7 @@ static void usage(u8 *argv0, int more_help) {
       "it.\n"
       "                  if using QEMU/FRIDA or the fuzzing target is "
       "compiled\n"
-      "                  for CmpLog then use '-c 0'. To disable Cmplog use '-c "
+      "                  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 "
@@ -335,6 +335,7 @@ static void usage(u8 *argv0, int more_help) {
       "AFL_STATSD_PORT: change default statsd port (default: 8125)\n"
       "AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n"
       "                        suported formats: dogstatsd, librato, signalfx, influxdb\n"
+      "AFL_NO_FASTRESUME: do not read or write a fast resume file\n"
       "AFL_NO_SYNC: disables all syncing\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"
@@ -960,7 +961,11 @@ int main(int argc, char **argv_orig, char **envp) {
 
         }
 
-        if (afl->fsrv.mem_limit < 5) { FATAL("Dangerously low value of -m"); }
+        if (afl->fsrv.mem_limit && afl->fsrv.mem_limit < 5) {
+
+          FATAL("Dangerously low value of -m");
+
+        }
 
         if (sizeof(rlim_t) == 4 && afl->fsrv.mem_limit > 2000) {
 
@@ -1868,6 +1873,13 @@ int main(int argc, char **argv_orig, char **envp) {
 
           OKF("Using Frida Address Sanitizer Mode");
 
+          if (afl->fsrv.mem_limit) {
+
+            WARNF("in the Frida Address Sanitizer Mode we disable all memory limits");
+            afl->fsrv.mem_limit = 0;
+
+          }
+
           fasan_check_afl_preload(afl_preload);
 
           setenv("ASAN_OPTIONS", "detect_leaks=false", 1);
@@ -2101,13 +2113,80 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  setup_cmdline_file(afl, argv + optind);
+  check_binary(afl, argv[optind]);
+
+  u64 prev_target_hash = 0;
+  s32 fast_resume = 0, fr_fd = -1;
+
+  if (afl->in_place_resume && !afl->afl_env.afl_no_fastresume) {
+
+    u8 fn[PATH_MAX], buf[32];
+    snprintf(fn, PATH_MAX, "%s/target_hash", afl->out_dir);
+    fr_fd = open(fn, O_RDONLY);
+    if (fr_fd >= 0) {
+
+      if (read(fr_fd, buf, 32) >= 16) {
+
+        sscanf(buf, "%p", (void **)&prev_target_hash);
+
+      }
+
+      close(fr_fd);
+
+    }
+
+  }
+
   write_setup_file(afl, argc, argv);
 
-  setup_cmdline_file(afl, argv + optind);
+  if (afl->in_place_resume && !afl->afl_env.afl_no_fastresume) {
+
+    u64 target_hash = get_binary_hash(afl->fsrv.target_path);
+
+    if (!target_hash || prev_target_hash != target_hash) {
+
+      ACTF("Target binary is different, cannot perform FAST RESUME!");
+
+    } else {
+
+      u8 fn[PATH_MAX];
+      snprintf(fn, PATH_MAX, "%s/fastresume.bin", afl->out_dir);
+      if ((fr_fd = open(fn, O_RDONLY)) >= 0) {
+
+        u8   ver_string[8];
+        u64 *ver = (u64 *)ver_string;
+        u64  expect_ver =
+            afl->shm.cmplog_mode + (sizeof(struct queue_entry) << 1);
+
+        if (read(fr_fd, ver_string, sizeof(ver_string)) != sizeof(ver_string))
+          WARNF("Emtpy fastresume.bin, ignoring, cannot perform FAST RESUME");
+        else if (expect_ver != *ver)
+          WARNF(
+              "Different AFL++ version or feature usage, cannot perform FAST "
+              "RESUME");
+        else {
+
+          OKF("Will perform FAST RESUME");
+          fast_resume = 1;
+
+        }
+
+      } else {
+
+        ACTF("fastresume.bin not found, cannot perform FAST RESUME!");
+
+      }
+
+      // If the fast resume file is not valid we will be unable to start, so
+      // we remove the file but keep the file descriptor open.
+      unlink(fn);
+
+    }
+
+  }
 
   read_testcases(afl, NULL);
-  // read_foreign_testcases(afl, 1); for the moment dont do this
-  OKF("Loaded a total of %u seeds.", afl->queued_items);
 
   pivot_inputs(afl);
 
@@ -2140,6 +2219,9 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  // read_foreign_testcases(afl, 1); for the moment dont do this
+  OKF("Loaded a total of %u seeds.", afl->queued_items);
+
   /* If we don't have a file name chosen yet, use a safe default. */
 
   if (!afl->fsrv.out_file) {
@@ -2196,8 +2278,6 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
-  check_binary(afl, argv[optind]);
-
   #ifdef AFL_PERSISTENT_RECORD
   if (unlikely(afl->fsrv.persistent_record)) {
 
@@ -2416,7 +2496,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
     }
 
-    OKF("Cmplog forkserver successfully started");
+    OKF("CMPLOG forkserver successfully started");
 
   }
 
@@ -2454,29 +2534,102 @@ int main(int argc, char **argv_orig, char **envp) {
   dedup_extras(afl);
   if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); }
 
-  // after we have the correct bitmap size we can read the bitmap -B option
-  // and set the virgin maps
-  if (afl->in_bitmap) {
+  if (unlikely(fast_resume)) {
 
-    read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size);
+    u64 resume_start = get_cur_time_us();
+    // if we get here then we should abort on errors
+    ck_read(fr_fd, afl->virgin_bits, afl->fsrv.map_size, "virgin_bits");
+    ck_read(fr_fd, afl->virgin_tmout, afl->fsrv.map_size, "virgin_tmout");
+    ck_read(fr_fd, afl->virgin_crash, afl->fsrv.map_size, "virgin_crash");
+    ck_read(fr_fd, afl->var_bytes, afl->fsrv.map_size, "var_bytes");
 
-  } else {
+    u8                  res[1] = {0};
+    u8                 *o_start = (u8 *)&(afl->queue_buf[0]->colorized);
+    u8                 *o_end = (u8 *)&(afl->queue_buf[0]->mother);
+    u32                 r = 8 + afl->fsrv.map_size * 4;
+    u32                 q_len = o_end - o_start;
+    u32                 m_len = (afl->fsrv.map_size >> 3);
+    struct queue_entry *q;
 
-    memset(afl->virgin_bits, 255, map_size);
+    for (u32 i = 0; i < afl->queued_items; i++) {
 
-  }
+      q = afl->queue_buf[i];
+      ck_read(fr_fd, (u8 *)&(q->colorized), q_len, "queue data");
+      ck_read(fr_fd, res, 1, "check map");
+      if (res[0]) {
 
-  memset(afl->virgin_tmout, 255, map_size);
-  memset(afl->virgin_crash, 255, map_size);
+        q->trace_mini = ck_alloc(m_len);
+        ck_read(fr_fd, q->trace_mini, m_len, "trace_mini");
+        r += q_len + m_len + 1;
+
+      } else {
 
-  if (likely(!afl->afl_env.afl_no_startup_calibration)) {
+        r += q_len + 1;
 
-    perform_dry_run(afl);
+      }
+
+      afl->total_bitmap_size += q->bitmap_size;
+      ++afl->total_bitmap_entries;
+      update_bitmap_score(afl, q);
+
+      if (q->was_fuzzed) { --afl->pending_not_fuzzed; }
+
+      if (q->disabled) {
+
+        if (!q->was_fuzzed) { --afl->pending_not_fuzzed; }
+        --afl->active_items;
+
+      }
+
+      if (q->var_behavior) { ++afl->queued_variable; }
+      if (q->favored) {
+
+        ++afl->queued_favored;
+        if (!q->was_fuzzed) { ++afl->pending_favored; }
+
+      }
+
+    }
+
+    u8 buf[4];
+    if (read(fr_fd, buf, 3) > 0) {
+
+      FATAL("invalid trailing data in fastresume.bin");
+
+    }
+
+    OKF("Successfully loaded fastresume.bin (%u bytes)!", r);
+    close(fr_fd);
+    afl->reinit_table = 1;
+    update_calibration_time(afl, &resume_start);
 
   } else {
 
-    ACTF("skipping initial seed calibration due option override!");
-    usleep(1000);
+    // after we have the correct bitmap size we can read the bitmap -B option
+    // and set the virgin maps
+    if (afl->in_bitmap) {
+
+      read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size);
+
+    } else {
+
+      memset(afl->virgin_bits, 255, map_size);
+
+    }
+
+    memset(afl->virgin_tmout, 255, map_size);
+    memset(afl->virgin_crash, 255, map_size);
+
+    if (likely(!afl->afl_env.afl_no_startup_calibration)) {
+
+      perform_dry_run(afl);
+
+    } else {
+
+      ACTF("Skipping initial seed calibration due option override!");
+      usleep(1000);
+
+    }
 
   }
 
@@ -2984,6 +3137,28 @@ stop_fuzzing:
   write_bitmap(afl);
   save_auto(afl);
 
+  #ifdef __AFL_CODE_COVERAGE
+  if (afl->fsrv.persistent_trace_bits) {
+
+    char cfn[4096];
+    snprintf(cfn, sizeof(cfn), "%s/covmap.dump", afl->out_dir);
+
+    FILE *cov_fd;
+    if ((cov_fd = fopen(cfn, "w")) == NULL) {
+
+      PFATAL("could not create '%s'", cfn);
+
+    }
+
+    // Write the real map size, as the map size must exactly match the pointer
+    // map in length.
+    fwrite(afl->fsrv.persistent_trace_bits, 1, afl->fsrv.real_map_size, cov_fd);
+    fclose(cov_fd);
+
+  }
+
+  #endif
+
   if (afl->pizza_is_served) {
 
     SAYF(CURSOR_SHOW cLRD "\n\n+++ Baking aborted %s +++\n" cRST,
@@ -3067,6 +3242,68 @@ stop_fuzzing:
   fclose(afl->fsrv.det_plot_file);
   #endif
 
+  if (!afl->afl_env.afl_no_fastresume) {
+
+    /* create fastresume.bin */
+    u8 fr[PATH_MAX];
+    snprintf(fr, PATH_MAX, "%s/fastresume.bin", afl->out_dir);
+    ACTF("Writing %s ...", fr);
+    if ((fr_fd = open(fr, O_WRONLY | O_TRUNC | O_CREAT, DEFAULT_PERMISSION)) >=
+        0) {
+
+      u8   ver_string[8];
+      u32  w = 0;
+      u64 *ver = (u64 *)ver_string;
+      *ver = afl->shm.cmplog_mode + (sizeof(struct queue_entry) << 1);
+
+      w += write(fr_fd, ver_string, sizeof(ver_string));
+
+      w += write(fr_fd, afl->virgin_bits, afl->fsrv.map_size);
+      w += write(fr_fd, afl->virgin_tmout, afl->fsrv.map_size);
+      w += write(fr_fd, afl->virgin_crash, afl->fsrv.map_size);
+      w += write(fr_fd, afl->var_bytes, afl->fsrv.map_size);
+
+      u8                  on[1] = {1}, off[1] = {0};
+      u8                 *o_start = (u8 *)&(afl->queue_buf[0]->colorized);
+      u8                 *o_end = (u8 *)&(afl->queue_buf[0]->mother);
+      u32                 q_len = o_end - o_start;
+      u32                 m_len = (afl->fsrv.map_size >> 3);
+      struct queue_entry *q;
+
+      afl->pending_not_fuzzed = afl->queued_items;
+      afl->active_items = afl->queued_items;
+
+      for (u32 i = 0; i < afl->queued_items; i++) {
+
+        q = afl->queue_buf[i];
+        ck_write(fr_fd, (u8 *)&(q->colorized), q_len, "queue data");
+        if (!q->trace_mini) {
+
+          ck_write(fr_fd, off, 1, "no_mini");
+          w += q_len + 1;
+
+        } else {
+
+          ck_write(fr_fd, on, 1, "yes_mini");
+          ck_write(fr_fd, q->trace_mini, m_len, "trace_mini");
+          w += q_len + m_len + 1;
+
+        }
+
+      }
+
+      close(fr_fd);
+      afl->var_byte_count = count_bytes(afl, afl->var_bytes);
+      OKF("Written fastresume.bin with %u bytes!", w);
+
+    } else {
+
+      WARNF("Could not create fastresume.bin");
+
+    }
+
+  }
+
   destroy_queue(afl);
   destroy_extras(afl);
   destroy_custom_mutators(afl);
diff --git a/src/afl-performance.c b/src/afl-performance.c
index e8ece6b5..b824fd35 100644
--- a/src/afl-performance.c
+++ b/src/afl-performance.c
@@ -99,11 +99,13 @@ inline u64 hash64(u8 *key, u32 len, u64 seed) {
 
 u64 get_binary_hash(u8 *fn) {
 
+  if (!fn) { return 0; }
   int fd = open(fn, O_RDONLY);
   if (fd < 0) { PFATAL("Unable to open '%s'", fn); }
   struct stat st;
   if (fstat(fd, &st) < 0) { PFATAL("Unable to fstat '%s'", fn); }
   u32 f_len = st.st_size;
+  if (!f_len) { return 0; }
   u8 *f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0);
   if (f_data == MAP_FAILED) { PFATAL("Unable to mmap file '%s'", fn); }
   close(fd);