about summary refs log tree commit diff
path: root/src/afl-fuzz-run.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/afl-fuzz-run.c')
-rw-r--r--src/afl-fuzz-run.c202
1 files changed, 100 insertions, 102 deletions
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index cdec75e8..1ddd7e1a 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -27,78 +27,62 @@
 #include <sys/time.h>
 #include <signal.h>
 
-/* Execute target application, monitoring for timeouts. Return status
-   information. The called program will update afl->fsrv.trace_bits[]. */
-
-void timeout_handle(union sigval timer_data) {
+#include "cmplog.h"
 
-  pid_t child_pid = timer_data.sival_int;
-  if (child_pid > 0) kill(child_pid, SIGKILL);
-
-}
+/* 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, u32 timeout) {
+u8 run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) {
 
   s32 res;
-  int sret;
-
-  fd_set readfds;
-
-  static struct timeval it;
-  static u32            prev_timed_out = 0;
+  u32 exec_ms;
 
   int status = 0;
   u32 tb4;
 
-  afl->fsrv.child_timed_out = 0;
+  fsrv->child_timed_out = 0;
 
-  /* After this memset, afl->fsrv.trace_bits[] are effectively volatile, so we
+  /* After this memset, fsrv->trace_bits[] are effectively volatile, so we
      must prevent any earlier operations from venturing into that
      territory. */
 
-  memset(afl->fsrv.trace_bits, 0, MAP_SIZE);
+  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(afl->fsrv.fsrv_ctl_fd, &prev_timed_out, 4)) != 4) {
+  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(afl->fsrv.fsrv_st_fd, &afl->fsrv.child_pid, 4)) != 4) {
+  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 (afl->fsrv.child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");
+  if (fsrv->child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");
 
-  /* use select to monitor the forkserver for timeouts. */
+  exec_ms = read_timed(fsrv->fsrv_st_fd, &status, 4, timeout, &afl->stop_soon);
 
-  FD_ZERO(&readfds);
-  FD_SET(afl->fsrv.fsrv_st_fd, &readfds);
-  it.tv_sec = ((timeout) / 1000);
-  it.tv_usec = ((timeout) % 1000) * 1000;
-
-  sret = select(afl->fsrv.fsrv_st_fd + 1, &readfds, NULL, NULL, &it);
-
-  if (sret == 0) {
+  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(afl->fsrv.child_pid, SIGKILL);
-    afl->fsrv.child_timed_out = 1;
+    kill(fsrv->child_pid, SIGKILL);
+    fsrv->child_timed_out = 1;
+    if (read(fsrv->fsrv_st_fd, &status, 4) < 4) exec_ms = 0;
 
   }
 
-  if ((res = read(afl->fsrv.fsrv_st_fd, &status, 4)) != 4) {
+  if (!exec_ms) {
 
     if (afl->stop_soon) return 0;
     SAYF("\n" cLRD "[-] " cRST
@@ -121,30 +105,30 @@ u8 run_target(afl_state_t *afl, u32 timeout) {
          "\n\n"
          "If all else fails you can disable the fork server via "
          "AFL_NO_FORKSRV=1.\n",
-         afl->fsrv.mem_limit);
+         fsrv->mem_limit);
     RPFATAL(res, "Unable to communicate with fork server");
 
   }
 
-  if (!WIFSTOPPED(status)) afl->fsrv.child_pid = 0;
+  if (!WIFSTOPPED(status)) fsrv->child_pid = 0;
 
   ++afl->total_execs;
 
-  /* Any subsequent operations on afl->fsrv.trace_bits must not be moved by the
-     compiler below this point. Past this location, afl->fsrv.trace_bits[]
+  /* 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 *)afl->fsrv.trace_bits;
+  tb4 = *(u32 *)fsrv->trace_bits;
 
 #ifdef WORD_SIZE_64
-  classify_counts((u64 *)afl->fsrv.trace_bits);
+  classify_counts(afl, (u64 *)fsrv->trace_bits);
 #else
-  classify_counts((u32 *)afl->fsrv.trace_bits);
+  classify_counts(afl, (u32 *)fsrv->trace_bits);
 #endif                                                     /* ^WORD_SIZE_64 */
 
-  prev_timed_out = afl->fsrv.child_timed_out;
+  fsrv->prev_timed_out = fsrv->child_timed_out;
 
   /* Report outcome to caller. */
 
@@ -152,7 +136,7 @@ u8 run_target(afl_state_t *afl, u32 timeout) {
 
     afl->kill_signal = WTERMSIG(status);
 
-    if (afl->fsrv.child_timed_out && afl->kill_signal == SIGKILL)
+    if (fsrv->child_timed_out && afl->kill_signal == SIGKILL)
       return FAULT_TMOUT;
 
     return FAULT_CRASH;
@@ -162,7 +146,7 @@ u8 run_target(afl_state_t *afl, u32 timeout) {
   /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and
      must use a special exit code. */
 
-  if (afl->fsrv.uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
+  if (fsrv->uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
 
     afl->kill_signal = 0;
     return FAULT_CRASH;
@@ -185,20 +169,16 @@ void write_to_testcase(afl_state_t *afl, void *mem, u32 len) {
   s32 fd = afl->fsrv.out_fd;
 
 #ifdef _AFL_DOCUMENT_MUTATIONS
-  s32   doc_fd;
-  char *fn = alloc_printf("%s/mutations/%09u:%s", afl->out_dir,
+  s32  doc_fd;
+  char fn[PATH_MAX];
+  snprintf(fn, PATH_MAX, ("%s/mutations/%09u:%s", afl->out_dir,
                           afl->document_counter++, describe_op(afl, 0));
-  if (fn != NULL) {
-
-    if ((doc_fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0) {
-
-      if (write(doc_fd, mem, len) != len)
-        PFATAL("write to mutation file failed: %s", fn);
-      close(doc_fd);
 
-    }
+  if ((doc_fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0) {
 
-    ck_free(fn);
+    if (write(doc_fd, mem, len) != len)
+      PFATAL("write to mutation file failed: %s", fn);
+    close(doc_fd);
 
   }
 
@@ -223,16 +203,22 @@ void write_to_testcase(afl_state_t *afl, void *mem, u32 len) {
 
     lseek(fd, 0, SEEK_SET);
 
-  if (afl->mutator && afl->mutator->afl_custom_pre_save) {
+  if (unlikely(afl->mutator && afl->mutator->afl_custom_pre_save)) {
+
+    u8 *new_buf = NULL;
+
+    size_t new_size = afl->mutator->afl_custom_pre_save(afl->mutator->data, mem,
+                                                        len, &new_buf);
+
+    if (unlikely(!new_buf))
+      FATAL("Custom_pre_save failed (ret: %lu)", (long unsigned)new_size);
 
-    u8 *   new_data;
-    size_t new_size =
-        afl->mutator->afl_custom_pre_save(afl, mem, len, &new_data);
-    ck_write(fd, new_data, new_size, afl->fsrv.out_file);
-    ck_free(new_data);
+    /* everything as planned. use the new data. */
+    ck_write(fd, new_buf, new_size, afl->fsrv.out_file);
 
   } else {
 
+    /* boring uncustom. */
     ck_write(fd, mem, len, afl->fsrv.out_file);
 
   }
@@ -299,8 +285,6 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at,
 u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
                   u32 handicap, u8 from_queue) {
 
-  static u8 first_trace[MAP_SIZE];
-
   u8 fault = 0, new_bits = 0, var_detected = 0,
      first_run = (q->exec_cksum == 0);
 
@@ -326,12 +310,22 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
   /* Make sure the forkserver is up before we do anything, and let's not
      count its spin-up time toward binary calibration. */
 
-  if (!afl->fsrv.fsrv_pid) afl_fsrv_start(&afl->fsrv, afl->argv);
-  if (afl->dumb_mode != 1 && !afl->no_forkserver && !afl->cmplog_fsrv_pid &&
-      afl->shm.cmplog_mode)
-    init_cmplog_forkserver(afl);
+  if (!afl->fsrv.fsrv_pid) {
+
+    if (afl->fsrv.cmplog_binary &&
+        afl->fsrv.init_child_func != cmplog_exec_child) {
+
+      FATAL("BUG in afl-fuzz detected. Cmplog mode not set correctly.");
+
+    }
+
+    afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
+                   afl->afl_env.afl_debug_child_output);
+
+  }
 
-  if (q->exec_cksum) memcpy(first_trace, afl->fsrv.trace_bits, MAP_SIZE);
+  if (q->exec_cksum)
+    memcpy(afl->first_trace, afl->fsrv.trace_bits, afl->fsrv.map_size);
 
   start_us = get_cur_time_us();
 
@@ -344,7 +338,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
 
     write_to_testcase(afl, use_mem, q->len);
 
-    fault = run_target(afl, use_tmout);
+    fault = run_target(afl, &afl->fsrv, use_tmout);
 
     /* afl->stop_soon is set by the handler for Ctrl+C. When it's pressed,
        we want to bail out quickly. */
@@ -352,14 +346,14 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
     if (afl->stop_soon || fault != afl->crash_mode) goto abort_calibration;
 
     if (!afl->dumb_mode && !afl->stage_cur &&
-        !count_bytes(afl->fsrv.trace_bits)) {
+        !count_bytes(afl, afl->fsrv.trace_bits)) {
 
       fault = FAULT_NOINST;
       goto abort_calibration;
 
     }
 
-    cksum = hash32(afl->fsrv.trace_bits, MAP_SIZE, HASH_CONST);
+    cksum = hash32(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
 
     if (q->exec_cksum != cksum) {
 
@@ -370,23 +364,21 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
 
         u32 i;
 
-        for (i = 0; i < MAP_SIZE; ++i) {
-
-          if (!afl->var_bytes[i] && first_trace[i] != afl->fsrv.trace_bits[i]) {
+        for (i = 0; i < afl->fsrv.map_size; ++i) {
 
+          if (unlikely(!afl->var_bytes[i]) &&
+              unlikely(afl->first_trace[i] != afl->fsrv.trace_bits[i]))
             afl->var_bytes[i] = 1;
-            afl->stage_max = CAL_CYCLES_LONG;
-
-          }
 
         }
 
         var_detected = 1;
+        afl->stage_max = CAL_CYCLES_LONG;
 
       } else {
 
         q->exec_cksum = cksum;
-        memcpy(first_trace, afl->fsrv.trace_bits, MAP_SIZE);
+        memcpy(afl->first_trace, afl->fsrv.trace_bits, afl->fsrv.map_size);
 
       }
 
@@ -403,7 +395,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
      This is used for fuzzing air time calculations in calculate_score(). */
 
   q->exec_us = (stop_us - start_us) / afl->stage_max;
-  q->bitmap_size = count_bytes(afl->fsrv.trace_bits);
+  q->bitmap_size = count_bytes(afl, afl->fsrv.trace_bits);
   q->handicap = handicap;
   q->cal_failed = 0;
 
@@ -431,7 +423,7 @@ abort_calibration:
 
   if (var_detected) {
 
-    afl->var_byte_count = count_bytes(afl->var_bytes);
+    afl->var_byte_count = count_bytes(afl, afl->var_bytes);
 
     if (!q->var_behavior) {
 
@@ -471,8 +463,6 @@ void sync_fuzzers(afl_state_t *afl) {
 
   while ((sd_ent = readdir(sd))) {
 
-    static u8 stage_tmp[128];
-
     DIR *          qd;
     struct dirent *qd_ent;
     u8 *           qd_path, *qd_synced_path;
@@ -511,13 +501,14 @@ void sync_fuzzers(afl_state_t *afl) {
 
     /* Show stats */
 
-    sprintf(stage_tmp, "sync %u", ++sync_cnt);
-    afl->stage_name = stage_tmp;
+    snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "sync %u", ++sync_cnt);
+
+    afl->stage_name = afl->stage_name_buf;
     afl->stage_cur = 0;
     afl->stage_max = 0;
 
-    /* For every file queued by this fuzzer, parse ID and see if we have looked
-       at it before; exec a test case if not. */
+    /* For every file queued by this fuzzer, parse ID and see if we have
+       looked at it before; exec a test case if not. */
 
     while ((qd_ent = readdir(qd))) {
 
@@ -564,7 +555,7 @@ void sync_fuzzers(afl_state_t *afl) {
 
         write_to_testcase(afl, mem, st.st_size);
 
-        fault = run_target(afl, afl->fsrv.exec_tmout);
+        fault = run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
 
         if (afl->stop_soon) goto close_sync;
 
@@ -608,26 +599,25 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
   if (afl->mutator && afl->mutator->afl_custom_trim)
     return trim_case_custom(afl, q, in_buf);
 
-  static u8 tmp[64];
-  static u8 clean_trace[MAP_SIZE];
-
   u8  needs_write = 0, fault = 0;
   u32 trim_exec = 0;
   u32 remove_len;
   u32 len_p2;
 
+  u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX];
+
   /* Although the trimmer will be less useful when variable behavior is
      detected, it will still work to some extent, so we don't check for
      this. */
 
   if (q->len < 5) return 0;
 
-  afl->stage_name = tmp;
+  afl->stage_name = afl->stage_name_buf;
   afl->bytes_trim_in += q->len;
 
   /* Select initial chunk len, starting with large steps. */
 
-  len_p2 = next_p2(q->len);
+  len_p2 = next_pow2(q->len);
 
   remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES);
 
@@ -638,7 +628,9 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
 
     u32 remove_pos = remove_len;
 
-    sprintf(tmp, "trim %s/%s", DI(remove_len), DI(remove_len));
+    sprintf(afl->stage_name_buf, "trim %s/%s",
+            u_stringify_int(val_bufs[0], remove_len),
+            u_stringify_int(val_bufs[1], remove_len));
 
     afl->stage_cur = 0;
     afl->stage_max = q->len / remove_len;
@@ -650,14 +642,15 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
 
       write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail);
 
-      fault = run_target(afl, afl->fsrv.exec_tmout);
+      fault = run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
       ++afl->trim_execs;
 
       if (afl->stop_soon || fault == FAULT_ERROR) goto abort_trimming;
 
-      /* Note that we don't keep track of crashes or hangs here; maybe TODO? */
+      /* Note that we don't keep track of crashes or hangs here; maybe TODO?
+       */
 
-      cksum = hash32(afl->fsrv.trace_bits, MAP_SIZE, HASH_CONST);
+      cksum = hash32(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
 
       /* If the deletion had no impact on the trace, make it permanent. This
          isn't perfect for variable-path inputs, but we're just making a
@@ -669,7 +662,7 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
         u32 move_tail = q->len - remove_pos - trim_avail;
 
         q->len -= trim_avail;
-        len_p2 = next_p2(q->len);
+        len_p2 = next_pow2(q->len);
 
         memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail,
                 move_tail);
@@ -680,7 +673,7 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
         if (!needs_write) {
 
           needs_write = 1;
-          memcpy(clean_trace, afl->fsrv.trace_bits, MAP_SIZE);
+          memcpy(afl->clean_trace, afl->fsrv.trace_bits, afl->fsrv.map_size);
 
         }
 
@@ -722,7 +715,7 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
     ck_write(fd, in_buf, q->len, q->fname);
     close(fd);
 
-    memcpy(afl->fsrv.trace_bits, clean_trace, MAP_SIZE);
+    memcpy(afl->fsrv.trace_bits, afl->clean_trace, afl->fsrv.map_size);
     update_bitmap_score(afl, q);
 
   }
@@ -744,14 +737,19 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
 
   if (afl->post_handler) {
 
-    out_buf = afl->post_handler(out_buf, &len);
-    if (!out_buf || !len) return 0;
+    u8 *post_buf = NULL;
+
+    size_t post_len =
+        afl->post_handler(afl->post_data, out_buf, len, &post_buf);
+    if (!post_buf || !post_len) return 0;
+    out_buf = post_buf;
+    len = post_len;
 
   }
 
   write_to_testcase(afl, out_buf, len);
 
-  fault = run_target(afl, afl->fsrv.exec_tmout);
+  fault = run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
 
   if (afl->stop_soon) return 1;