about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavide Quarta <quarta@qti.qualcomm.com>2024-01-23 19:36:49 +0100
committerDavide Quarta <quarta@qti.qualcomm.com>2024-01-23 19:36:49 +0100
commit8fedf4998449d5b6b909a1118fc2e152e4d2e6e7 (patch)
tree71c8d03f94c006f952be8522f8403fe0fca273c7
parentb99bbf671b7469a5aad29898fe28489004c4cbe7 (diff)
downloadafl++-8fedf4998449d5b6b909a1118fc2e152e4d2e6e7.tar.gz
replay mode support
-rw-r--r--.gitignore1
-rw-r--r--include/afl-fuzz.h4
-rw-r--r--include/config.h5
-rw-r--r--include/persistent_replay.h149
-rw-r--r--instrumentation/afl-compiler-rt.o.c36
-rw-r--r--src/afl-forkserver.c79
-rw-r--r--src/afl-fuzz-init.c6
-rw-r--r--src/afl-fuzz.c2
-rw-r--r--utils/persistent_mode/Makefile3
-rw-r--r--utils/persistent_mode/persistent_demo_new.c15
10 files changed, 257 insertions, 43 deletions
diff --git a/.gitignore b/.gitignore
index f76a86fc..891ced9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,6 +103,7 @@ utils/optimin/build
 utils/optimin/optimin
 utils/persistent_mode/persistent_demo
 utils/persistent_mode/persistent_demo_new
+utils/persistent_mode/persistent_demo_new_compat
 utils/persistent_mode/test-instr
 utils/plot_ui/afl-plot-ui
 vuln_prog
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index f1813df6..864bc6b6 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -124,6 +124,10 @@
   #define CASE_PREFIX "id_"
 #endif                                                    /* ^!SIMPLE_FILES */
 
+#ifdef AFL_PERSISTENT_RECORD
+  #define RECORD_PREFIX "RECORD:" 
+#endif
+
 #define STAGE_BUF_SIZE (64)  /* usable size for stage name buf in afl_state */
 
 // Little helper to access the ptr to afl->##name_buf - for use in afl_realloc.
diff --git a/include/config.h b/include/config.h
index 63340650..1649f110 100644
--- a/include/config.h
+++ b/include/config.h
@@ -83,7 +83,10 @@
    will be kept and written to the crash/ directory as RECORD:... files.
    Note that every crash will be written, not only unique ones! */
 
-// #define AFL_PERSISTENT_RECORD
+//  #define AFL_PERSISTENT_RECORD
+
+/* Builds compiler-rt with support to replay persistent records */
+//  #define AFL_PERSISTENT_REPLAY
 
 /* console output colors: There are three ways to configure its behavior
  * 1. default: colored outputs fixed on: defined USE_COLOR && defined
diff --git a/include/persistent_replay.h b/include/persistent_replay.h
new file mode 100644
index 00000000..b1a55e9f
--- /dev/null
+++ b/include/persistent_replay.h
@@ -0,0 +1,149 @@
+#ifndef _HAVE_PERSISTENT_REPLAY_H
+#define _HAVE_PERSISTENT_REPLAY_H
+
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static unsigned short int is_replay_record;
+static unsigned int replay_record;
+static unsigned int replay_record_cnt;
+static char replay_record_path[PATH_MAX];
+static char **record_arg;
+static char *replay_record_dir;
+static struct dirent **record_list;
+
+static int select_files(const struct dirent *dirbuf) {
+
+  char fn[4096];
+
+  if (dirbuf->d_name[0] == '.'){
+    return 0;
+  } else {
+    snprintf(fn, sizeof(fn), "RECORD:%06u", replay_record);
+    return !!strstr(dirbuf->d_name, fn);
+  }
+}
+  
+static int compare_files(const struct dirent **da, const struct dirent **db) {
+  
+  unsigned int c1=0, c2=0;
+
+  sscanf((*da)->d_name, "RECORD:%*u,cnt:%06u", &c1);
+  sscanf((*db)->d_name, "RECORD:%*u,cnt:%06u", &c2);
+
+  return c1-c2;
+}
+
+__attribute__((destructor)) static void __afl_record_replay_destroy(void){
+  for (int i=0; i < replay_record_cnt; i++) {
+    free(record_list[i]);
+  }
+  free(record_list);
+}
+
+__attribute__((constructor)) static void __afl_record_replay_init(int argc, char **argv) {
+  
+  char **argp;
+
+  /* caveat: if harness uses @@ and we don't pass it, it will regardless loop the number of iterations defined for AFL_LOOP (on the same file)*/
+  if(!(is_replay_record = !!getenv("AFL_PERSISTENT_REPLAY"))){
+    // printf("[warning] AFL_PERSISTENT_REPLAY not set.\n");
+    return;
+  }
+
+  replay_record = atoi(getenv("AFL_PERSISTENT_REPLAY"));
+  replay_record_dir = getenv("AFL_PERSISTENT_DIR");
+  replay_record_cnt = scandir(replay_record_dir ? replay_record_dir : "./", &record_list, select_files, compare_files);  
+
+  if (!replay_record_cnt){
+    printf("[error] Can't find the requested record!\n");
+    is_replay_record = 0;
+  }
+
+  argp = argv;
+  while (*argp){
+    if (!strcmp(*argp, "@@")){
+      record_arg = argp;
+      *record_arg = replay_record_path;
+      break;
+    }
+    ++argp;
+  }
+
+}
+
+/* only used if explictly included for compatibility
+   compiling without afl-cc */
+
+#ifdef AFL_COMPAT
+
+#ifndef PATH_MAX
+  #define PATH_MAX 4096
+#endif
+
+#define FUZZ_BUF_SIZE 1024000
+
+// extern ssize_t read(int fildes, void *buf, size_t nbyte);
+
+//extern int __afl_persistent_loop(unsigned int max_cnt);
+//extern unsigned char fuzz_buf[];
+
+#ifndef __AFL_HAVE_MANUAL_CONTROL
+  #define __AFL_HAVE_MANUAL_CONTROL
+#endif
+
+#define __AFL_FUZZ_TESTCASE_LEN (read(0, fuzz_buf, FUZZ_BUF_SIZE))
+#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
+#define __AFL_FUZZ_INIT() void sync(void); 
+#define __AFL_INIT() sync() 
+#define __AFL_LOOP(x) __afl_persistent_loop(x)
+
+unsigned char fuzz_buf[FUZZ_BUF_SIZE];
+
+int __afl_persistent_loop(unsigned int max_cnt) {
+
+  static unsigned int cycle_cnt = 1;
+  static unsigned short int inited = 0;
+  char tcase[PATH_MAX];
+
+  if( is_replay_record ){
+
+    if (!inited){
+      cycle_cnt = replay_record_cnt;
+      inited = 1;
+    }
+
+    snprintf(tcase, PATH_MAX, "%s/%s",
+                  replay_record_dir ? replay_record_dir : "./",
+                  record_list[replay_record_cnt-cycle_cnt]->d_name);
+
+
+    if (record_arg) {
+      *record_arg = tcase;
+    } else {
+      int fd = open(tcase, O_RDONLY);
+      dup2(fd, 0);
+      close(fd);
+    }
+
+  } else {
+
+    if (!inited){
+      cycle_cnt = max_cnt;
+      inited = 1;
+    }
+
+  }
+
+  return cycle_cnt--;
+}
+
+#endif  // AFL_COMPAT
+
+#endif  // _HAVE_PERSISTENT_REPLAY_H
\ No newline at end of file
diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c
index 39a762b6..0fa22aee 100644
--- a/instrumentation/afl-compiler-rt.o.c
+++ b/instrumentation/afl-compiler-rt.o.c
@@ -83,6 +83,10 @@
 #include <sys/mman.h>
 #include <fcntl.h>
 
+#ifdef AFL_PERSISTENT_REPLAY
+#include "persistent_replay.h"
+#endif
+
 /* Globals needed by the injected instrumentation. The __afl_area_initial region
    is used for instrumentation output before __afl_map_shm() has a chance to
    run. It will end up as .comm, so it shouldn't be too wasteful. */
@@ -1338,6 +1342,38 @@ int __afl_persistent_loop(unsigned int max_cnt) {
   static u8  first_pass = 1;
   static u32 cycle_cnt;
 
+#ifdef AFL_PERSISTENT_REPLAY
+
+#ifndef PATH_MAX
+  #define PATH_MAX 4096
+#endif
+
+  static u8  inited = 0;
+  char tcase[PATH_MAX];
+
+  if( unlikely(is_replay_record) ){
+
+      if (!inited){
+        cycle_cnt = replay_record_cnt;
+        inited = 1;
+      }
+
+      snprintf(tcase, PATH_MAX, "%s/%s",
+                  replay_record_dir ? replay_record_dir : "./",
+                  record_list[replay_record_cnt-cycle_cnt]->d_name);
+
+      if (record_arg) {
+        *record_arg = tcase;
+      } else {
+        int fd = open(tcase, O_RDONLY);
+        dup2(fd, 0);
+        close(fd);
+      }
+    return cycle_cnt--;
+  } else
+
+#endif  
+
   if (first_pass) {
 
     /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate.
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index 3f9bfa72..f8dd783f 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -1591,6 +1591,11 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
   u32 exec_ms;
   u32 write_value = fsrv->last_run_timed_out;
 
+#ifdef AFL_PERSISTENT_RECORD
+  fsrv_run_result_t retval = FSRV_RUN_OK;
+  char *persistent_out_fmt;
+#endif
+
 #ifdef __linux__
   if (fsrv->nyx_mode) {
 
@@ -1684,7 +1689,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
 
   }
 
-#ifdef AFL_PERSISTENT_RECORD
+#ifdef AFL_eERSISTENT_RECORD
   // end of persistent loop?
   if (unlikely(fsrv->persistent_record &&
                fsrv->persistent_record_pid != fsrv->child_pid)) {
@@ -1790,8 +1795,14 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
   if (unlikely(fsrv->last_run_timed_out)) {
 
     fsrv->last_kill_signal = fsrv->child_kill_signal;
-    return FSRV_RUN_TMOUT;
 
+#ifndef AFL_PERSISTENT_RECORD
+    return FSRV_RUN_TMOUT;
+#else
+    retval = FSRV_RUN_TMOUT;
+    persistent_out_fmt = "%s/hangs/RECORD:%06u,cnt:%06u";
+    goto store_persistent_record;
+#endif
   }
 
   /* Did we crash?
@@ -1811,48 +1822,58 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
           (fsrv->uses_crash_exitcode &&
            WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) {
 
-#ifdef AFL_PERSISTENT_RECORD
-    if (unlikely(fsrv->persistent_record)) {
+    /* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
+    fsrv->last_kill_signal =
+        WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
 
-      char fn[PATH_MAX];
-      u32  i, writecnt = 0;
-      for (i = 0; i < fsrv->persistent_record; ++i) {
+#ifndef AFL_PERSISTENT_RECORD
+    return FSRV_RUN_CRASH;
+#else
+    retval = FSRV_RUN_CRASH;
+    persistent_out_fmt = "%s/crashes/RECORD:%06u,cnt:%06u";
+    goto store_persistent_record;
+#endif
 
-        u32 entry = (i + fsrv->persistent_record_idx) % fsrv->persistent_record;
-        u8 *data = fsrv->persistent_record_data[entry];
-        u32 len = fsrv->persistent_record_len[entry];
-        if (likely(len && data)) {
+  }
 
-          snprintf(fn, sizeof(fn), "%s/RECORD:%06u,cnt:%06u",
-                   fsrv->persistent_record_dir, fsrv->persistent_record_cnt,
-                   writecnt++);
-          int fd = open(fn, O_CREAT | O_TRUNC | O_WRONLY, 0644);
-          if (fd >= 0) {
+  /* success :) */
+  return FSRV_RUN_OK;
+
+#ifdef AFL_PERSISTENT_RECORD
+store_persistent_record:
+  if (unlikely(retval == FSRV_RUN_CRASH || retval == FSRV_RUN_TMOUT)  &&
+      unlikely(fsrv->persistent_record)) {
 
-            ck_write(fd, data, len, fn);
-            close(fd);
+    char fn[PATH_MAX];
+    u32  i, writecnt = 0;
+    for (i = 0; i < fsrv->persistent_record; ++i) {
 
-          }
+      u32 entry = (i + fsrv->persistent_record_idx) % fsrv->persistent_record;
+      u8 *data = fsrv->persistent_record_data[entry];
+      u32 len = fsrv->persistent_record_len[entry];
+      if (likely(len && data)) {
+
+        snprintf(fn, sizeof(fn), persistent_out_fmt,
+                 fsrv->persistent_record_dir, fsrv->persistent_record_cnt,
+                 writecnt++);
+        int fd = open(fn, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+        if (fd >= 0) {
+
+          ck_write(fd, data, len, fn);
+          close(fd);
 
         }
 
       }
 
-      ++fsrv->persistent_record_cnt;
-
     }
 
-#endif
-
-    /* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
-    fsrv->last_kill_signal =
-        WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
-    return FSRV_RUN_CRASH;
+    ++fsrv->persistent_record_cnt;
 
   }
 
-  /* success :) */
-  return FSRV_RUN_OK;
+  return retval;
+#endif
 
 }
 
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index 35932913..5b7dc4c1 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -1915,6 +1915,9 @@ static void handle_existing_out_dir(afl_state_t *afl) {
 
   }
 
+#ifdef AFL_PERSISTENT_RECORD
+  delete_files(fn, RECORD_PREFIX);
+#endif
   if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
@@ -1947,6 +1950,9 @@ static void handle_existing_out_dir(afl_state_t *afl) {
 
   }
 
+#ifdef AFL_PERSISTENT_RECORD
+  delete_files(fn, RECORD_PREFIX);
+#endif
   if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
   ck_free(fn);
 
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 17949fd7..40c30472 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -2163,7 +2163,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
     }
 
-    afl->fsrv.persistent_record_dir = alloc_printf("%s/crashes", afl->out_dir);
+    afl->fsrv.persistent_record_dir = alloc_printf("%s", afl->out_dir);
 
   }
 
diff --git a/utils/persistent_mode/Makefile b/utils/persistent_mode/Makefile
index e348c46c..64de82a7 100644
--- a/utils/persistent_mode/Makefile
+++ b/utils/persistent_mode/Makefile
@@ -1,10 +1,11 @@
 all:
 	../../afl-clang-fast -o persistent_demo persistent_demo.c
 	../../afl-clang-fast -o persistent_demo_new persistent_demo_new.c
+	gcc -g -I ../../include -o persistent_demo_new_compat persistent_demo_new.c
 	AFL_DONT_OPTIMIZE=1 ../../afl-clang-fast -o test-instr test-instr.c
 
 document:
 	AFL_DONT_OPTIMIZE=1 ../../afl-clang-fast -D_AFL_DOCUMENT_MUTATIONS -o test-instr test-instr.c
 
 clean:
-	rm -f persistent_demo persistent_demo_new test-instr
+	rm -f persistent_demo persistent_demo_new persistent_demo_new_compat test-instr
diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c
index 285f50aa..40ada9e1 100644
--- a/utils/persistent_mode/persistent_demo_new.c
+++ b/utils/persistent_mode/persistent_demo_new.c
@@ -31,17 +31,8 @@
 
 /* this lets the source compile without afl-clang-fast/lto */
 #ifndef __AFL_FUZZ_TESTCASE_LEN
-
-ssize_t       fuzz_len;
-unsigned char fuzz_buf[1024000];
-
-  #define __AFL_FUZZ_TESTCASE_LEN fuzz_len
-  #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
-  #define __AFL_FUZZ_INIT() void sync(void);
-  #define __AFL_LOOP(x) \
-    ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
-  #define __AFL_INIT() sync()
-
+#define AFL_COMPAT
+#include "persistent_replay.h"
 #endif
 
 __AFL_FUZZ_INIT();
@@ -95,6 +86,8 @@ int main(int argc, char **argv) {
               if (buf[5] == '!') {
 
                 printf("six\n");
+                char *nullo = NULL+1;
+                *nullo = 'p';
                 abort();
 
               }