about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2020-07-21 20:53:51 +0200
committervan Hauser <vh@thc.org>2020-07-21 20:53:51 +0200
commitce9b4698fec5222e0af1b62d68c4105e6364771e (patch)
tree04a9ca3d25eac9dc07b20e6b9daa0fa9da369898 /src
parent9a33a29b4a87ec9f211f83fa83f499e1ed8c256d (diff)
downloadafl++-ce9b4698fec5222e0af1b62d68c4105e6364771e.tar.gz
added andrea's splicing, added cycle_schedules
Diffstat (limited to 'src')
-rw-r--r--src/afl-fuzz-one.c719
-rw-r--r--src/afl-fuzz-queue.c112
-rw-r--r--src/afl-fuzz-state.c7
-rw-r--r--src/afl-fuzz.c80
4 files changed, 801 insertions, 117 deletions
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index 399bfcab..250409da 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -24,6 +24,11 @@
  */
 
 #include "afl-fuzz.h"
+#include <string.h>
+
+static u8 *strnstr(const u8 *s, const u8 *find, size_t slen);
+static u32 string_replace(u8 **out_buf, s32 *temp_len, u32 pos, u8 *from,
+                          u8 *to);
 
 /* MOpt */
 
@@ -362,6 +367,450 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) {
 
 #endif                                                     /* !IGNORE_FINDS */
 
+#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size
+
+/* search a string */
+
+static u8 *strnstr(const u8 *s, const u8 *find, size_t slen) {
+
+  char   c, sc;
+  size_t len;
+
+  if ((c = *find++) != '\0') {
+
+    len = strlen(find);
+    do {
+
+      do {
+
+        if (slen-- < 1 || (sc = *s++) == '\0') return (NULL);
+
+      } while (sc != c);
+
+      if (len > slen) return (NULL);
+
+    } while (strncmp(s, find, len) != 0);
+
+    s--;
+
+  }
+
+  return ((u8 *)s);
+
+}
+
+/* replace between deliminators, if rep == NULL, then we will duplicate the
+ * target */
+
+static u32 delim_replace(u8 **out_buf, s32 *temp_len, size_t pos,
+                         const u8 *ldelim, const u8 *rdelim, u8 *rep) {
+
+  u8 *end_buf = *out_buf + *temp_len;
+  u8 *ldelim_start = strnstr(*out_buf + pos, ldelim, *temp_len - pos);
+
+  if (ldelim_start != NULL) {
+
+    u32 max = (end_buf - ldelim_start - 1 > AFL_TXT_STRING_MAX_LEN
+                   ? AFL_TXT_STRING_MAX_LEN
+                   : end_buf - ldelim_start - 1);
+
+    if (max > 0) {
+
+      u8 *rdelim_end = strnstr(ldelim_start + 1, rdelim, max);
+
+      if (rdelim_end != NULL) {
+
+        u32 rep_len, delim_space_len = rdelim_end - ldelim_start - 1, xtra = 0;
+
+        if (rep != NULL) {
+
+          rep_len = (u32)strlen(rep);
+
+        } else {  // NULL? then we copy the value in between the delimiters
+
+          rep_len = delim_space_len;
+          delim_space_len = 0;
+          rep = ldelim_start + 1;
+          xtra = rep_len;
+
+        }
+
+        if (rep_len != delim_space_len) {
+
+          memmove(ldelim_start + rep_len + xtra + 1, rdelim_end,
+                  *temp_len - (rdelim_end - *out_buf));
+
+        }
+
+        memcpy(ldelim_start + 1, rep, rep_len);
+        *temp_len = (*temp_len - delim_space_len + rep_len);
+
+        return 1;
+
+      }
+
+    }
+
+  }
+
+  return 0;
+
+}
+
+static u32 delim_swap(u8 **out_buf, s32 *temp_len, size_t pos, const u8 *ldelim,
+                      const u8 *mdelim, const u8 *rdelim) {
+
+  u8 *out_buf_end = *out_buf + *temp_len;
+  u32 max = (*temp_len - pos > AFL_TXT_STRING_MAX_LEN ? AFL_TXT_STRING_MAX_LEN
+                                                      : *temp_len - pos);
+  u8 *ldelim_start = strnstr(*out_buf + pos, ldelim, max);
+
+  if (ldelim_start != NULL) {
+
+    max = (out_buf_end - ldelim_start - 1 > AFL_TXT_STRING_MAX_LEN
+               ? AFL_TXT_STRING_MAX_LEN
+               : out_buf_end - ldelim_start - 1);
+    if (max > 1) {
+
+      u8 *mdelim_pos = strnstr(ldelim_start + 1, mdelim, max);
+
+      if (mdelim_pos != NULL) {
+
+        max = (out_buf_end - mdelim_pos - 1 > AFL_TXT_STRING_MAX_LEN
+                   ? AFL_TXT_STRING_MAX_LEN
+                   : out_buf_end - mdelim_pos - 1);
+        if (max > 0) {
+
+          u8 *rdelim_end = strnstr(mdelim + 1, rdelim, max);
+
+          if (rdelim_end != NULL) {
+
+            u32 first_len = mdelim_pos - ldelim_start - 1;
+            u32 second_len = rdelim_end - mdelim_pos - 1;
+            u8  scratch[AFL_TXT_STRING_MAX_LEN];
+
+            memcpy(scratch, ldelim_start + 1, first_len);
+
+            if (first_len != second_len) {
+
+              memmove(ldelim_start + second_len + 1, mdelim_pos,
+                      out_buf_end - mdelim_pos);
+
+            }
+
+            memcpy(ldelim_start + 1, mdelim_pos + 1, second_len);
+
+            if (first_len != second_len) {
+
+              memmove(mdelim_pos + first_len + 1, rdelim_end,
+                      out_buf_end - rdelim_end);
+
+            }
+
+            memcpy(mdelim_pos + 1, scratch, first_len);
+
+            return 1;
+
+          }
+
+        }
+
+      }
+
+    }
+
+  }
+
+  return 0;
+
+}
+
+/* replace a string */
+
+static u32 string_replace(u8 **out_buf, s32 *temp_len, u32 pos, u8 *from,
+                          u8 *to) {
+
+  u8 *start = strnstr(*out_buf + pos, from, *temp_len - pos);
+
+  if (start) {
+
+    u32 from_len = strlen(from);
+    u32 to_len = strlen(to);
+
+    if (from_len != to_len) {
+
+      memmove(start + to_len, start + from_len,
+              *temp_len - from_len - (start - *out_buf));
+
+    }
+
+    memcpy(start, to, to_len);
+    *temp_len = (*temp_len - from_len + to_len);
+
+    return 1;
+
+  }
+
+  return 0;
+
+}
+
+/* Returns 1 if a mutant was generated and placed in out_buf, 0 if none
+ * generated. */
+
+static int text_mutation(afl_state_t *afl, u8 **out_buf, s32 *orig_temp_len) {
+
+  s32 temp_len;
+  u32 pos, yes = 0,
+           mutations = rand_below(afl, AFL_TXT_STRING_MAX_MUTATIONS) + 1;
+  u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch),
+                              *orig_temp_len + AFL_TXT_STRING_MAX_MUTATIONS);
+  temp_len = *orig_temp_len;
+  memcpy(new_buf, *out_buf, temp_len);
+
+  for (u32 i = 0; i < mutations; i++) {
+
+    if (temp_len < AFL_TXT_MIN_LEN) { return 0; }
+
+    pos = rand_below(afl, temp_len - 1);
+    int choice = rand_below(afl, 72);
+    switch (choice) {
+
+      case 0:                                /* Semantic statement deletion */
+        yes += string_replace(out_buf, &temp_len, pos, "\n", "#");
+        break;
+      case 1:
+        yes += string_replace(out_buf, &temp_len, pos, "(", "(!");
+        break;
+      case 2:
+        yes += string_replace(out_buf, &temp_len, pos, "==", "!=");
+        break;
+      case 3:
+        yes += string_replace(out_buf, &temp_len, pos, "!=", "==");
+        break;
+      case 4:
+        yes += string_replace(out_buf, &temp_len, pos, "==", "<");
+        break;
+      case 5:
+        yes += string_replace(out_buf, &temp_len, pos, "<", "==");
+        break;
+      case 6:
+        yes += string_replace(out_buf, &temp_len, pos, "==", ">");
+        break;
+      case 7:
+        yes += string_replace(out_buf, &temp_len, pos, ">", "==");
+        break;
+      case 8:
+        yes += string_replace(out_buf, &temp_len, pos, "=", "<");
+        break;
+      case 9:
+        yes += string_replace(out_buf, &temp_len, pos, "=", ">");
+        break;
+      case 10:
+        yes += string_replace(out_buf, &temp_len, pos, "<", ">");
+        break;
+      case 11:
+        yes += string_replace(out_buf, &temp_len, pos, ">", "<");
+        break;
+      case 12:
+        yes += string_replace(out_buf, &temp_len, pos, "++", "--");
+        break;
+      case 13:
+        yes += string_replace(out_buf, &temp_len, pos, "--", "++");
+        break;
+      case 14:
+        yes += string_replace(out_buf, &temp_len, pos, "+", "-");
+        break;
+      case 15:
+        yes += string_replace(out_buf, &temp_len, pos, "+", "*");
+        break;
+      case 16:
+        yes += string_replace(out_buf, &temp_len, pos, "+", "/");
+        break;
+      case 17:
+        yes += string_replace(out_buf, &temp_len, pos, "+", "%");
+        break;
+      case 18:
+        yes += string_replace(out_buf, &temp_len, pos, "*", "-");
+        break;
+      case 19:
+        yes += string_replace(out_buf, &temp_len, pos, "*", "+");
+        break;
+      case 20:
+        yes += string_replace(out_buf, &temp_len, pos, "*", "/");
+        break;
+      case 21:
+        yes += string_replace(out_buf, &temp_len, pos, "*", "%");
+        break;
+      case 22:
+        yes += string_replace(out_buf, &temp_len, pos, "-", "+");
+        break;
+      case 23:
+        yes += string_replace(out_buf, &temp_len, pos, "-", "*");
+        break;
+      case 24:
+        yes += string_replace(out_buf, &temp_len, pos, "-", "/");
+        break;
+      case 25:
+        yes += string_replace(out_buf, &temp_len, pos, "-", "%");
+        break;
+      case 26:
+        yes += string_replace(out_buf, &temp_len, pos, "/", "-");
+        break;
+      case 27:
+        yes += string_replace(out_buf, &temp_len, pos, "/", "*");
+        break;
+      case 28:
+        yes += string_replace(out_buf, &temp_len, pos, "/", "+");
+        break;
+      case 29:
+        yes += string_replace(out_buf, &temp_len, pos, "/", "%");
+        break;
+      case 30:
+        yes += string_replace(out_buf, &temp_len, pos, "%", "-");
+        break;
+      case 31:
+        yes += string_replace(out_buf, &temp_len, pos, "%", "*");
+        break;
+      case 32:
+        yes += string_replace(out_buf, &temp_len, pos, "%", "/");
+        break;
+      case 33:
+        yes += string_replace(out_buf, &temp_len, pos, "%", "+");
+        break;
+      case 34:
+        yes += string_replace(out_buf, &temp_len, pos, " ", "|");
+        break;
+      case 35:
+        yes += string_replace(out_buf, &temp_len, pos, " ", "$");
+        break;
+      case 36:
+        yes += string_replace(out_buf, &temp_len, pos, "0", "1");
+        break;
+      case 37:
+        yes += string_replace(out_buf, &temp_len, pos, "1", "0");
+        break;
+      case 38:
+        yes += string_replace(out_buf, &temp_len, pos, " ", "`");
+        break;
+      case 39:
+        yes += string_replace(out_buf, &temp_len, pos, " ", "\"");
+        break;
+      case 40:
+        yes += string_replace(out_buf, &temp_len, pos, ";", " ");
+        break;
+      case 41:
+        yes += string_replace(out_buf, &temp_len, pos, "&&", "||");
+        break;
+      case 42:
+        yes += string_replace(out_buf, &temp_len, pos, "||", "&&");
+        break;
+      case 43:
+        yes += string_replace(out_buf, &temp_len, pos, "!", "");
+        break;
+      case 44:
+        yes += string_replace(out_buf, &temp_len, pos, "==", "=");
+        break;
+      case 45:
+        yes += string_replace(out_buf, &temp_len, pos, "--", "");
+        break;
+      case 46:
+        yes += string_replace(out_buf, &temp_len, pos, "<<", "<");
+        break;
+      case 47:
+        yes += string_replace(out_buf, &temp_len, pos, ">>", ">");
+        break;
+      case 48:
+        yes += string_replace(out_buf, &temp_len, pos, "<", "<<");
+        break;
+      case 49:
+        yes += string_replace(out_buf, &temp_len, pos, ">", ">>");
+        break;
+      case 50:
+        yes += string_replace(out_buf, &temp_len, pos, "\"", "'");
+        break;
+      case 51:
+        yes += string_replace(out_buf, &temp_len, pos, "'", "\"");
+        break;
+      case 52:
+        yes += string_replace(out_buf, &temp_len, pos, "(", "\"");
+        break;
+      case 53:  /* Remove a semicolon delimited statement after a semicolon */
+        yes += delim_replace(out_buf, &temp_len, pos, ";", ";", ";");
+        break;
+      case 54: /* Remove a semicolon delimited statement after a left curly
+                  brace */
+        yes += delim_replace(out_buf, &temp_len, pos, "}", ";", "}");
+        break;
+      case 55:                            /* Remove a curly brace construct */
+        yes += delim_replace(out_buf, &temp_len, pos, "{", "}", "");
+        break;
+      case 56:         /* Replace a curly brace construct with an empty one */
+        yes += delim_replace(out_buf, &temp_len, pos, "{", "}", "{}");
+        break;
+      case 57:
+        yes += delim_swap(out_buf, &temp_len, pos, ";", ";", ";");
+        break;
+      case 58:
+        yes += delim_swap(out_buf, &temp_len, pos, "}", ";", ";");
+        break;
+      case 59:                        /* Swap comma delimited things case 1 */
+        yes += delim_swap(out_buf, &temp_len, pos, "(", ",", ")");
+        break;
+      case 60:                        /* Swap comma delimited things case 2 */
+        yes += delim_swap(out_buf, &temp_len, pos, "(", ",", ",");
+        break;
+      case 61:                        /* Swap comma delimited things case 3 */
+        yes += delim_swap(out_buf, &temp_len, pos, ",", ",", ",");
+        break;
+      case 62:                        /* Swap comma delimited things case 4 */
+        yes += delim_swap(out_buf, &temp_len, pos, ",", ",", ")");
+        break;
+      case 63:                                        /* Just delete a line */
+        yes += delim_replace(out_buf, &temp_len, pos, "\n", "\n", "");
+        break;
+      case 64:                      /* Delete something like "const" case 1 */
+        yes += delim_replace(out_buf, &temp_len, pos, " ", " ", "");
+        break;
+      case 65:                      /* Delete something like "const" case 2 */
+        yes += delim_replace(out_buf, &temp_len, pos, "\n", " ", "");
+        break;
+      case 66:                      /* Delete something like "const" case 3 */
+        yes += delim_replace(out_buf, &temp_len, pos, "(", " ", "");
+        break;
+      case 67:                        /* Swap space delimited things case 1 */
+        yes += delim_swap(out_buf, &temp_len, pos, " ", " ", " ");
+        break;
+      case 68:                        /* Swap space delimited things case 2 */
+        yes += delim_swap(out_buf, &temp_len, pos, " ", " ", ")");
+        break;
+      case 69:                        /* Swap space delimited things case 3 */
+        yes += delim_swap(out_buf, &temp_len, pos, "(", " ", " ");
+        break;
+      case 70:                        /* Swap space delimited things case 4 */
+        yes += delim_swap(out_buf, &temp_len, pos, "(", " ", ")");
+        break;
+      case 71:                           /* Duplicate a single line of code */
+        yes += delim_replace(out_buf, &temp_len, pos, "\n", "\n", NULL);
+        break;
+      case 72:  /* Duplicate a construct (most often, a non-nested for loop */
+        yes += delim_replace(out_buf, &temp_len, pos, "\n", "}", NULL);
+        break;
+
+    }
+
+  }
+
+  if (yes == 0 || temp_len <= 0) { return 0; }
+
+  swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
+  *out_buf = new_buf;
+  *orig_temp_len = temp_len;
+
+  return 1;
+
+}
+
 /* Take the current entry from the queue, fuzz it for a while. This
    function is a tad too long... returns 0 if fuzzed successfully, 1 if
    skipped or bailed out. */
@@ -1854,6 +2303,22 @@ havoc_stage:
   /* We essentially just do several thousand runs (depending on perf_score)
      where we take the input file and make random stacked tweaks. */
 
+  u32 r_max, r;
+
+  if (unlikely(afl->expand_havoc)) {
+
+    /* add expensive havoc cases here, they are activated after a full
+       cycle without finds happened */
+
+    r_max = 16 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0) +
+            (afl->queue_cur->is_ascii ? AFL_TXT_BIAS : 0);
+
+  } else {
+
+    r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0);
+
+  }
+
   for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) {
 
     u32 use_stacking = 1 << (1 + rand_below(afl, HAVOC_STACK_POW2));
@@ -1896,8 +2361,9 @@ havoc_stage:
 
       }
 
-      switch (rand_below(
-          afl, 16 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0))) {
+    retry_havoc:
+
+      switch ((r = rand_below(afl, r_max))) {
 
         case 0:
 
@@ -2191,176 +2657,203 @@ havoc_stage:
           break;
 
         }
-        
-        case 15: {
 
-          /* Overwrite bytes with a randomly selected chunk from another
-             testcase or insert that chunk. */
+        default:
 
-          if (afl->queued_paths < 2) break;
+          if (likely(r <= 16 && (afl->extras_cnt || afl->a_extras_cnt))) {
 
-          /* Pick a random queue entry and seek to it. */
+            /* Values 15 and 16 can be selected only if there are any extras
+               present in the dictionaries. */
 
-          u32 tid;
-          do
-            tid = rand_below(afl, afl->queued_paths);
-          while (tid == afl->current_entry);
+            if (r == 15) {
 
-          struct queue_entry* target = afl->queue_buf[tid];
+              /* Overwrite bytes with an extra. */
 
-          /* Make sure that the target has a reasonable length. */
+              if (!afl->extras_cnt ||
+                  (afl->a_extras_cnt && rand_below(afl, 2))) {
 
-          while (target && (target->len < 2 || target == afl->queue_cur))
-            target = target->next;
+                /* No user-specified extras or odds in our favor. Let's use an
+                   auto-detected one. */
 
-          if (!target) break;
+                u32 use_extra = rand_below(afl, afl->a_extras_cnt);
+                u32 extra_len = afl->a_extras[use_extra].len;
+                u32 insert_at;
 
-          /* Read the testcase into a new buffer. */
+                if (extra_len > temp_len) { break; }
 
-          fd = open(target->fname, O_RDONLY);
+                insert_at = rand_below(afl, temp_len - extra_len + 1);
+                memcpy(out_buf + insert_at, afl->a_extras[use_extra].data,
+                       extra_len);
 
-          if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); }
+              } else {
 
-          u32 new_len = target->len;
-          u8 * new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len);
+                /* No auto extras or odds in our favor. Use the dictionary. */
 
-          ck_read(fd, new_buf, new_len, target->fname);
+                u32 use_extra = rand_below(afl, afl->extras_cnt);
+                u32 extra_len = afl->extras[use_extra].len;
+                u32 insert_at;
 
-          close(fd);
+                if (extra_len > temp_len) { break; }
 
-          u8 overwrite = 0;
-          if (temp_len >= 2 && rand_below(afl, 2))
-            overwrite = 1;
-          else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) {
-            if (temp_len >= 2) overwrite = 1;
-            else break;
-          }
+                insert_at = rand_below(afl, temp_len - extra_len + 1);
+                memcpy(out_buf + insert_at, afl->extras[use_extra].data,
+                       extra_len);
 
-          if (overwrite) {
+              }
 
-            u32 copy_from, copy_to, copy_len;
+              break;
 
-            copy_len = choose_block_len(afl, new_len - 1);
-            if (copy_len > temp_len) copy_len = temp_len;
+            } else {  // case 16
 
-            copy_from = rand_below(afl, new_len - copy_len + 1);
-            copy_to = rand_below(afl, temp_len - copy_len + 1);
+              u32 use_extra, extra_len,
+                  insert_at = rand_below(afl, temp_len + 1);
+              u8 *ptr;
 
-            memmove(out_buf + copy_to, new_buf + copy_from, copy_len);
+              /* Insert an extra. Do the same dice-rolling stuff as for the
+                 previous case. */
 
-          } else {
-          
-            u32 clone_from, clone_to, clone_len;
-            
-            clone_len = choose_block_len(afl, new_len);
-            clone_from = rand_below(afl, new_len - clone_len + 1);
-          
-            clone_to = rand_below(afl, temp_len);
+              if (!afl->extras_cnt ||
+                  (afl->a_extras_cnt && rand_below(afl, 2))) {
 
-            u8 * temp_buf =
-                ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len);
+                use_extra = rand_below(afl, afl->a_extras_cnt);
+                extra_len = afl->a_extras[use_extra].len;
+                ptr = afl->a_extras[use_extra].data;
 
-            /* Head */
+              } else {
 
-            memcpy(temp_buf, out_buf, clone_to);
+                use_extra = rand_below(afl, afl->extras_cnt);
+                extra_len = afl->extras[use_extra].len;
+                ptr = afl->extras[use_extra].data;
 
-            /* Inserted part */
+              }
 
-            memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len);
+              if (temp_len + extra_len >= MAX_FILE) { break; }
 
-            /* Tail */
-            memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to,
-                   temp_len - clone_to);
+              out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len);
 
-            swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
-            out_buf = temp_buf;
-            temp_len += clone_len;
-          
-          }
+              /* Tail */
+              memmove(out_buf + insert_at + extra_len, out_buf + insert_at,
+                      temp_len - insert_at);
 
-          break;
+              /* Inserted part */
+              memcpy(out_buf + insert_at, ptr, extra_len);
 
-        }
+              temp_len += extra_len;
 
-          /* Values 15 and 16 can be selected only if there are any extras
-             present in the dictionaries. */
+              break;
 
-        case 16: {
+            }
 
-          /* Overwrite bytes with an extra. */
+          } else
 
-          if (!afl->extras_cnt || (afl->a_extras_cnt && rand_below(afl, 2))) {
+            switch (r) {
 
-            /* No user-specified extras or odds in our favor. Let's use an
-               auto-detected one. */
+              case 15:  // fall through
+              case 17: {
 
-            u32 use_extra = rand_below(afl, afl->a_extras_cnt);
-            u32 extra_len = afl->a_extras[use_extra].len;
-            u32 insert_at;
+                /* Overwrite bytes with a randomly selected chunk from another
+                   testcase or insert that chunk. */
 
-            if (extra_len > temp_len) { break; }
+                if (afl->queued_paths < 2) break;
 
-            insert_at = rand_below(afl, temp_len - extra_len + 1);
-            memcpy(out_buf + insert_at, afl->a_extras[use_extra].data,
-                   extra_len);
+                /* Pick a random queue entry and seek to it. */
 
-          } else {
+                u32 tid;
+                do
+                  tid = rand_below(afl, afl->queued_paths);
+                while (tid == afl->current_entry);
 
-            /* No auto extras or odds in our favor. Use the dictionary. */
+                struct queue_entry *target = afl->queue_buf[tid];
 
-            u32 use_extra = rand_below(afl, afl->extras_cnt);
-            u32 extra_len = afl->extras[use_extra].len;
-            u32 insert_at;
+                /* Make sure that the target has a reasonable length. */
 
-            if (extra_len > temp_len) { break; }
+                while (target && (target->len < 2 || target == afl->queue_cur))
+                  target = target->next;
 
-            insert_at = rand_below(afl, temp_len - extra_len + 1);
-            memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len);
+                if (!target) break;
 
-          }
+                /* Read the testcase into a new buffer. */
 
-          break;
+                fd = open(target->fname, O_RDONLY);
 
-        }
+                if (unlikely(fd < 0)) {
+
+                  PFATAL("Unable to open '%s'", target->fname);
 
-        case 17: {
+                }
 
-          u32 use_extra, extra_len, insert_at = rand_below(afl, temp_len + 1);
-          u8 *ptr;
+                u32 new_len = target->len;
+                u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len);
 
-          /* Insert an extra. Do the same dice-rolling stuff as for the
-             previous case. */
+                ck_read(fd, new_buf, new_len, target->fname);
 
-          if (!afl->extras_cnt || (afl->a_extras_cnt && rand_below(afl, 2))) {
+                close(fd);
 
-            use_extra = rand_below(afl, afl->a_extras_cnt);
-            extra_len = afl->a_extras[use_extra].len;
-            ptr = afl->a_extras[use_extra].data;
+                u8 overwrite = 0;
+                if (temp_len >= 2 && rand_below(afl, 2))
+                  overwrite = 1;
+                else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) {
 
-          } else {
+                  if (temp_len >= 2)
+                    overwrite = 1;
+                  else
+                    break;
 
-            use_extra = rand_below(afl, afl->extras_cnt);
-            extra_len = afl->extras[use_extra].len;
-            ptr = afl->extras[use_extra].data;
+                }
 
-          }
+                if (overwrite) {
 
-          if (temp_len + extra_len >= MAX_FILE) { break; }
+                  u32 copy_from, copy_to, copy_len;
 
-          out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len);
+                  copy_len = choose_block_len(afl, new_len - 1);
+                  if (copy_len > temp_len) copy_len = temp_len;
 
-          /* Tail */
-          memmove(out_buf + insert_at + extra_len, out_buf + insert_at,
-                  temp_len - insert_at);
+                  copy_from = rand_below(afl, new_len - copy_len + 1);
+                  copy_to = rand_below(afl, temp_len - copy_len + 1);
 
-          /* Inserted part */
-          memcpy(out_buf + insert_at, ptr, extra_len);
+                  memmove(out_buf + copy_to, new_buf + copy_from, copy_len);
 
-          temp_len += extra_len;
+                } else {
 
-          break;
+                  u32 clone_from, clone_to, clone_len;
 
-        }
+                  clone_len = choose_block_len(afl, new_len);
+                  clone_from = rand_below(afl, new_len - clone_len + 1);
+
+                  clone_to = rand_below(afl, temp_len);
+
+                  u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch),
+                                               temp_len + clone_len);
+
+                  /* Head */
+
+                  memcpy(temp_buf, out_buf, clone_to);
+
+                  /* Inserted part */
+
+                  memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len);
+
+                  /* Tail */
+                  memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to,
+                         temp_len - clone_to);
+
+                  swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
+                  out_buf = temp_buf;
+                  temp_len += clone_len;
+
+                }
+
+                break;
+
+              }
+
+              default:
+
+                // perform ascii mutations
+                if (text_mutation(afl, &out_buf, &temp_len) == 0)
+                  goto retry_havoc;
+
+            }  // end default: switch(r)
 
       }
 
@@ -4827,7 +5320,7 @@ u8 fuzz_one(afl_state_t *afl) {
 
   return (key_val_lv_1 | key_val_lv_2);
 
-#undef BUF_PARAMS
-
 }
 
+#undef BUF_PARAMS
+
diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c
index a96995e5..56073b0a 100644
--- a/src/afl-fuzz-queue.c
+++ b/src/afl-fuzz-queue.c
@@ -24,6 +24,7 @@
 
 #include "afl-fuzz.h"
 #include <limits.h>
+#include <ctype.h>
 
 #define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size
 
@@ -102,6 +103,108 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) {
 
 }
 
+/* check if ascii or UTF-8 */
+
+static u8 check_if_text(struct queue_entry *q) {
+
+  if (q->len < AFL_TXT_MIN_LEN) return 0;
+
+  u8  buf[MAX_FILE];
+  s32 fd, len = q->len, offset = 0, ascii = 0, utf8 = 0, comp;
+
+  if ((fd = open(q->fname, O_RDONLY)) < 0) return 0;
+  if ((comp = read(fd, buf, len)) != len) return 0;
+  close(fd);
+
+  while (offset < len) {
+
+    // ASCII: <= 0x7F to allow ASCII control characters
+    if ((buf[offset + 0] == 0x09 || buf[offset + 0] == 0x0A ||
+         buf[offset + 0] == 0x0D ||
+         (0x20 <= buf[offset + 0] && buf[offset + 0] <= 0x7E))) {
+
+      offset++;
+      utf8++;
+      ascii++;
+      continue;
+
+    }
+
+    if (isascii((int)buf[offset]) || isprint((int)buf[offset])) {
+
+      ascii++;
+      // we continue though as it can also be a valid utf8
+
+    }
+
+    // non-overlong 2-byte
+    if (((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) &&
+         (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF))) {
+
+      offset += 2;
+      utf8++;
+      comp--;
+      continue;
+
+    }
+
+    // excluding overlongs
+    if ((buf[offset + 0] == 0xE0 &&
+         (0xA0 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) &&
+         (0x80 <= buf[offset + 2] &&
+          buf[offset + 2] <= 0xBF)) ||  // straight 3-byte
+        (((0xE1 <= buf[offset + 0] && buf[offset + 0] <= 0xEC) ||
+          buf[offset + 0] == 0xEE || buf[offset + 0] == 0xEF) &&
+         (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) &&
+         (0x80 <= buf[offset + 2] &&
+          buf[offset + 2] <= 0xBF)) ||  // excluding surrogates
+        (buf[offset + 0] == 0xED &&
+         (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x9F) &&
+         (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF))) {
+
+      offset += 3;
+      utf8++;
+      comp -= 2;
+      continue;
+
+    }
+
+    // planes 1-3
+    if ((buf[offset + 0] == 0xF0 &&
+         (0x90 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) &&
+         (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) &&
+         (0x80 <= buf[offset + 3] &&
+          buf[offset + 3] <= 0xBF)) ||  // planes 4-15
+        ((0xF1 <= buf[offset + 0] && buf[offset + 0] <= 0xF3) &&
+         (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) &&
+         (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) &&
+         (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF)) ||  // plane 16
+        (buf[offset + 0] == 0xF4 &&
+         (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x8F) &&
+         (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) &&
+         (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF))) {
+
+      offset += 4;
+      utf8++;
+      comp -= 3;
+      continue;
+
+    }
+
+    offset++;
+
+  }
+
+  u32 percent_utf8 = (utf8 * 100) / comp;
+  u32 percent_ascii = (ascii * 100) / len;
+
+  if (percent_utf8 >= percent_ascii && percent_utf8 >= AFL_TXT_MIN_PERCENT)
+    return 2;
+  if (percent_ascii >= AFL_TXT_MIN_PERCENT) return 1;
+  return 0;
+
+}
+
 /* Append new test case to the queue. */
 
 void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
@@ -139,9 +242,10 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
     afl->q_prev100 = q;
 
   }
-  
-  struct queue_entry** queue_buf = ck_maybe_grow(BUF_PARAMS(queue), afl->queued_paths * sizeof(struct queue_entry*));
-  queue_buf[afl->queued_paths -1] = q;
+
+  struct queue_entry **queue_buf = ck_maybe_grow(
+      BUF_PARAMS(queue), afl->queued_paths * sizeof(struct queue_entry *));
+  queue_buf[afl->queued_paths - 1] = q;
 
   afl->last_path_time = get_cur_time();
 
@@ -164,6 +268,8 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
 
   }
 
+  q->is_ascii = check_if_text(q);
+
 }
 
 /* Destroy the entire queue. */
diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c
index e56d122a..f68a79e8 100644
--- a/src/afl-fuzz-state.c
+++ b/src/afl-fuzz-state.c
@@ -293,6 +293,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
             afl->afl_env.afl_autoresume =
                 get_afl_env(afl_environment_variables[i]) ? 1 : 0;
 
+          } else if (!strncmp(env, "AFL_CYCLE_SCHEDULES",
+
+                              afl_environment_variable_len)) {
+
+            afl->cycle_schedules = afl->afl_env.afl_cycle_schedules =
+                get_afl_env(afl_environment_variables[i]) ? 1 : 0;
+
           } else if (!strncmp(env, "AFL_CAL_FAST",
 
                               afl_environment_variable_len)) {
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index df2896d2..88f8e902 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -1252,11 +1252,42 @@ int main(int argc, char **argv_orig, char **envp) {
       /* If we had a full queue cycle with no new finds, try
          recombination strategies next. */
 
-      if (afl->queued_paths == prev_queued) {
+      if (afl->queued_paths == prev_queued &&
+          (get_cur_time() - afl->start_time) >= 3600) {
 
         if (afl->use_splicing) {
 
           ++afl->cycles_wo_finds;
+          switch (afl->expand_havoc) {
+
+            case 0:
+              afl->expand_havoc = 1;
+              break;
+            case 1:
+              if (afl->limit_time_sig == 0) {
+
+                afl->limit_time_sig = -1;
+                afl->limit_time_puppet = 0;
+
+              }
+
+              afl->expand_havoc = 2;
+              break;
+            case 2:
+              afl->cycle_schedules = 1;
+              afl->expand_havoc = 3;
+              break;
+            case 3:
+              // nothing else currently
+              break;
+
+          }
+
+          if (afl->expand_havoc) {
+
+          } else
+
+            afl->expand_havoc = 1;
 
         } else {
 
@@ -1270,6 +1301,53 @@ int main(int argc, char **argv_orig, char **envp) {
 
       }
 
+      if (afl->cycle_schedules) {
+
+        /* we cannot mix non-AFLfast schedules with others */
+
+        switch (afl->schedule) {
+
+          case EXPLORE:
+            afl->schedule = EXPLOIT;
+            break;
+          case EXPLOIT:
+            afl->schedule = MMOPT;
+            break;
+          case MMOPT:
+            afl->schedule = SEEK;
+            break;
+          case SEEK:
+            afl->schedule = EXPLORE;
+            break;
+          case FAST:
+            afl->schedule = COE;
+            break;
+          case COE:
+            afl->schedule = LIN;
+            break;
+          case LIN:
+            afl->schedule = QUAD;
+            break;
+          case QUAD:
+            afl->schedule = RARE;
+            break;
+          case RARE:
+            afl->schedule = FAST;
+            break;
+
+        }
+
+        struct queue_entry *q = afl->queue;
+        // we must recalculate the scores of all queue entries
+        while (q) {
+
+          update_bitmap_score(afl, q);
+          q = q->next;
+
+        }
+
+      }
+
       prev_queued = afl->queued_paths;
 
       if (afl->sync_id && afl->queue_cycle == 1 &&