Exit on time (#904)

* Variable AFL_EXIT_ON_TIME description has been added.
Variables AFL_EXIT_ON_TIME and afl_exit_on_time has been added.
afl->exit_on_time variable initialization has been added.
The asignment of a value to the afl->afl_env.afl_exit_on_time variable from
environment variables has been added.
Code to exit on timeout if new path not found has been added.

* Type of afl_exit_on_time variable has been changed.
Variable exit_on_time has been added to the afl_state_t structure.

* Command `export AFL_EXIT_WHEN_DONE=1` has been added.

* Millisecond to second conversion has been added.
Call get_cur_time() has been added.

* Revert to using the saved current time value.

* Useless check has been removed.
diff --git a/docs/env_variables.md b/docs/env_variables.md
index 0100ffa..8879db7 100644
--- a/docs/env_variables.md
+++ b/docs/env_variables.md
@@ -284,6 +284,10 @@
     normally indicated by the cycle counter in the UI turning green. May be
     convenient for some types of automated jobs.
 
+  - `AFL_EXIT_ON_TIME` Causes afl-fuzz to terminate if no new paths were 
+    found within a specified period of time. May be convenient for some 
+    types of automated jobs.
+
   - `AFL_EXIT_ON_SEED_ISSUES` will restore the vanilla afl-fuzz behaviour
     which does not allow crashes or timeout seeds in the initial -i corpus.
 
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index f201782..a09d6f7 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -392,7 +392,7 @@
       *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port,
       *afl_crash_exitcode, *afl_statsd_tags_flavor, *afl_testcache_size,
       *afl_testcache_entries, *afl_kill_signal, *afl_target_env,
-      *afl_persistent_record;
+      *afl_persistent_record, *afl_exit_on_time;
 
 } afl_env_vars_t;
 
@@ -575,7 +575,8 @@
       last_sync_cycle,                  /* Cycle no. of the last sync       */
       last_path_time,                   /* Time for most recent path (ms)   */
       last_crash_time,                  /* Time for most recent crash (ms)  */
-      last_hang_time;                   /* Time for most recent hang (ms)   */
+      last_hang_time,                   /* Time for most recent hang (ms)   */
+      exit_on_time;                     /* Delay to exit if no new paths    */
 
   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 cd23ca3..9175005 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -49,6 +49,7 @@
     "AFL_DUMB_FORKSRV",
     "AFL_ENTRYPOINT",
     "AFL_EXIT_WHEN_DONE",
+    "AFL_EXIT_ON_TIME",
     "AFL_EXIT_ON_SEED_ISSUES",
     "AFL_FAST_CAL",
     "AFL_FORCE_UI",
diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c
index 28d3339..73ba7a5 100644
--- a/src/afl-fuzz-state.c
+++ b/src/afl-fuzz-state.c
@@ -99,6 +99,7 @@
   afl->cal_cycles = CAL_CYCLES;
   afl->cal_cycles_long = CAL_CYCLES_LONG;
   afl->hang_tmout = EXEC_TIMEOUT;
+  afl->exit_on_time = 0;
   afl->stats_update_freq = 1;
   afl->stats_avg_exec = 0;
   afl->skip_deterministic = 1;
@@ -187,6 +188,13 @@
             afl->afl_env.afl_exit_when_done =
                 get_afl_env(afl_environment_variables[i]) ? 1 : 0;
 
+          } else if (!strncmp(env, "AFL_EXIT_ON_TIME",
+
+                              afl_environment_variable_len)) {
+
+            afl->afl_env.afl_exit_on_time =
+                (u8 *) get_afl_env(afl_environment_variables[i]);
+
           } else if (!strncmp(env, "AFL_NO_AFFINITY",
 
                               afl_environment_variable_len)) {
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c
index fd9af5e..ee8bd2d 100644
--- a/src/afl-fuzz-stats.c
+++ b/src/afl-fuzz-stats.c
@@ -574,6 +574,16 @@
 
   }
 
+  /* AFL_EXIT_ON_TIME. */ 
+
+  if (unlikely(afl->last_path_time && !afl->non_instrumented_mode && 
+    afl->afl_env.afl_exit_on_time && 
+    (cur_ms - afl->last_path_time) > afl->exit_on_time)) {
+
+    afl->stop_soon = 2;
+
+  }
+
   if (unlikely(afl->total_crashes && afl->afl_env.afl_bench_until_crash)) {
 
     afl->stop_soon = 2;
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 8c3ba57..8de3ed6 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -204,6 +204,7 @@
       "AFL_DISABLE_TRIM: disable the trimming of test cases\n"
       "AFL_DUMB_FORKSRV: use fork server without feedback from target\n"
       "AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n"
+      "AFL_EXIT_ON_TIME: exit when no new paths are found within the specified time period\n"
       "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)\n"
       "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n"
       "AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n"
@@ -1246,6 +1247,13 @@
 
   }
 
+  if (afl->afl_env.afl_exit_on_time) {
+
+    u64 exit_on_time = atoi(afl->afl_env.afl_exit_on_time);
+    afl->exit_on_time = (u64)exit_on_time * 1000;
+
+  }
+
   if (afl->afl_env.afl_max_det_extras) {
 
     s32 max_det_extras = atoi(afl->afl_env.afl_max_det_extras);
diff --git a/test/test-performance.sh b/test/test-performance.sh
index cd9f6ca..d61e2f2 100755
--- a/test/test-performance.sh
+++ b/test/test-performance.sh
@@ -18,6 +18,7 @@
 export AFL_PATH=`pwd`/..
 
 unset AFL_EXIT_WHEN_DONE
+unset AFL_EXIT_ON_TIME
 unset AFL_SKIP_CPUFREQ
 unset AFL_DEBUG
 unset AFL_HARDEN
diff --git a/test/test-pre.sh b/test/test-pre.sh
index 174f2f7..7819da4 100755
--- a/test/test-pre.sh
+++ b/test/test-pre.sh
@@ -62,6 +62,7 @@
 test -z "$ECHO" && { printf Error: printf command does not support octal character codes ; exit 1 ; }
 
 export AFL_EXIT_WHEN_DONE=1
+export AFL_EXIT_ON_TIME=60
 export AFL_SKIP_CPUFREQ=1
 export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1
 unset AFL_NO_X86