From 63d79213e1dea7d683dffd3dc656baef239f1ee1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ulf=20Karg=C3=A9n?= <ulf.kargen@liu.se>
Date: Fri, 10 May 2024 20:34:00 +0200
Subject: [PATCH] implement fuzzer execution serialization

---
 include/afl-fuzz.h   |  4 +--
 include/envs.h       |  1 +
 src/afl-fuzz-state.c |  7 ++++
 src/afl-fuzz.c       | 86 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 96 insertions(+), 2 deletions(-)

diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index f87e21df..425508c5 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -399,7 +399,7 @@ typedef struct afl_env_vars {
       afl_cycle_schedules, afl_expand_havoc, afl_statsd, afl_cmplog_only_new,
       afl_exit_on_seed_issues, afl_try_affinity, afl_ignore_problems,
       afl_keep_timeouts, afl_no_crash_readme, afl_ignore_timeouts,
-      afl_no_startup_calibration, afl_no_warn_instability;
+      afl_no_startup_calibration, afl_no_warn_instability, afl_serialize_exec;
 
   u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path,
       *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload,
@@ -596,7 +596,7 @@ typedef struct afl_state {
       longest_find_time,                /* Longest time taken for a find    */
       exit_on_time,                     /* Delay to exit if no new paths    */
       sync_time,                        /* Sync time (ms)                   */
-      sync_delay;                       /* Sync delay (ms)                   */
+      sync_delay;                       /* Sync delay (ms)                  */
 
   u32 slowest_exec_ms,                  /* Slowest testcase non hang in ms  */
       subseq_tmouts;                    /* Number of timeouts in a row      */
diff --git a/include/envs.h b/include/envs.h
index 942aab66..9b13295b 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -211,6 +211,7 @@ static char *afl_environment_variables[] = {
     "AFL_QUIET",
     "AFL_RANDOM_ALLOC_CANARY",
     "AFL_REAL_PATH",
+    "AFL_SERIALIZE_EXEC",
     "AFL_SHUFFLE_QUEUE",
     "AFL_SKIP_BIN_CHECK",
     "AFL_SKIP_CPUFREQ",
diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c
index 60206b4a..1b47cc00 100644
--- a/src/afl-fuzz-state.c
+++ b/src/afl-fuzz-state.c
@@ -584,6 +584,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
 
             }
 
+          } else if (!strncmp(env, "AFL_SERIALIZE_EXEC",
+
+                              afl_environment_variable_len)) {
+
+            afl->afl_env.afl_serialize_exec =
+                get_afl_env(afl_environment_variables[i]) ? 1 : 0;
+
           } else if (!strncmp(env, "AFL_FUZZER_STATS_UPDATE_INTERVAL",
 
                               afl_environment_variable_len)) {
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index b93b04ef..58f90669 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -453,6 +453,12 @@ int main(int argc, char **argv_orig, char **envp) {
   struct timeval  tv;
   struct timezone tz;
 
+  u8* syncpath = NULL;
+  s32 sync_fd;
+  struct flock synclock = { F_UNLCK, SEEK_SET, 0, 0, 0 };
+  u64 sync_work = 0, sleep_start = 0, tot_sleep = 0;
+  u8 done_work = 0;
+
   #if defined USE_COLOR && defined ALWAYS_COLORED
   if (getenv("AFL_NO_COLOR") || getenv("AFL_NO_COLOUR")) {
 
@@ -1406,6 +1412,18 @@ int main(int argc, char **argv_orig, char **envp) {
 
     fix_up_sync(afl);
 
+    if (afl->afl_env.afl_serialize_exec) {
+
+      syncpath = alloc_printf("%s/.serialize", afl->sync_dir);
+
+      sync_fd = open(syncpath, O_RDWR | O_CREAT, 0644);
+
+      if (sync_fd < 0) {
+        PFATAL("Unable to open fuzzer serialization file.");
+      }
+
+    }
+
   }
 
   if (!strcmp(afl->in_dir, afl->out_dir)) {
@@ -2506,6 +2524,61 @@ int main(int argc, char **argv_orig, char **envp) {
 
     do {
 
+      if (afl->sync_id && afl->afl_env.afl_serialize_exec) {
+
+        // Acquire lock on serialization sync file
+        if (synclock.l_type == F_UNLCK) {
+
+          synclock.l_type = F_WRLCK;
+          if (fcntl(sync_fd, F_SETLKW, &synclock) < 0) {
+              PFATAL("Unable to fcntl() fuzzer serialization file.");
+          }
+
+          if (sleep_start) {
+            tot_sleep += get_cur_time() - sleep_start;
+          }
+
+          s64 res;
+
+          if (lseek(sync_fd, 0, SEEK_SET) < 0 ||
+              (res = read(sync_fd, &sync_work, sizeof(sync_work))) < 0) {
+            PFATAL("Unable to read fuzzer serialization file.");
+          }
+
+          if (res == 0) {
+            sync_work = 0;
+          }
+
+        }
+
+        u64 tot_work = get_cur_time() - afl->start_time - tot_sleep;
+
+        if (done_work && sync_work + 1000 < tot_work) {
+
+          if (lseek(sync_fd, 0, SEEK_SET) < 0 ||
+              write(sync_fd, &tot_work, sizeof(tot_work)) < 0) {
+            PFATAL("Unable to write to fuzzer serialization file.");
+          }
+
+          sleep_start = get_cur_time();
+          done_work = 0;
+
+          // Release lock
+          synclock.l_type = F_UNLCK;
+          if (fcntl(sync_fd, F_SETLK, &synclock) < 0) {
+            PFATAL("Unable to fcntl() fuzzer serialization file.");
+          }
+
+          // Yield execution
+          usleep(1000);
+
+          continue;
+        }
+
+        done_work = 1;
+
+      }
+
       if (likely(!afl->old_seed_selection)) {
 
         if (unlikely(prev_queued_items < afl->queued_items ||
@@ -2695,6 +2768,19 @@ stop_fuzzing:
 
   }
 
+  if (afl->sync_id && afl->afl_env.afl_serialize_exec) {
+
+    /* TODO: This only works as intended when all fuzzer instances shut down
+       sequentially. Will break if one instance exits and another one joins
+       before all others have shut down. (Running instances keep references to
+       inode until all fds have been closed.)*/
+
+    close(sync_fd);
+    unlink(syncpath);
+    free(syncpath);
+
+  }
+
   if (frida_afl_preload) { ck_free(frida_afl_preload); }
 
   fclose(afl->fsrv.plot_file);
-- 
GitLab