about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/afl-forkserver.c37
-rw-r--r--src/afl-fuzz-bitmap.c9
-rw-r--r--src/afl-fuzz-extras.c58
-rw-r--r--src/afl-fuzz-one.c8
-rw-r--r--src/afl-fuzz-redqueen.c15
-rw-r--r--src/afl-fuzz-run.c10
-rw-r--r--src/afl-fuzz-state.c5
-rw-r--r--src/afl-showmap.c104
8 files changed, 166 insertions, 80 deletions
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index 56475320..173cc70f 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -58,6 +58,8 @@ static list_t fsrv_list = {.element_prealloc_count = 0};
 
 static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) {
 
+  if (fsrv->qemu_mode) setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0);
+
   execv(fsrv->target_path, argv);
 
 }
@@ -122,8 +124,8 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) {
   Returns the time passed to read.
   If the wait times out, returns timeout_ms + 1;
   Returns 0 if an error occurred (fd closed, signal, ...); */
-static u32 read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms,
-                          volatile u8 *stop_soon_p) {
+static u32 __attribute__((hot))
+read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) {
 
   fd_set readfds;
   FD_ZERO(&readfds);
@@ -638,13 +640,19 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
       if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) {
 
-        if (fsrv->function_ptr == NULL || fsrv->function_opt == NULL) {
+        if (fsrv->autodict_func == NULL || fsrv->afl_ptr == NULL) {
 
           // this is not afl-fuzz - we deny and return
-          if (fsrv->use_shmem_fuzz)
+          if (fsrv->use_shmem_fuzz) {
+
             status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ);
-          else
+
+          } else {
+
             status = (FS_OPT_ENABLED);
+
+          }
+
           if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
 
             FATAL("Writing to forkserver failed.");
@@ -657,11 +665,16 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
         if (!be_quiet) { ACTF("Using AUTODICT feature."); }
 
-        if (fsrv->use_shmem_fuzz)
+        if (fsrv->use_shmem_fuzz) {
+
           status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ);
-        else
+
+        } else {
+
           status = (FS_OPT_ENABLED | FS_OPT_AUTODICT);
 
+        }
+
         if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
 
           FATAL("Writing to forkserver failed.");
@@ -680,7 +693,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
         }
 
-        u32 len = status, offset = 0, count = 0;
+        u32 offset = 0, count = 0;
+        u32 len = status;
         u8 *dict = ck_alloc(len);
         if (dict == NULL) {
 
@@ -711,8 +725,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
         while (offset < (u32)status &&
                (u8)dict[offset] + offset < (u32)status) {
 
-          fsrv->function_ptr(fsrv->function_opt, dict + offset + 1,
-                             (u8)dict[offset]);
+          fsrv->autodict_func(fsrv->afl_ptr, dict + offset + 1,
+                              (u8)dict[offset]);
           offset += (1 + dict[offset]);
           count++;
 
@@ -871,7 +885,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
         "    - the target was compiled with afl-clang-lto and a constructor "
         "was\n"
         "      instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve "
-        "your problem\n\n"
+        "your \n"
+        "      problem\n\n"
 
         "    - Less likely, there is a horrible bug in the fuzzer. If other "
         "options\n"
diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c
index d273818d..db57061d 100644
--- a/src/afl-fuzz-bitmap.c
+++ b/src/afl-fuzz-bitmap.c
@@ -57,7 +57,7 @@ void write_bitmap(afl_state_t *afl) {
    This function is called after every exec() on a fairly large buffer, so
    it needs to be fast. We do this in 32-bit and 64-bit flavors. */
 
-u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) {
+u8 __attribute__((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) {
 
 #ifdef WORD_SIZE_64
 
@@ -407,7 +407,7 @@ void init_count_class16(void) {
 
 #ifdef WORD_SIZE_64
 
-void classify_counts(afl_forkserver_t *fsrv) {
+void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) {
 
   u64 *mem = (u64 *)fsrv->trace_bits;
 
@@ -436,7 +436,7 @@ void classify_counts(afl_forkserver_t *fsrv) {
 
 #else
 
-void classify_counts(afl_forkserver_t *fsrv) {
+void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) {
 
   u32 *mem = (u32 *)fsrv->trace_bits;
 
@@ -594,7 +594,8 @@ static void write_crash_readme(afl_state_t *afl) {
    save or queue the input test case for further analysis if so. Returns 1 if
    entry is saved, 0 otherwise. */
 
-u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
+u8 __attribute__((hot))
+save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
   if (unlikely(len == 0)) { return 0; }
 
diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c
index 097871c8..17f02984 100644
--- a/src/afl-fuzz-extras.c
+++ b/src/afl-fuzz-extras.c
@@ -25,23 +25,28 @@
 
 #include "afl-fuzz.h"
 
-/* Helper function for load_extras. */
+/* helper function for auto_extras qsort */
+static int compare_auto_extras_len(const void *ae1, const void *ae2) {
+
+  return ((struct auto_extra_data *)ae1)->len -
+         ((struct auto_extra_data *)ae2)->len;
+
+}
 
-static int compare_extras_len(const void *p1, const void *p2) {
+/* descending order */
 
-  struct extra_data *e1 = (struct extra_data *)p1,
-                    *e2 = (struct extra_data *)p2;
+static int compare_auto_extras_use_d(const void *ae1, const void *ae2) {
 
-  return e1->len - e2->len;
+  return ((struct auto_extra_data *)ae2)->hit_cnt -
+         ((struct auto_extra_data *)ae1)->hit_cnt;
 
 }
 
-static int compare_extras_use_d(const void *p1, const void *p2) {
+/* Helper function for load_extras. */
 
-  struct extra_data *e1 = (struct extra_data *)p1,
-                    *e2 = (struct extra_data *)p2;
+static int compare_extras_len(const void *e1, const void *e2) {
 
-  return e2->hit_cnt - e1->hit_cnt;
+  return ((struct extra_data *)e1)->len - ((struct extra_data *)e2)->len;
 
 }
 
@@ -354,14 +359,10 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) {
 }
 
 /* Maybe add automatic extra. */
-/* Ugly hack: afl state is transfered as u8* because we import data via
-   afl-forkserver.c - which is shared with other afl tools that do not
-   have the afl state struct */
 
-void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) {
+void maybe_add_auto(afl_state_t *afl, u8 *mem, u32 len) {
 
-  afl_state_t *afl = (afl_state_t *)afl_tmp;
-  u32          i;
+  u32 i;
 
   /* Allow users to specify that they don't want auto dictionaries. */
 
@@ -375,7 +376,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) {
 
   }
 
-  if (i == len) { return; }
+  if (i == len || unlikely(len > MAX_AUTO_EXTRA)) { return; }
 
   /* Reject builtin interesting values. */
 
@@ -452,10 +453,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) {
 
   if (afl->a_extras_cnt < MAX_AUTO_EXTRAS) {
 
-    afl->a_extras = ck_realloc_block(
-        afl->a_extras, (afl->a_extras_cnt + 1) * sizeof(struct extra_data));
-
-    afl->a_extras[afl->a_extras_cnt].data = ck_memdup(mem, len);
+    memcpy(afl->a_extras[afl->a_extras_cnt].data, mem, len);
     afl->a_extras[afl->a_extras_cnt].len = len;
     ++afl->a_extras_cnt;
 
@@ -463,9 +461,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) {
 
     i = MAX_AUTO_EXTRAS / 2 + rand_below(afl, (MAX_AUTO_EXTRAS + 1) / 2);
 
-    ck_free(afl->a_extras[i].data);
-
-    afl->a_extras[i].data = ck_memdup(mem, len);
+    memcpy(afl->a_extras[i].data, mem, len);
     afl->a_extras[i].len = len;
     afl->a_extras[i].hit_cnt = 0;
 
@@ -475,13 +471,13 @@ sort_a_extras:
 
   /* First, sort all auto extras by use count, descending order. */
 
-  qsort(afl->a_extras, afl->a_extras_cnt, sizeof(struct extra_data),
-        compare_extras_use_d);
+  qsort(afl->a_extras, afl->a_extras_cnt, sizeof(struct auto_extra_data),
+        compare_auto_extras_use_d);
 
   /* Then, sort the top USE_AUTO_EXTRAS entries by size. */
 
   qsort(afl->a_extras, MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt),
-        sizeof(struct extra_data), compare_extras_len);
+        sizeof(struct auto_extra_data), compare_auto_extras_len);
 
 }
 
@@ -544,7 +540,7 @@ void load_auto(afl_state_t *afl) {
 
     if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA) {
 
-      maybe_add_auto((u8 *)afl, tmp, len);
+      maybe_add_auto(afl, tmp, len);
 
     }
 
@@ -579,13 +575,5 @@ void destroy_extras(afl_state_t *afl) {
 
   ck_free(afl->extras);
 
-  for (i = 0; i < afl->a_extras_cnt; ++i) {
-
-    ck_free(afl->a_extras[i].data);
-
-  }
-
-  ck_free(afl->a_extras);
-
 }
 
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index 4db6febf..6d52b2b4 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -841,7 +841,7 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
         if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) {
 
-          maybe_add_auto((u8 *)afl, a_collect, a_len);
+          maybe_add_auto(afl, a_collect, a_len);
 
         }
 
@@ -852,7 +852,7 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
         if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) {
 
-          maybe_add_auto((u8 *)afl, a_collect, a_len);
+          maybe_add_auto(afl, a_collect, a_len);
 
         }
 
@@ -3108,7 +3108,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) {
 
         if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) {
 
-          maybe_add_auto((u8 *)afl, a_collect, a_len);
+          maybe_add_auto(afl, a_collect, a_len);
 
         }
 
@@ -3119,7 +3119,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) {
 
         if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) {
 
-          maybe_add_auto((u8 *)afl, a_collect, a_len);
+          maybe_add_auto(afl, a_collect, a_len);
 
         }
 
diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c
index 9716be95..f21dd0b0 100644
--- a/src/afl-fuzz-redqueen.c
+++ b/src/afl-fuzz-redqueen.c
@@ -151,7 +151,8 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) {
       /* Discard if the mutations change the paths or if it is too decremental
         in speed */
       if (cksum != exec_cksum ||
-          (stop_us - start_us > 2 * afl->queue_cur->exec_us)) {
+          ((stop_us - start_us > 2 * afl->queue_cur->exec_us) &&
+           likely(!afl->fixed_seed))) {
 
         ranges = add_range(ranges, rng->start, rng->start + s / 2);
         ranges = add_range(ranges, rng->start + s / 2 + 1, rng->end);
@@ -499,7 +500,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) {
 
   }
 
-  maybe_add_auto((u8 *)afl, (u8 *)&v, shape);
+  maybe_add_auto(afl, (u8 *)&v, shape);
 
   u64 rev;
   switch (shape) {
@@ -508,15 +509,15 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) {
       break;
     case 2:
       rev = SWAP16((u16)v);
-      maybe_add_auto((u8 *)afl, (u8 *)&rev, shape);
+      maybe_add_auto(afl, (u8 *)&rev, shape);
       break;
     case 4:
       rev = SWAP32((u32)v);
-      maybe_add_auto((u8 *)afl, (u8 *)&rev, shape);
+      maybe_add_auto(afl, (u8 *)&rev, shape);
       break;
     case 8:
       rev = SWAP64(v);
-      maybe_add_auto((u8 *)afl, (u8 *)&rev, shape);
+      maybe_add_auto(afl, (u8 *)&rev, shape);
       break;
 
   }
@@ -771,8 +772,8 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) {
 
       if (afl->pass_stats[key].total == 0) {
 
-        maybe_add_auto((u8 *)afl, o->v0, SHAPE_BYTES(h->shape));
-        maybe_add_auto((u8 *)afl, o->v1, SHAPE_BYTES(h->shape));
+        maybe_add_auto(afl, o->v0, SHAPE_BYTES(h->shape));
+        maybe_add_auto(afl, o->v1, SHAPE_BYTES(h->shape));
 
       }
 
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index 94cfc383..5381723d 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -38,8 +38,8 @@ u64 time_spent_working = 0;
 /* Execute target application, monitoring for timeouts. Return status
    information. The called program will update afl->fsrv->trace_bits. */
 
-fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv,
-                                  u32 timeout) {
+fsrv_run_result_t __attribute__((hot))
+fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) {
 
 #ifdef PROFILING
   static u64      time_spent_start = 0;
@@ -72,7 +72,8 @@ fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv,
    old file is unlinked and a new one is created. Otherwise, afl->fsrv.out_fd is
    rewound and truncated. */
 
-void write_to_testcase(afl_state_t *afl, void *mem, u32 len) {
+void __attribute__((hot))
+write_to_testcase(afl_state_t *afl, void *mem, u32 len) {
 
 #ifdef _AFL_DOCUMENT_MUTATIONS
   s32  doc_fd;
@@ -871,7 +872,8 @@ abort_trimming:
    error conditions, returning 1 if it's time to bail out. This is
    a helper function for fuzz_one(). */
 
-u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
+u8 __attribute__((hot))
+common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
 
   u8 fault;
 
diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c
index aab785e1..a8416eb1 100644
--- a/src/afl-fuzz-state.c
+++ b/src/afl-fuzz-state.c
@@ -102,8 +102,9 @@ void afl_state_init_1(afl_state_t *afl, uint32_t map_size) {
 
   afl->fsrv.use_stdin = 1;
   afl->fsrv.map_size = map_size;
-  afl->fsrv.function_opt = (u8 *)afl;
-  afl->fsrv.function_ptr = &maybe_add_auto;
+  // afl_state_t is not available in forkserver.c
+  afl->fsrv.afl_ptr = (void *)afl;
+  afl->fsrv.autodict_func = (void (*)(void *, u8 *, u32)) & maybe_add_auto;
 
   afl->cal_cycles = CAL_CYCLES;
   afl->cal_cycles_long = CAL_CYCLES_LONG;
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index 0aa116e5..47c615d8 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -68,9 +68,11 @@ static char *stdin_file;               /* stdin file                        */
 static u8 *in_dir = NULL,              /* input folder                      */
     *out_file = NULL, *at_file = NULL;        /* Substitution string for @@ */
 
-static u8 *in_data;                    /* Input data                        */
+static u8 *in_data,                    /* Input data                        */
+    *coverage_map;                     /* Coverage map                      */
 
-static u32 total, highest;             /* tuple content information         */
+static u64 total;                      /* tuple content information         */
+static u32 tcnt, highest;              /* tuple content information         */
 
 static u32 in_len,                     /* Input data length                 */
     arg_offset;                        /* Total number of execs             */
@@ -83,7 +85,8 @@ static u8 quiet_mode,                  /* Hide non-essential messages?      */
     cmin_mode,                         /* Generate output in afl-cmin mode? */
     binary_mode,                       /* Write output as a binary map      */
     keep_cores,                        /* Allow coredumps?                  */
-    remove_shm = 1;                    /* remove shmem?                     */
+    remove_shm = 1,                    /* remove shmem?                     */
+    collect_coverage;                  /* collect coverage                  */
 
 static volatile u8 stop_soon,          /* Ctrl-C pressed?                   */
     child_crashed;                     /* Child crashed?                    */
@@ -175,6 +178,25 @@ static void at_exit_handler(void) {
 
 }
 
+/* Analyze results. */
+
+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; }
+
+    }
+
+  }
+
+}
+
 /* Write results. */
 
 static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) {
@@ -588,9 +610,14 @@ static void usage(u8 *argv0) {
       "                  (Not necessary, here for consistency with other afl-* "
       "tools)\n\n"
       "Other settings:\n"
-      "  -i dir        - process all files in this directory, -o must be a "
+      "  -i dir        - process all files in this directory, must be combined "
+      "with -o.\n"
+      "                  With -C, -o is a file, without -C it must be a "
       "directory\n"
       "                  and each bitmap will be written there individually.\n"
+      "  -C            - collect coverage, writes all edges to -o and gives a "
+      "summary\n"
+      "                  Must be combined with -i.\n"
       "  -q            - sink program's output and don't show messages\n"
       "  -e            - show edge coverage only, ignore hit counts\n"
       "  -r            - show real tuple values instead of AFL filter values\n"
@@ -624,7 +651,6 @@ int main(int argc, char **argv_orig, char **envp) {
 
   s32    opt, i;
   u8     mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0;
-  u32    tcnt = 0;
   char **use_argv;
 
   char **argv = argv_cpy_dup(argc, argv_orig);
@@ -639,10 +665,14 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; }
 
-  while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqZQUWbcrh")) > 0) {
+  while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrh")) > 0) {
 
     switch (opt) {
 
+      case 'C':
+        collect_coverage = 1;
+        break;
+
       case 'i':
         if (in_dir) { FATAL("Multiple -i options not supported"); }
         in_dir = optarg;
@@ -820,6 +850,13 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (optind == argc || !out_file) { usage(argv[0]); }
 
+  if (in_dir) {
+
+    if (!out_file && !collect_coverage)
+      FATAL("for -i you need to specify either -C and/or -o");
+
+  }
+
   if (fsrv->qemu_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; }
   if (unicorn_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; }
 
@@ -910,7 +947,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (in_dir) {
 
-    DIR *          dir_in, *dir_out;
+    DIR *          dir_in, *dir_out = NULL;
     struct dirent *dir_ent;
     int            done = 0;
     u8             infile[PATH_MAX], outfile[PATH_MAX];
@@ -924,20 +961,43 @@ int main(int argc, char **argv_orig, char **envp) {
     fsrv->dev_null_fd = open("/dev/null", O_RDWR);
     if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
 
+    // if a queue subdirectory exists switch to that
+    u8 *dn = alloc_printf("%s/queue", in_dir);
+    if ((dir_in = opendir(dn)) != NULL) {
+
+      closedir(dir_in);
+      in_dir = dn;
+
+    } else
+
+      ck_free(dn);
+    if (!be_quiet) ACTF("Reading from directory '%s'...", in_dir);
+
     if (!(dir_in = opendir(in_dir))) {
 
       PFATAL("cannot open directory %s", in_dir);
 
     }
 
-    if (!(dir_out = opendir(out_file))) {
+    if (!collect_coverage) {
+
+      if (!(dir_out = opendir(out_file))) {
+
+        if (mkdir(out_file, 0700)) {
 
-      if (mkdir(out_file, 0700)) {
+          PFATAL("cannot create output directory %s", out_file);
 
-        PFATAL("cannot create output directory %s", out_file);
+        }
 
       }
 
+    } else {
+
+      if ((coverage_map = (u8 *)malloc(map_size)) == NULL)
+        FATAL("coult not grab memory");
+      edges_only = 0;
+      raw_instr_output = 1;
+
     }
 
     u8 *use_dir = ".";
@@ -978,6 +1038,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
     afl_fsrv_start(fsrv, use_argv, &stop_soon,
                    get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0);
+    map_size = fsrv->map_size;
 
     if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz)
       shm_fuzz = deinit_shmem(fsrv, shm_fuzz);
@@ -1005,7 +1066,8 @@ int main(int argc, char **argv_orig, char **envp) {
       if (-1 == stat(infile, &statbuf) || !S_ISREG(statbuf.st_mode)) continue;
 #endif
 
-      snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name);
+      if (!collect_coverage)
+        snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name);
 
       if (read_file(infile)) {
 
@@ -1019,7 +1081,10 @@ int main(int argc, char **argv_orig, char **envp) {
 
         showmap_run_target_forkserver(fsrv, in_data, in_len);
         ck_free(in_data);
-        tcnt = write_results_to_file(fsrv, outfile);
+        if (collect_coverage)
+          analyze_results(fsrv);
+        else
+          tcnt = write_results_to_file(fsrv, outfile);
 
       }
 
@@ -1030,6 +1095,13 @@ int main(int argc, char **argv_orig, char **envp) {
     closedir(dir_in);
     if (dir_out) { closedir(dir_out); }
 
+    if (collect_coverage) {
+
+      memcpy(fsrv->trace_bits, coverage_map, map_size);
+      tcnt = write_results_to_file(fsrv, out_file);
+
+    }
+
   } else {
 
     if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz)
@@ -1043,8 +1115,14 @@ int main(int argc, char **argv_orig, char **envp) {
   if (!quiet_mode) {
 
     if (!tcnt) { FATAL("No instrumentation detected" cRST); }
-    OKF("Captured %u tuples (highest value %u, total values %u) in '%s'." cRST,
+    OKF("Captured %u tuples (highest value %u, total values %llu) in "
+        "'%s'." cRST,
         tcnt, highest, total, out_file);
+    if (collect_coverage)
+      OKF("A coverage of %u edges were achieved out of %u existing (%.02f%%) "
+          "with %llu input files.",
+          tcnt, map_size, ((float)tcnt * 100) / (float)map_size,
+          fsrv->total_execs);
 
   }