about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/afl-fuzz.h16
-rw-r--r--include/forkserver.h21
-rw-r--r--src/afl-analyze.c2
-rw-r--r--src/afl-forkserver.c132
-rw-r--r--src/afl-fuzz-bitmap.c16
-rw-r--r--src/afl-fuzz-cmplog.c2
-rw-r--r--src/afl-fuzz-init.c14
-rw-r--r--src/afl-fuzz-mutators.c2
-rw-r--r--src/afl-fuzz-one.c12
-rw-r--r--src/afl-fuzz-queue.c2
-rw-r--r--src/afl-fuzz-redqueen.c4
-rw-r--r--src/afl-fuzz-run.c128
-rw-r--r--src/afl-fuzz-stats.c18
-rw-r--r--src/afl-fuzz.c2
-rw-r--r--src/afl-sharedmem.c1
-rw-r--r--src/afl-showmap.c95
-rw-r--r--src/afl-tmin.c109
17 files changed, 222 insertions, 354 deletions
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index 3df99a58..abaa71b5 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -195,18 +195,6 @@ enum {
 
 };
 
-/* Execution status fault codes */
-
-enum {
-
-  /* 00 */ FAULT_NONE,
-  /* 01 */ FAULT_TMOUT,
-  /* 02 */ FAULT_CRASH,
-  /* 03 */ FAULT_ERROR,
-  /* 04 */ FAULT_NOINST,
-  /* 05 */ FAULT_NOBITS
-
-};
 
 #define operator_num 16
 #define swarm_num 5
@@ -433,7 +421,6 @@ typedef struct afl_state {
       use_splicing,                     /* Recombine input files?           */
       dumb_mode,                        /* Run in non-instrumented mode?    */
       score_changed,                    /* Scoring for favorites changed?   */
-      kill_signal,                      /* Signal that killed the child     */
       resuming_fuzz,                    /* Resuming an older fuzzing job?   */
       timeout_given,                    /* Specific timeout given?          */
       not_on_tty,                       /* stdout is not a tty              */
@@ -488,7 +475,6 @@ typedef struct afl_state {
       total_tmouts,                     /* Total number of timeouts         */
       unique_tmouts,                    /* Timeouts with unique signatures  */
       unique_hangs,                     /* Hangs with unique signatures     */
-      total_execs,                      /* Total execve() calls             */
       last_crash_execs,                 /* Exec counter at last crash       */
       queue_cycle,                      /* Queue round counter              */
       cycles_wo_finds,                  /* Cycles without any new paths     */
@@ -888,7 +874,7 @@ void show_init_stats(afl_state_t *);
 
 /* Run */
 
-u8   run_target(afl_state_t *, afl_forkserver_t *fsrv, u32);
+fsrv_run_result_t run_target(afl_state_t *, afl_forkserver_t *fsrv, u32);
 void write_to_testcase(afl_state_t *, void *, u32);
 u8   calibrate_case(afl_state_t *, struct queue_entry *, u8 *, u32, u8);
 void sync_fuzzers(afl_state_t *);
diff --git a/include/forkserver.h b/include/forkserver.h
index 6fbaf612..7559e785 100644
--- a/include/forkserver.h
+++ b/include/forkserver.h
@@ -29,6 +29,7 @@
 #define __AFL_FORKSERVER_H
 
 #include <stdio.h>
+#include <stdbool.h>
 
 typedef struct afl_forkserver {
 
@@ -55,16 +56,18 @@ typedef struct afl_forkserver {
   u32 snapshot;                         /* is snapshot feature used         */
   u64 mem_limit;                        /* Memory cap for child (MB)        */
 
+  u64 total_execs;                      /* How often run_target was called  */
+
   u8 *out_file,                         /* File to fuzz, if any             */
       *target_path;                                   /* Path of the target */
 
   FILE *plot_file;                      /* Gnuplot output file              */
 
-  u8 child_timed_out;                   /* Traced process timed out?        */
+  u8 last_run_timed_out;                   /* Traced process timed out?        */
 
-  u8 use_fauxsrv;                       /* Fauxsrv for non-forking targets? */
+  u8 last_kill_signal;                  /* Signal that killed the child     */
 
-  u32 prev_timed_out;                   /* if prev forkserver run timed out */
+  u8 use_fauxsrv;                       /* Fauxsrv for non-forking targets? */
 
   u8 qemu_mode;                         /* if running in qemu mode or not   */
 
@@ -79,10 +82,22 @@ typedef struct afl_forkserver {
 
 } afl_forkserver_t;
 
+typedef enum fsrv_run_result {
+
+  /* 00 */ FSRV_RUN_OK = 0,
+  /* 01 */ FSRV_RUN_TMOUT,
+  /* 02 */ FSRV_RUN_CRASH,
+  /* 03 */ FSRV_RUN_ERROR,
+  /* 04 */ FSRV_RUN_NOINST,
+  /* 05 */ FSRV_RUN_NOBITS,
+
+} fsrv_run_result_t;
+
 void afl_fsrv_init(afl_forkserver_t *fsrv);
 void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from);
 void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
                     volatile u8 *stop_soon_p, u8 debug_child_output);
+fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, volatile u8 *stop_soon_p);
 void afl_fsrv_killall(void);
 void afl_fsrv_deinit(afl_forkserver_t *fsrv);
 
diff --git a/src/afl-analyze.c b/src/afl-analyze.c
index 952786b0..8625cfda 100644
--- a/src/afl-analyze.c
+++ b/src/afl-analyze.c
@@ -80,7 +80,7 @@ static u8 edges_only,                  /* Ignore hit counts?                */
     use_stdin = 1;                     /* Use stdin for program input?      */
 
 static volatile u8 stop_soon,          /* Ctrl-C pressed?                   */
-    child_timed_out;                   /* Child timed out?                  */
+         child_timed_out;              /* Child timed out?                  */
 
 static u8 *target_path;
 static u8  qemu_mode;
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index f647ff5d..a7be8e8b 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -76,7 +76,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
   fsrv->child_pid = -1;
   fsrv->map_size = MAP_SIZE;
   fsrv->use_fauxsrv = 0;
-  fsrv->prev_timed_out = 0;
+  fsrv->last_run_timed_out = 0;
 
   fsrv->init_child_func = fsrv_exec_child;
 
@@ -102,7 +102,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) {
   fsrv_to->out_dir_fd = -1;
   fsrv_to->child_pid = -1;
   fsrv_to->use_fauxsrv = 0;
-  fsrv_to->prev_timed_out = 0;
+  fsrv_to->last_run_timed_out = 0;
 
   fsrv_to->init_child_func = fsrv_exec_child;
 
@@ -217,7 +217,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
   if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed");
 
-  fsrv->child_timed_out = 0;
+  fsrv->last_run_timed_out = 0;
   fsrv->fsrv_pid = fork();
 
   if (fsrv->fsrv_pid < 0) PFATAL("fork() failed");
@@ -361,7 +361,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
     } else if (time > fsrv->exec_tmout * FORK_WAIT_MULT) {
 
-      fsrv->child_timed_out = 1;
+      fsrv->last_run_timed_out = 1;
       kill(fsrv->fsrv_pid, SIGKILL);
 
     } else {
@@ -476,7 +476,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
 
   }
 
-  if (fsrv->child_timed_out)
+  if (fsrv->last_run_timed_out)
     FATAL("Timeout while initializing fork server (adjusting -t may help)");
 
   if (waitpid(fsrv->fsrv_pid, &status, 0) <= 0) PFATAL("waitpid() failed");
@@ -640,6 +640,127 @@ static void afl_fsrv_kill(afl_forkserver_t *fsrv) {
 
 }
 
+/* Execute target application, monitoring for timeouts. Return status
+   information. The called program will update afl->fsrv->trace_bits. */
+
+fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, volatile u8 *stop_soon_p) {
+
+  s32 res;
+  u32 exec_ms;
+
+  int status = 0;
+
+  u32 timeout = fsrv->exec_tmout;
+
+  /* After this memset, fsrv->trace_bits[] are effectively volatile, so we
+     must prevent any earlier operations from venturing into that
+     territory. */
+
+  memset(fsrv->trace_bits, 0, fsrv->map_size);
+
+  MEM_BARRIER();
+
+  /* we have the fork server (or faux server) up and running
+  First, tell it if the previous run timed out. */
+
+  if ((res = write(fsrv->fsrv_ctl_fd, &fsrv->last_run_timed_out, 4)) != 4) {
+
+    if (*stop_soon_p) return 0;
+    RPFATAL(res, "Unable to request new process from fork server (OOM?)");
+
+  }
+
+  fsrv->last_run_timed_out = 0;
+
+  if ((res = read(fsrv->fsrv_st_fd, &fsrv->child_pid, 4)) != 4) {
+
+    if (stop_soon_p) return 0;
+    RPFATAL(res, "Unable to request new process from fork server (OOM?)");
+
+  }
+
+  if (fsrv->child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");
+
+  exec_ms = read_timed(fsrv->fsrv_st_fd, &status, 4, timeout, stop_soon_p);
+
+  if (exec_ms > timeout) {
+
+    /* If there was no response from forkserver after timeout seconds,
+    we kill the child. The forkserver should inform us afterwards */
+
+    kill(fsrv->child_pid, SIGKILL);
+    fsrv->last_run_timed_out = 1;
+    if (read(fsrv->fsrv_st_fd, &status, 4) < 4) exec_ms = 0;
+
+  }
+
+  if (!exec_ms) {
+
+    if (*stop_soon_p) return 0;
+    SAYF("\n" cLRD "[-] " cRST
+         "Unable to communicate with fork server. Some possible reasons:\n\n"
+         "    - You've run out of memory. Use -m to increase the the memory "
+         "limit\n"
+         "      to something higher than %lld.\n"
+         "    - The binary or one of the libraries it uses manages to "
+         "create\n"
+         "      threads before the forkserver initializes.\n"
+         "    - The binary, at least in some circumstances, exits in a way "
+         "that\n"
+         "      also kills the parent process - raise() could be the "
+         "culprit.\n"
+         "    - If using persistent mode with QEMU, "
+         "AFL_QEMU_PERSISTENT_ADDR "
+         "is\n"
+         "      probably not valid (hint: add the base address in case of "
+         "PIE)"
+         "\n\n"
+         "If all else fails you can disable the fork server via "
+         "AFL_NO_FORKSRV=1.\n",
+         fsrv->mem_limit);
+    RPFATAL(res, "Unable to communicate with fork server");
+
+  }
+
+  if (!WIFSTOPPED(status)) fsrv->child_pid = 0;
+
+  fsrv->total_execs++;
+
+  /* Any subsequent operations on fsrv->trace_bits must not be moved by the
+     compiler below this point. Past this location, fsrv->trace_bits[]
+     behave very normally and do not have to be treated as volatile. */
+
+  MEM_BARRIER();
+
+  /* Report outcome to caller. */
+
+  if (WIFSIGNALED(status) && !*stop_soon_p) {
+
+    fsrv->last_kill_signal = WTERMSIG(status);
+
+    if (fsrv->last_run_timed_out && fsrv->last_kill_signal == SIGKILL)
+      return FSRV_RUN_TMOUT;
+
+    return FSRV_RUN_CRASH;
+
+  }
+
+  /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and
+     must use a special exit code. */
+
+  if (fsrv->uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
+
+    fsrv->last_kill_signal = 0;
+    return FSRV_RUN_CRASH;
+
+  }
+
+  if ((*(u32 *)fsrv->trace_bits) == EXEC_FAIL_SIG) return FSRV_RUN_NOINST;
+
+  return FSRV_RUN_OK;
+
+}
+
 void afl_fsrv_killall() {
 
   LIST_FOREACH(&fsrv_list, afl_forkserver_t, {
@@ -656,4 +777,3 @@ void afl_fsrv_deinit(afl_forkserver_t *fsrv) {
   list_remove(&fsrv_list, fsrv);
 
 }
-
diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c
index a0a720fa..66b1e60d 100644
--- a/src/afl-fuzz-bitmap.c
+++ b/src/afl-fuzz-bitmap.c
@@ -598,7 +598,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
     res = calibrate_case(afl, afl->queue_top, mem, afl->queue_cycle - 1, 0);
 
-    if (unlikely(res == FAULT_ERROR))
+    if (unlikely(res == FSRV_RUN_ERROR))
       FATAL("Unable to execute target application");
 
     fd = open(queue_fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
@@ -612,7 +612,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
   switch (fault) {
 
-    case FAULT_TMOUT:
+    case FSRV_RUN_TMOUT:
 
       /* Timeouts are not very interesting, but we're still obliged to keep
          a handful of samples. We use the presence of new bits in the
@@ -651,9 +651,9 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
            timeout actually uncovers a crash. Make sure we don't discard it if
            so. */
 
-        if (!afl->stop_soon && new_fault == FAULT_CRASH) goto keep_as_crash;
+        if (!afl->stop_soon && new_fault == FSRV_RUN_CRASH) goto keep_as_crash;
 
-        if (afl->stop_soon || new_fault != FAULT_TMOUT) return keeping;
+        if (afl->stop_soon || new_fault != FSRV_RUN_TMOUT) return keeping;
 
       }
 
@@ -675,7 +675,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
       break;
 
-    case FAULT_CRASH:
+    case FSRV_RUN_CRASH:
 
     keep_as_crash:
 
@@ -704,7 +704,7 @@ u8 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->unique_crashes, afl->kill_signal, describe_op(afl, 0));
+               afl->unique_crashes, afl->fsrv.last_kill_signal, describe_op(afl, 0));
 
 #else
 
@@ -730,11 +730,11 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
       }
 
       afl->last_crash_time = get_cur_time();
-      afl->last_crash_execs = afl->total_execs;
+      afl->last_crash_execs = afl->fsrv.total_execs;
 
       break;
 
-    case FAULT_ERROR: FATAL("Unable to execute target application");
+    case FSRV_RUN_ERROR: FATAL("Unable to execute target application");
 
     default: return keeping;
 
diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c
index e2747097..ab93d838 100644
--- a/src/afl-fuzz-cmplog.c
+++ b/src/afl-fuzz-cmplog.c
@@ -66,7 +66,7 @@ u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
 
   if (afl->stop_soon) return 1;
 
-  if (fault == FAULT_TMOUT) {
+  if (fault == FSRV_RUN_TMOUT) {
 
     if (afl->subseq_tmouts++ > TMOUT_LIMIT) {
 
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index 10417da6..55f7ce53 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -493,13 +493,13 @@ void perform_dry_run(afl_state_t *afl) {
 
     if (afl->stop_soon) return;
 
-    if (res == afl->crash_mode || res == FAULT_NOBITS)
+    if (res == afl->crash_mode || res == FSRV_RUN_NOBITS)
       SAYF(cGRA "    len = %u, map size = %u, exec speed = %llu us\n" cRST,
            q->len, q->bitmap_size, q->exec_us);
 
     switch (res) {
 
-      case FAULT_NONE:
+      case FSRV_RUN_OK:
 
         if (q == afl->queue) check_map_coverage(afl);
 
@@ -507,7 +507,7 @@ void perform_dry_run(afl_state_t *afl) {
 
         break;
 
-      case FAULT_TMOUT:
+      case FSRV_RUN_TMOUT:
 
         if (afl->timeout_given) {
 
@@ -556,7 +556,7 @@ void perform_dry_run(afl_state_t *afl) {
 
         }
 
-      case FAULT_CRASH:
+      case FSRV_RUN_CRASH:
 
         if (afl->crash_mode) break;
 
@@ -650,13 +650,13 @@ void perform_dry_run(afl_state_t *afl) {
 
         FATAL("Test case '%s' results in a crash", fn);
 
-      case FAULT_ERROR:
+      case FSRV_RUN_ERROR:
 
         FATAL("Unable to execute target application ('%s')", afl->argv[0]);
 
-      case FAULT_NOINST: FATAL("No instrumentation detected");
+      case FSRV_RUN_NOINST: FATAL("No instrumentation detected");
 
-      case FAULT_NOBITS:
+      case FSRV_RUN_NOBITS:
 
         ++afl->useless_at_start;
 
diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c
index efb1c117..7bf23e84 100644
--- a/src/afl-fuzz-mutators.c
+++ b/src/afl-fuzz-mutators.c
@@ -242,7 +242,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
     fault = run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
     ++afl->trim_execs;
 
-    if (afl->stop_soon || fault == FAULT_ERROR) { goto abort_trimming; }
+    if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; }
 
     cksum = hash32(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
 
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index 961a29d6..cc97654a 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -442,14 +442,14 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
   if (unlikely(afl->queue_cur->cal_failed)) {
 
-    u8 res = FAULT_TMOUT;
+    u8 res = FSRV_RUN_TMOUT;
 
     if (afl->queue_cur->cal_failed < CAL_CHANCES) {
 
       res =
           calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1, 0);
 
-      if (unlikely(res == FAULT_ERROR))
+      if (unlikely(res == FSRV_RUN_ERROR))
         FATAL("Unable to execute target application");
 
     }
@@ -471,7 +471,7 @@ u8 fuzz_one_original(afl_state_t *afl) {
 
     u8 res = trim_case(afl, afl->queue_cur, in_buf);
 
-    if (unlikely(res == FAULT_ERROR))
+    if (unlikely(res == FSRV_RUN_ERROR))
       FATAL("Unable to execute target application");
 
     if (unlikely(afl->stop_soon)) {
@@ -2469,14 +2469,14 @@ u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) {
 
   if (afl->queue_cur->cal_failed) {
 
-    u8 res = FAULT_TMOUT;
+    u8 res = FSRV_RUN_TMOUT;
 
     if (afl->queue_cur->cal_failed < CAL_CHANCES) {
 
       res =
           calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1, 0);
 
-      if (res == FAULT_ERROR) FATAL("Unable to execute target application");
+      if (res == FSRV_RUN_ERROR) FATAL("Unable to execute target application");
 
     }
 
@@ -2497,7 +2497,7 @@ u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) {
 
     u8 res = trim_case(afl, afl->queue_cur, in_buf);
 
-    if (res == FAULT_ERROR) FATAL("Unable to execute target application");
+    if (res == FSRV_RUN_ERROR) FATAL("Unable to execute target application");
 
     if (afl->stop_soon) {
 
diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c
index 5eb110d0..d05eee08 100644
--- a/src/afl-fuzz-queue.c
+++ b/src/afl-fuzz-queue.c
@@ -492,7 +492,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) {
       // the more often fuzz result paths are equal to this queue entry,
       // reduce its value
       perf_score *=
-          (1 - (double)((double)q->n_fuzz / (double)afl->total_execs));
+          (1 - (double)((double)q->n_fuzz / (double)afl->fsrv.total_execs));
 
       break;
 
diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c
index 6a01ec89..8cea01e8 100644
--- a/src/afl-fuzz-redqueen.c
+++ b/src/afl-fuzz-redqueen.c
@@ -622,7 +622,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len,
   if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) return 1;
 
   u64 orig_hit_cnt, new_hit_cnt;
-  u64 orig_execs = afl->total_execs;
+  u64 orig_execs = afl->fsrv.total_execs;
   orig_hit_cnt = afl->queued_paths + afl->unique_crashes;
 
   afl->stage_name = "input-to-state";
@@ -670,7 +670,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len,
 exit_its:
   new_hit_cnt = afl->queued_paths + afl->unique_crashes;
   afl->stage_finds[STAGE_ITS] += new_hit_cnt - orig_hit_cnt;
-  afl->stage_cycles[STAGE_ITS] += afl->total_execs - orig_execs;
+  afl->stage_cycles[STAGE_ITS] += afl->fsrv.total_execs - orig_execs;
 
   memcpy(orig_buf, buf, len);
 
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index 514ba9ef..b20c5436 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -32,95 +32,9 @@
 /* Execute target application, monitoring for timeouts. Return status
    information. The called program will update afl->fsrv->trace_bits. */
 
-u8 run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) {
+fsrv_run_result_t run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) {
 
-  s32 res;
-  u32 exec_ms;
-
-  int status = 0;
-  u32 tb4;
-
-  fsrv->child_timed_out = 0;
-
-  /* After this memset, fsrv->trace_bits[] are effectively volatile, so we
-     must prevent any earlier operations from venturing into that
-     territory. */
-
-  memset(fsrv->trace_bits, 0, fsrv->map_size);
-
-  MEM_BARRIER();
-
-  /* we have the fork server (or faux server) up and running, so simply
-      tell it to have at it, and then read back PID. */
-
-  if ((res = write(fsrv->fsrv_ctl_fd, &fsrv->prev_timed_out, 4)) != 4) {
-
-    if (afl->stop_soon) return 0;
-    RPFATAL(res, "Unable to request new process from fork server (OOM?)");
-
-  }
-
-  if ((res = read(fsrv->fsrv_st_fd, &fsrv->child_pid, 4)) != 4) {
-
-    if (afl->stop_soon) return 0;
-    RPFATAL(res, "Unable to request new process from fork server (OOM?)");
-
-  }
-
-  if (fsrv->child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");
-
-  exec_ms = read_timed(fsrv->fsrv_st_fd, &status, 4, timeout, &afl->stop_soon);
-
-  if (exec_ms > timeout) {
-
-    /* If there was no response from forkserver after timeout seconds,
-    we kill the child. The forkserver should inform us afterwards */
-
-    kill(fsrv->child_pid, SIGKILL);
-    fsrv->child_timed_out = 1;
-    if (read(fsrv->fsrv_st_fd, &status, 4) < 4) exec_ms = 0;
-
-  }
-
-  if (!exec_ms) {
-
-    if (afl->stop_soon) return 0;
-    SAYF("\n" cLRD "[-] " cRST
-         "Unable to communicate with fork server. Some possible reasons:\n\n"
-         "    - You've run out of memory. Use -m to increase the the memory "
-         "limit\n"
-         "      to something higher than %lld.\n"
-         "    - The binary or one of the libraries it uses manages to "
-         "create\n"
-         "      threads before the forkserver initializes.\n"
-         "    - The binary, at least in some circumstances, exits in a way "
-         "that\n"
-         "      also kills the parent process - raise() could be the "
-         "culprit.\n"
-         "    - If using persistent mode with QEMU, "
-         "AFL_QEMU_PERSISTENT_ADDR "
-         "is\n"
-         "      probably not valid (hint: add the base address in case of "
-         "PIE)"
-         "\n\n"
-         "If all else fails you can disable the fork server via "
-         "AFL_NO_FORKSRV=1.\n",
-         fsrv->mem_limit);
-    RPFATAL(res, "Unable to communicate with fork server");
-
-  }
-
-  if (!WIFSTOPPED(status)) fsrv->child_pid = 0;
-
-  ++afl->total_execs;
-
-  /* Any subsequent operations on fsrv->trace_bits must not be moved by the
-     compiler below this point. Past this location, fsrv->trace_bits[]
-     behave very normally and do not have to be treated as volatile. */
-
-  MEM_BARRIER();
-
-  tb4 = *(u32 *)fsrv->trace_bits;
+  fsrv_run_result_t res = afl_fsrv_run_target(&afl->fsrv, &afl->stop_soon);
 
 #ifdef WORD_SIZE_64
   classify_counts(afl, (u64 *)fsrv->trace_bits);
@@ -128,35 +42,7 @@ u8 run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) {
   classify_counts(afl, (u32 *)fsrv->trace_bits);
 #endif                                                     /* ^WORD_SIZE_64 */
 
-  fsrv->prev_timed_out = fsrv->child_timed_out;
-
-  /* Report outcome to caller. */
-
-  if (WIFSIGNALED(status) && !afl->stop_soon) {
-
-    afl->kill_signal = WTERMSIG(status);
-
-    if (fsrv->child_timed_out && afl->kill_signal == SIGKILL)
-      return FAULT_TMOUT;
-
-    return FAULT_CRASH;
-
-  }
-
-  /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and
-     must use a special exit code. */
-
-  if (fsrv->uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
-
-    afl->kill_signal = 0;
-    return FAULT_CRASH;
-
-  }
-
-  if ((afl->dumb_mode == 1 || afl->no_forkserver) && tb4 == EXEC_FAIL_SIG)
-    return FAULT_ERROR;
-
-  return FAULT_NONE;
+  return res;
 
 }
 
@@ -348,7 +234,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
     if (!afl->dumb_mode && !afl->stage_cur &&
         !count_bytes(afl, afl->fsrv.trace_bits)) {
 
-      fault = FAULT_NOINST;
+      fault = FSRV_RUN_NOINST;
       goto abort_calibration;
 
     }
@@ -408,7 +294,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
      parent. This is a non-critical problem, but something to warn the user
      about. */
 
-  if (!afl->dumb_mode && first_run && !fault && !new_bits) fault = FAULT_NOBITS;
+  if (!afl->dumb_mode && first_run && !fault && !new_bits) fault = FSRV_RUN_NOBITS;
 
 abort_calibration:
 
@@ -645,7 +531,7 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
       fault = run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
       ++afl->trim_execs;
 
-      if (afl->stop_soon || fault == FAULT_ERROR) goto abort_trimming;
+      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?
        */
@@ -753,7 +639,7 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
 
   if (afl->stop_soon) return 1;
 
-  if (fault == FAULT_TMOUT) {
+  if (fault == FSRV_RUN_TMOUT) {
 
     if (afl->subseq_tmouts++ > TMOUT_LIMIT) {
 
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c
index d48dd5e3..52148dc2 100644
--- a/src/afl-fuzz-stats.c
+++ b/src/afl-fuzz-stats.c
@@ -108,14 +108,14 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability,
       afl->start_time / 1000, cur_time / 1000,
       (cur_time - afl->start_time) / 1000, getpid(),
       afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds,
-      afl->total_execs,
-      afl->total_execs / ((double)(get_cur_time() - afl->start_time) / 1000),
+      afl->fsrv.total_execs,
+      afl->fsrv.total_execs / ((double)(get_cur_time() - afl->start_time) / 1000),
       afl->queued_paths, afl->queued_favored, afl->queued_discovered,
       afl->queued_imported, afl->max_depth, afl->current_entry,
       afl->pending_favored, afl->pending_not_fuzzed, afl->queued_variable,
       stability, bitmap_cvg, afl->unique_crashes, afl->unique_hangs,
       afl->last_path_time / 1000, afl->last_crash_time / 1000,
-      afl->last_hang_time / 1000, afl->total_execs - afl->last_crash_execs,
+      afl->last_hang_time / 1000, afl->fsrv.total_execs - afl->last_crash_execs,
       afl->fsrv.exec_tmout, afl->slowest_exec_ms,
 #ifdef __APPLE__
       (unsigned long int)(rus.ru_maxrss >> 20),
@@ -227,7 +227,7 @@ void show_stats(afl_state_t *afl) {
 
   if (afl->most_execs_key == 1) {
 
-    if (afl->most_execs <= afl->total_execs) {
+    if (afl->most_execs <= afl->fsrv.total_execs) {
 
       afl->most_execs_key = 2;
       afl->stop_soon = 2;
@@ -251,11 +251,11 @@ void show_stats(afl_state_t *afl) {
   if (!afl->stats_last_execs) {
 
     afl->stats_avg_exec =
-        ((double)afl->total_execs) * 1000 / (cur_ms - afl->start_time);
+        ((double)afl->fsrv.total_execs) * 1000 / (cur_ms - afl->start_time);
 
   } else {
 
-    double cur_avg = ((double)(afl->total_execs - afl->stats_last_execs)) *
+    double cur_avg = ((double)(afl->fsrv.total_execs - afl->stats_last_execs)) *
                      1000 / (cur_ms - afl->stats_last_ms);
 
     /* If there is a dramatic (5x+) jump in speed, reset the indicator
@@ -270,7 +270,7 @@ void show_stats(afl_state_t *afl) {
   }
 
   afl->stats_last_ms = cur_ms;
-  afl->stats_last_execs = afl->total_execs;
+  afl->stats_last_execs = afl->fsrv.total_execs;
 
   /* Tell the callers when to contact us (as measured in execs). */
 
@@ -543,14 +543,14 @@ void show_stats(afl_state_t *afl) {
 
     SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP
                   "   new crashes : %s%-22s" bSTG         bV "\n",
-         u_stringify_int(IB(0), afl->total_execs),
+         u_stringify_int(IB(0), afl->fsrv.total_execs),
          afl->unique_crashes ? cLRD : cRST, tmp);
 
   } else {
 
     SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP
                   " total crashes : %s%-22s" bSTG         bV "\n",
-         u_stringify_int(IB(0), afl->total_execs),
+         u_stringify_int(IB(0), afl->fsrv.total_execs),
          afl->unique_crashes ? cLRD : cRST, tmp);
 
   }
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 07067691..9f17b61b 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -480,7 +480,7 @@ int main(int argc, char **argv_orig, char **envp) {
       case 'C':                                               /* crash mode */
 
         if (afl->crash_mode) FATAL("Multiple -C options not supported");
-        afl->crash_mode = FAULT_CRASH;
+        afl->crash_mode = FSRV_RUN_CRASH;
         break;
 
       case 'n':                                                /* dumb mode */
diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c
index eea1cc95..16d6fe41 100644
--- a/src/afl-sharedmem.c
+++ b/src/afl-sharedmem.c
@@ -40,7 +40,6 @@
 
 #include <stdio.h>
 #include <unistd.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index 3fcc1d2b..5f622c25 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -69,7 +69,7 @@ static u8 *in_data;                    /* Input data                        */
 static u32 total, highest;             /* tuple content information         */
 
 static u32 in_len,                     /* Input data length                 */
-    arg_offset, total_execs;           /* Total number of execs             */
+    arg_offset;                        /* Total number of execs             */
 
 static u8 quiet_mode,                  /* Hide non-essential messages?      */
     edges_only,                        /* Ignore hit counts?                */
@@ -193,7 +193,7 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) {
 
       if (cmin_mode) {
 
-        if (fsrv->child_timed_out) break;
+        if (fsrv->last_run_timed_out) break;
         if (!caa && child_crashed != cco) break;
 
         fprintf(f, "%u%u\n", fsrv->trace_bits[i], i);
@@ -233,75 +233,18 @@ static void write_to_testcase(afl_forkserver_t *fsrv, void *mem, u32 len) {
 
 }
 
-/* Execute target application. Returns 0 if the changes are a dud, or
-   1 if they should be kept. */
+/* Execute target application. */
 
-static u8 run_target_forkserver(afl_forkserver_t *fsrv, char **argv, u8 *mem,
+void run_target_forkserver(afl_forkserver_t *fsrv, char **argv, u8 *mem,
                                 u32 len) {
 
-  struct itimerval it;
-  int              status = 0;
-
-  memset(fsrv->trace_bits, 0, MAP_SIZE);
-  MEM_BARRIER();
-
   write_to_testcase(fsrv, mem, len);
 
-  s32 res;
-
-  /* we have the fork server up and running, so simply
-     tell it to have at it, and then read back PID. */
-
-  if ((res = write(fsrv->fsrv_ctl_fd, &fsrv->prev_timed_out, 4)) != 4) {
-
-    if (stop_soon) return 0;
-    RPFATAL(res, "Unable to request new process from fork server (OOM?)");
-
-  }
-
-  if ((res = read(fsrv->fsrv_st_fd, &fsrv->child_pid, 4)) != 4) {
-
-    if (stop_soon) return 0;
-    RPFATAL(res, "Unable to request new process from fork server (OOM?)");
-
-  }
-
-  if (fsrv->child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");
-
-  /* Configure timeout, wait for child, cancel timeout. */
-
-  if (fsrv->exec_tmout) {
-
-    it.it_value.tv_sec = (fsrv->exec_tmout / 1000);
-    it.it_value.tv_usec = (fsrv->exec_tmout % 1000) * 1000;
-
-  }
-
-  setitimer(ITIMER_REAL, &it, NULL);
-
-  if ((res = read(fsrv->fsrv_st_fd, &status, 4)) != 4) {
-
-    if (stop_soon) return 0;
-    RPFATAL(res, "Unable to communicate with fork server (OOM?)");
-
-  }
-
-  fsrv->child_pid = 0;
-  it.it_value.tv_sec = 0;
-  it.it_value.tv_usec = 0;
-
-  setitimer(ITIMER_REAL, &it, NULL);
-
-  MEM_BARRIER();
-
-  /* Clean up bitmap, analyze exit condition, etc. */
-
-  if (*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG)
-    FATAL("Unable to execute '%s'", argv[0]);
+  fsrv_run_result_t res = afl_fsrv_run_target(fsrv, &stop_soon);
+  if (res == FSRV_RUN_NOINST || res == FSRV_RUN_ERROR) FATAL("Error running target");
 
   classify_counts(fsrv->trace_bits,
                   binary_mode ? count_class_binary : count_class_human);
-  total_execs++;
 
   if (stop_soon) {
 
@@ -310,22 +253,6 @@ static u8 run_target_forkserver(afl_forkserver_t *fsrv, char **argv, u8 *mem,
 
   }
 
-  /* Always discard inputs that time out. */
-
-  if (fsrv->child_timed_out) { return 0; }
-
-  /* Handle crashing inputs depending on current mode. */
-
-  if (WIFSIGNALED(status) ||
-      (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) ||
-      (WIFEXITED(status) && WEXITSTATUS(status))) {
-
-    return 0;
-
-  }
-
-  return 0;
-
 }
 
 /* Read initial file. */
@@ -425,7 +352,7 @@ static void run_target(afl_forkserver_t *fsrv, char **argv) {
 
   if (fsrv->exec_tmout) {
 
-    fsrv->child_timed_out = 0;
+    fsrv->last_run_timed_out = 0;
     it.it_value.tv_sec = (fsrv->exec_tmout / 1000);
     it.it_value.tv_usec = (fsrv->exec_tmout % 1000) * 1000;
 
@@ -452,12 +379,12 @@ static void run_target(afl_forkserver_t *fsrv, char **argv) {
 
   if (!quiet_mode) SAYF(cRST "-- Program output ends --\n");
 
-  if (!fsrv->child_timed_out && !stop_soon && WIFSIGNALED(status))
+  if (!fsrv->last_run_timed_out && !stop_soon && WIFSIGNALED(status))
     child_crashed = 1;
 
   if (!quiet_mode) {
 
-    if (fsrv->child_timed_out)
+    if (fsrv->last_run_timed_out)
       SAYF(cLRD "\n+++ Program timed off +++\n" cRST);
     else if (stop_soon)
       SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST);
@@ -980,7 +907,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
     }
 
-    if (!quiet_mode) OKF("Processed %u input files.", total_execs);
+    if (!quiet_mode) OKF("Processed %llu input files.", fsrv->total_execs);
 
     closedir(dir_in);
     if (dir_out) closedir(dir_out);
@@ -1010,7 +937,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   afl_shm_deinit(&shm);
 
-  u32 ret = child_crashed * 2 + fsrv->child_timed_out;
+  u32 ret = child_crashed * 2 + fsrv->last_run_timed_out;
 
   if (fsrv->target_path) ck_free(fsrv->target_path);
 
diff --git a/src/afl-tmin.c b/src/afl-tmin.c
index 31fad1df..999d5f65 100644
--- a/src/afl-tmin.c
+++ b/src/afl-tmin.c
@@ -67,7 +67,6 @@ static u8 *in_data;                    /* Input data for trimming           */
 
 static u32 in_len,                     /* Input data length                 */
     orig_cksum,                        /* Original checksum                 */
-    total_execs,                       /* Total number of execs             */
     missed_hangs,                      /* Misses due to hangs               */
     missed_crashes,                    /* Misses due to crashes             */
     missed_paths;                      /* Misses due to exec path diffs     */
@@ -249,69 +248,11 @@ static void write_to_testcase(afl_forkserver_t *fsrv, void *mem, u32 len) {
 static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len,
                      u8 first_run) {
 
-  struct itimerval it;
-  int              status = 0;
-
-  u32 cksum;
-
-  fsrv->child_timed_out = 0;
-
-  memset(fsrv->trace_bits, 0, fsrv->map_size);
-  MEM_BARRIER();
-
   write_to_testcase(fsrv, mem, len);
 
-  s32 res;
-
-  /* we have the fork server up and running, so simply
-     tell it to have at it, and then read back PID. */
-
-  if ((res = write(fsrv->fsrv_ctl_fd, &fsrv->prev_timed_out, 4)) != 4) {
-
-    if (stop_soon) return 0;
-    RPFATAL(res, "Unable to request new process from fork server (OOM?)");
-
-  }
-
-  if ((res = read(fsrv->fsrv_st_fd, &fsrv->child_pid, 4)) != 4) {
-
-    if (stop_soon) return 0;
-    RPFATAL(res, "Unable to request new process from fork server (OOM?)");
-
-  }
-
-  if (fsrv->child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");
-
-  /* Configure timeout, wait for child, cancel timeout. */
-
-  if (fsrv->exec_tmout) {
-
-    it.it_value.tv_sec = (fsrv->exec_tmout / 1000);
-    it.it_value.tv_usec = (fsrv->exec_tmout % 1000) * 1000;
-
-  }
-
-  setitimer(ITIMER_REAL, &it, NULL);
+  fsrv_run_result_t ret = afl_fsrv_run_target(fsrv, &stop_soon);
 
-  if ((res = read(fsrv->fsrv_st_fd, &status, 4)) != 4) {
-
-    if (stop_soon) return 0;
-    RPFATAL(res, "Unable to communicate with fork server (OOM?)");
-
-  }
-
-  fsrv->child_pid = 0;
-  it.it_value.tv_sec = 0;
-  it.it_value.tv_usec = 0;
-
-  setitimer(ITIMER_REAL, &it, NULL);
-
-  MEM_BARRIER();
-
-  /* Clean up bitmap, analyze exit condition, etc. */
-
-  if (*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG)
-    FATAL("Unable to execute '%s'", argv[0]);
+  if (ret == FSRV_RUN_ERROR) FATAL("Couldn't run child");
 
   if (!hang_mode) {
 
@@ -320,8 +261,6 @@ static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len,
 
   }
 
-  total_execs++;
-
   if (stop_soon) {
 
     SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST);
@@ -334,25 +273,21 @@ static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len,
 
   if (hang_mode) {
 
-    if (fsrv->child_timed_out) return 1;
-
-    if (WIFSIGNALED(status) ||
-        (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) ||
-        (WIFEXITED(status) && WEXITSTATUS(status) && exit_crash)) {
-
+    switch (ret)
+    {
+    case FSRV_RUN_TMOUT:
+      return 1;
+    case FSRV_RUN_CRASH:
       missed_crashes++;
-
-    } else {
-
+      return 0;
+    default:
       missed_hangs++;
-
+      return 0;
     }
 
-    return 0;
-
   }
 
-  if (fsrv->child_timed_out) {
+  if (ret == FSRV_RUN_TMOUT) {
 
     missed_hangs++;
     return 0;
@@ -361,9 +296,7 @@ static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len,
 
   /* Handle crashing inputs depending on current mode. */
 
-  if (WIFSIGNALED(status) ||
-      (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) ||
-      (WIFEXITED(status) && WEXITSTATUS(status) && exit_crash)) {
+  if (ret == FSRV_RUN_CRASH) {
 
     if (first_run) crash_mode = 1;
 
@@ -391,7 +324,9 @@ static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len,
 
   }
 
-  cksum = hash32(fsrv->trace_bits, fsrv->map_size, HASH_CONST);
+  if (ret == FSRV_RUN_NOINST) FATAL("Binary not instrumented?");
+
+  u32 cksum = hash32(fsrv->trace_bits, fsrv->map_size, HASH_CONST);
 
   if (first_run) orig_cksum = cksum;
 
@@ -640,11 +575,11 @@ finalize_all:
 
     SAYF("\n" cGRA "     File size reduced by : " cRST
          "%0.02f%% (to %u byte%s)\n" cGRA "    Characters simplified : " cRST
-         "%0.02f%%\n" cGRA "     Number of execs done : " cRST "%u\n" cGRA
+         "%0.02f%%\n" cGRA "     Number of execs done : " cRST "%llu\n" cGRA
          "          Fruitless execs : " cRST "termination=%u crash=%u\n\n",
          100 - ((double)in_len) * 100 / orig_len, in_len,
          in_len == 1 ? "" : "s",
-         ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), total_execs,
+         ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), fsrv->total_execs,
          missed_paths, missed_crashes);
     return;
 
@@ -652,13 +587,13 @@ finalize_all:
 
   SAYF("\n" cGRA "     File size reduced by : " cRST
        "%0.02f%% (to %u byte%s)\n" cGRA "    Characters simplified : " cRST
-       "%0.02f%%\n" cGRA "     Number of execs done : " cRST "%u\n" cGRA
+       "%0.02f%%\n" cGRA "     Number of execs done : " cRST "%llu\n" cGRA
        "          Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n",
        100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s",
-       ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), total_execs,
+       ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), fsrv->total_execs,
        missed_paths, missed_crashes, missed_hangs ? cLRD : "", missed_hangs);
 
-  if (total_execs > 50 && missed_hangs * 10 > total_execs && !hang_mode)
+  if (fsrv->total_execs > 50 && missed_hangs * 10 > fsrv->total_execs && !hang_mode)
     WARNF(cLRD "Frequent timeouts - results may be skewed." cRST);
 
 }
@@ -1139,13 +1074,13 @@ int main(int argc, char **argv_orig, char **envp) {
 
   run_target(fsrv, use_argv, in_data, in_len, 1);
 
-  if (hang_mode && !fsrv->child_timed_out)
+  if (hang_mode && !fsrv->last_run_timed_out)
     FATAL(
         "Target binary did not time out but hang minimization mode "
         "(-H) was set (-t %u).",
         fsrv->exec_tmout);
 
-  if (fsrv->child_timed_out && !hang_mode)
+  if (fsrv->last_run_timed_out && !hang_mode)
     FATAL(
         "Target binary times out (adjusting -t may help). Use -H to minimize a "
         "hang.");