about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvanhauser-thc <vh@thc.org>2022-02-05 08:27:17 +0100
committervanhauser-thc <vh@thc.org>2022-02-05 08:27:17 +0100
commitd5b9cd4b73253c2fbbc7da88015ae0eac303eb32 (patch)
tree987c3ab057607e26bba6fbd7309f894d6107b07d
parentce5032cc2949366260db12a7d52699b23ff9cda4 (diff)
downloadafl++-d5b9cd4b73253c2fbbc7da88015ae0eac303eb32.tar.gz
add afl-fuzz -y fuzz length support
-rw-r--r--docs/Changelog.md3
-rw-r--r--include/afl-fuzz.h9
-rw-r--r--src/afl-fuzz-bitmap.c2
-rw-r--r--src/afl-fuzz-cmplog.c2
-rw-r--r--src/afl-fuzz-init.c5
-rw-r--r--src/afl-fuzz-mutators.c2
-rw-r--r--src/afl-fuzz-run.c35
-rw-r--r--src/afl-fuzz-state.c2
-rw-r--r--src/afl-fuzz.c42
9 files changed, 85 insertions, 17 deletions
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 94e854e4..153369b7 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -11,6 +11,9 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
 ### Version ++4.01a (dev)
   - fix */build_...sh scripts to work outside of git
   - new custom_mutator: libafl with token fuzzing :)
+  - afl-fuzz:
+    - new commandline option -y to set min and max length of generated
+      fuzz inputs
   - frida_mode:
     - update to new frida release, handles now c++ throw/catch
 
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index e225211f..3712fc4f 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -726,6 +726,9 @@ typedef struct afl_state {
   /* queue entries ready for splicing count (len > 4) */
   u32 ready_for_splicing_count;
 
+  /* min/max length for generated fuzzing inputs */
+  u32 min_length, max_length;
+
   /* This is the user specified maximum size to use for the testcase cache */
   u64 q_testcase_max_cache_size;
 
@@ -1090,12 +1093,12 @@ int  statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen);
 
 /* Run */
 
-fsrv_run_result_t fuzz_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 *);
+u32  write_to_testcase(afl_state_t *, void *, u32, u32);
+u8   calibrate_case(afl_state_t *, struct queue_entry *, u8 *, u32, u8);
 u8   trim_case(afl_state_t *, struct queue_entry *, u8 *);
 u8   common_fuzz_stuff(afl_state_t *, u8 *, u32);
+fsrv_run_result_t fuzz_run_target(afl_state_t *, afl_forkserver_t *fsrv, u32);
 
 /* Fuzz one */
 
diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c
index 8d044959..b963caf8 100644
--- a/src/afl-fuzz-bitmap.c
+++ b/src/afl-fuzz-bitmap.c
@@ -633,7 +633,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
       if (afl->fsrv.exec_tmout < afl->hang_tmout) {
 
         u8 new_fault;
-        write_to_testcase(afl, mem, len);
+        len = write_to_testcase(afl, mem, len, 0);
         new_fault = fuzz_run_target(afl, &afl->fsrv, afl->hang_tmout);
         classify_counts(&afl->fsrv);
 
diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c
index ce8f1a83..1a8052a0 100644
--- a/src/afl-fuzz-cmplog.c
+++ b/src/afl-fuzz-cmplog.c
@@ -49,7 +49,7 @@ u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
 
   u8 fault;
 
-  write_to_testcase(afl, out_buf, len);
+  len = write_to_testcase(afl, out_buf, len, 0);
 
   fault = fuzz_run_target(afl, &afl->cmplog_fsrv, afl->fsrv.exec_tmout);
 
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index eb73b120..45f28d4b 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -617,11 +617,10 @@ void read_foreign_testcases(afl_state_t *afl, int first) {
 
         }
 
-        write_to_testcase(afl, mem, st.st_size);
+        u32 len = write_to_testcase(afl, mem, st.st_size, 1);
         fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
         afl->syncing_party = foreign_name;
-        afl->queued_imported +=
-            save_if_interesting(afl, mem, st.st_size, fault);
+        afl->queued_imported += save_if_interesting(afl, mem, len, fault);
         afl->syncing_party = 0;
         munmap(mem, st.st_size);
         close(fd);
diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c
index 51a43dbd..e78e2dc4 100644
--- a/src/afl-fuzz-mutators.c
+++ b/src/afl-fuzz-mutators.c
@@ -428,7 +428,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf,
 
     if (likely(retlen)) {
 
-      write_to_testcase(afl, retbuf, retlen);
+      retlen = write_to_testcase(afl, retbuf, retlen, 0);
 
       fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
       ++afl->trim_execs;
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index eaa82b19..5da0e583 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -73,8 +73,8 @@ fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) {
    old file is unlinked and a new one is created. Otherwise, afl->fsrv.out_fd is
    rewound and truncated. */
 
-void __attribute__((hot))
-write_to_testcase(afl_state_t *afl, void *mem, u32 len) {
+u32 __attribute__((hot))
+write_to_testcase(afl_state_t *afl, void *mem, u32 len, u32 fix) {
 
 #ifdef _AFL_DOCUMENT_MUTATIONS
   s32  doc_fd;
@@ -120,16 +120,39 @@ write_to_testcase(afl_state_t *afl, void *mem, u32 len) {
 
     });
 
+    if (unlikely(new_size < afl->min_length && !fix)) {
+
+      new_size = afl->min_length;
+
+    } else if (unlikely(new_size > afl->max_length)) {
+
+      new_size = afl->max_length;
+
+    }
+
     /* everything as planned. use the potentially new data. */
     afl_fsrv_write_to_testcase(&afl->fsrv, new_mem, new_size);
+    len = new_size;
 
   } else {
 
+    if (unlikely(len < afl->min_length && !fix)) {
+
+      len = afl->min_length;
+
+    } else if (unlikely(len > afl->max_length)) {
+
+      len = afl->max_length;
+
+    }
+
     /* boring uncustom. */
     afl_fsrv_write_to_testcase(&afl->fsrv, mem, len);
 
   }
 
+  return len;
+
 }
 
 /* The same, but with an adjustable gap. Used for trimming. */
@@ -346,7 +369,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
   /* we need a dummy run if this is LTO + cmplog */
   if (unlikely(afl->shm.cmplog_mode)) {
 
-    write_to_testcase(afl, use_mem, q->len);
+    (void)write_to_testcase(afl, use_mem, q->len, 1);
 
     fault = fuzz_run_target(afl, &afl->fsrv, use_tmout);
 
@@ -389,7 +412,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
 
     u64 cksum;
 
-    write_to_testcase(afl, use_mem, q->len);
+    (void)write_to_testcase(afl, use_mem, q->len, 1);
 
     fault = fuzz_run_target(afl, &afl->fsrv, use_tmout);
 
@@ -700,7 +723,7 @@ void sync_fuzzers(afl_state_t *afl) {
         /* See what happens. We rely on save_if_interesting() to catch major
            errors and save the test case. */
 
-        write_to_testcase(afl, mem, st.st_size);
+        (void)write_to_testcase(afl, mem, st.st_size, 1);
 
         fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
 
@@ -943,7 +966,7 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
 
   u8 fault;
 
-  write_to_testcase(afl, out_buf, len);
+  len = write_to_testcase(afl, out_buf, len, 0);
 
   fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
 
diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c
index 69ffa8cf..24bd28dd 100644
--- a/src/afl-fuzz-state.c
+++ b/src/afl-fuzz-state.c
@@ -102,6 +102,8 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) {
   afl->stats_avg_exec = 0;
   afl->skip_deterministic = 1;
   afl->cmplog_lvl = 2;
+  afl->min_length = 1;
+  afl->max_length = MAX_FILE;
 #ifndef NO_SPLICING
   afl->use_splicing = 1;
 #endif
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 7a74fc7e..6ca9be33 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -155,6 +155,9 @@ static void usage(u8 *argv0, int more_help) {
       "\n"
 
       "Mutator settings:\n"
+      "  -y [min-]max  - set minimum and maximum length of generated fuzzing "
+      "input.\n"
+      "                  default: 1-%lu\n"
       "  -D            - enable deterministic fuzzing (once per queue entry)\n"
       "  -L minutes    - use MOpt(imize) mode and set the time limit for "
       "entering the\n"
@@ -204,7 +207,7 @@ static void usage(u8 *argv0, int more_help) {
       "(0-...)\n"
       "  -e ext        - file extension for the fuzz test input file (if "
       "needed)\n\n",
-      argv0, EXEC_TIMEOUT, MEM_LIMIT, FOREIGN_SYNCS_MAX);
+      argv0, EXEC_TIMEOUT, MEM_LIMIT, MAX_FILE, FOREIGN_SYNCS_MAX);
 
   if (more_help > 1) {
 
@@ -529,11 +532,36 @@ int main(int argc, char **argv_orig, char **envp) {
 
   while ((opt = getopt(
               argc, argv,
-              "+Ab:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNOXYo:p:RQs:S:t:T:UV:Wx:Z")) >
+              "+Ab:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNOo:p:RQs:S:t:T:UV:WXx:Yy:Z")) >
          0) {
 
     switch (opt) {
 
+      case 'y': {
+
+        u8 *sep;
+        if (!(sep = strchr(optarg, '-')) && !(sep = strchr(optarg, ':'))) {
+
+          afl->max_length = atoi(optarg);
+
+        } else {
+
+          afl->min_length = atoi(optarg);
+          afl->max_length = atoi(sep + 1);
+
+        }
+
+        if (afl->min_length < 1 || afl->max_length > MAX_FILE ||
+            afl->min_length > afl->max_length) {
+
+          FATAL("Illegal min/max length values: %s", optarg);
+
+        }
+
+        break;
+
+      }
+
       case 'Z':
         afl->old_seed_selection = 1;
         break;
@@ -1622,6 +1650,16 @@ int main(int argc, char **argv_orig, char **envp) {
 
   }
 
+  OKF("Generating fuzz data with a a length of min=%u max=%u", afl->min_length,
+      afl->max_length);
+  u32 min_alloc = MAX(64U, afl->min_length);
+  afl_realloc(AFL_BUF_PARAM(in_scratch), min_alloc);
+  afl_realloc(AFL_BUF_PARAM(in), min_alloc);
+  afl_realloc(AFL_BUF_PARAM(out_scratch), min_alloc);
+  afl_realloc(AFL_BUF_PARAM(out), min_alloc);
+  afl_realloc(AFL_BUF_PARAM(eff), min_alloc);
+  afl_realloc(AFL_BUF_PARAM(ex), min_alloc);
+
   afl->fsrv.use_fauxsrv = afl->non_instrumented_mode == 1 || afl->no_forkserver;
 
   #ifdef __linux__