about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/afl-fuzz.h46
-rw-r--r--src/afl-fuzz-src/afl-fuzz.c615
-rw-r--r--src/afl-fuzz-src/extras.c484
-rw-r--r--src/afl-fuzz-src/misc.c152
4 files changed, 667 insertions, 630 deletions
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index 22a78373..c50a21a7 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -468,31 +468,47 @@ void trim_py(char**, size_t*);
 
 /* Queue */
 
-void mark_as_det_done(struct queue_entry* q);
-void mark_as_variable(struct queue_entry* q);
-void mark_as_redundant(struct queue_entry* q, u8 state);
-void add_to_queue(u8* fname, u32 len, u8 passed_det);
+void mark_as_det_done(struct queue_entry*);
+void mark_as_variable(struct queue_entry*);
+void mark_as_redundant(struct queue_entry*, u8);
+void add_to_queue(u8*, u32, u8);
 void destroy_queue(void);
-void update_bitmap_score(struct queue_entry* q);
+void update_bitmap_score(struct queue_entry*);
 void cull_queue(void);
 
 /* Bitmap */
 
 void write_bitmap(void);
-void read_bitmap(u8* fname);
-u8 has_new_bits(u8* virgin_map);
-u32 count_bits(u8* mem);
-u32 count_bytes(u8* mem);
-u32 count_non_255_bytes(u8* mem);
+void read_bitmap(u8*);
+u8 has_new_bits(u8*);
+u32 count_bits(u8*);
+u32 count_bytes(u8*);
+u32 count_non_255_bytes(u8*);
 #ifdef __x86_64__
-void simplify_trace(u64* mem);
-void classify_counts(u64* mem);
+void simplify_trace(u64*);
+void classify_counts(u64*);
 #else
-void simplify_trace(u32* mem);
-void classify_counts(u32* mem);
+void simplify_trace(u32*);
+void classify_counts(u32*);
 #endif
 void init_count_class16(void);
-void minimize_bits(u8* dst, u8* src);
+void minimize_bits(u8*, u8*);
+
+/* Misc */
+
+u8* DI(u64);
+u8* DF(double);
+u8* DMS(u64);
+u8* DTD(u64, u64);
+
+/* Extras */
+
+void load_extras_file(u8*, u32*, u32*, u32);
+void load_extras(u8*);
+void maybe_add_auto(u8*, u32);
+void save_auto(void);
+void load_auto(void);
+void destroy_extras(void);
 
 /**** Inline routines ****/
 
diff --git a/src/afl-fuzz-src/afl-fuzz.c b/src/afl-fuzz-src/afl-fuzz.c
index dcb97387..7cc05a39 100644
--- a/src/afl-fuzz-src/afl-fuzz.c
+++ b/src/afl-fuzz-src/afl-fuzz.c
@@ -211,159 +211,6 @@ static void locate_diffs(u8* ptr1, u8* ptr2, u32 len, s32* first, s32* last) {
 #endif /* !IGNORE_FINDS */
 
 
-/* Describe integer. Uses 12 cyclic static buffers for return values. The value
-   returned should be five characters or less for all the integers we reasonably
-   expect to see. */
-
-static u8* DI(u64 val) {
-
-  static u8 tmp[12][16];
-  static u8 cur;
-
-  cur = (cur + 1) % 12;
-
-#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) do { \
-    if (val < (_divisor) * (_limit_mult)) { \
-      sprintf(tmp[cur], _fmt, ((_cast)val) / (_divisor)); \
-      return tmp[cur]; \
-    } \
-  } while (0)
-
-  /* 0-9999 */
-  CHK_FORMAT(1, 10000, "%llu", u64);
-
-  /* 10.0k - 99.9k */
-  CHK_FORMAT(1000, 99.95, "%0.01fk", double);
-
-  /* 100k - 999k */
-  CHK_FORMAT(1000, 1000, "%lluk", u64);
-
-  /* 1.00M - 9.99M */
-  CHK_FORMAT(1000 * 1000, 9.995, "%0.02fM", double);
-
-  /* 10.0M - 99.9M */
-  CHK_FORMAT(1000 * 1000, 99.95, "%0.01fM", double);
-
-  /* 100M - 999M */
-  CHK_FORMAT(1000 * 1000, 1000, "%lluM", u64);
-
-  /* 1.00G - 9.99G */
-  CHK_FORMAT(1000LL * 1000 * 1000, 9.995, "%0.02fG", double);
-
-  /* 10.0G - 99.9G */
-  CHK_FORMAT(1000LL * 1000 * 1000, 99.95, "%0.01fG", double);
-
-  /* 100G - 999G */
-  CHK_FORMAT(1000LL * 1000 * 1000, 1000, "%lluG", u64);
-
-  /* 1.00T - 9.99G */
-  CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 9.995, "%0.02fT", double);
-
-  /* 10.0T - 99.9T */
-  CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 99.95, "%0.01fT", double);
-
-  /* 100T+ */
-  strcpy(tmp[cur], "infty");
-  return tmp[cur];
-
-}
-
-
-/* Describe float. Similar to the above, except with a single 
-   static buffer. */
-
-static u8* DF(double val) {
-
-  static u8 tmp[16];
-
-  if (val < 99.995) {
-    sprintf(tmp, "%0.02f", val);
-    return tmp;
-  }
-
-  if (val < 999.95) {
-    sprintf(tmp, "%0.01f", val);
-    return tmp;
-  }
-
-  return DI((u64)val);
-
-}
-
-
-/* Describe integer as memory size. */
-
-static u8* DMS(u64 val) {
-
-  static u8 tmp[12][16];
-  static u8 cur;
-
-  cur = (cur + 1) % 12;
-
-  /* 0-9999 */
-  CHK_FORMAT(1, 10000, "%llu B", u64);
-
-  /* 10.0k - 99.9k */
-  CHK_FORMAT(1024, 99.95, "%0.01f kB", double);
-
-  /* 100k - 999k */
-  CHK_FORMAT(1024, 1000, "%llu kB", u64);
-
-  /* 1.00M - 9.99M */
-  CHK_FORMAT(1024 * 1024, 9.995, "%0.02f MB", double);
-
-  /* 10.0M - 99.9M */
-  CHK_FORMAT(1024 * 1024, 99.95, "%0.01f MB", double);
-
-  /* 100M - 999M */
-  CHK_FORMAT(1024 * 1024, 1000, "%llu MB", u64);
-
-  /* 1.00G - 9.99G */
-  CHK_FORMAT(1024LL * 1024 * 1024, 9.995, "%0.02f GB", double);
-
-  /* 10.0G - 99.9G */
-  CHK_FORMAT(1024LL * 1024 * 1024, 99.95, "%0.01f GB", double);
-
-  /* 100G - 999G */
-  CHK_FORMAT(1024LL * 1024 * 1024, 1000, "%llu GB", u64);
-
-  /* 1.00T - 9.99G */
-  CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 9.995, "%0.02f TB", double);
-
-  /* 10.0T - 99.9T */
-  CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 99.95, "%0.01f TB", double);
-
-#undef CHK_FORMAT
-
-  /* 100T+ */
-  strcpy(tmp[cur], "infty");
-  return tmp[cur];
-
-}
-
-
-/* Describe time delta. Returns one static buffer, 34 chars of less. */
-
-static u8* DTD(u64 cur_ms, u64 event_ms) {
-
-  static u8 tmp[64];
-  u64 delta;
-  s32 t_d, t_h, t_m, t_s;
-
-  if (!event_ms) return "none seen yet";
-
-  delta = cur_ms - event_ms;
-
-  t_d = delta / 1000 / 60 / 60 / 24;
-  t_h = (delta / 1000 / 60 / 60) % 24;
-  t_m = (delta / 1000 / 60) % 60;
-  t_s = (delta / 1000) % 60;
-
-  sprintf(tmp, "%s days, %d hrs, %d min, %d sec", DI(t_d), t_h, t_m, t_s);
-  return tmp;
-
-}
-
 
 /* Load postprocessor, if available. */
 
@@ -516,468 +363,6 @@ static void read_testcases(void) {
 }
 
 
-/* Helper function for load_extras. */
-
-static int compare_extras_len(const void* p1, const void* p2) {
-  struct extra_data *e1 = (struct extra_data*)p1,
-                    *e2 = (struct extra_data*)p2;
-
-  return e1->len - e2->len;
-}
-
-static int compare_extras_use_d(const void* p1, const void* p2) {
-  struct extra_data *e1 = (struct extra_data*)p1,
-                    *e2 = (struct extra_data*)p2;
-
-  return e2->hit_cnt - e1->hit_cnt;
-}
-
-
-/* Read extras from a file, sort by size. */
-
-static void load_extras_file(u8* fname, u32* min_len, u32* max_len,
-                             u32 dict_level) {
-
-  FILE* f;
-  u8  buf[MAX_LINE];
-  u8  *lptr;
-  u32 cur_line = 0;
-
-  f = fopen(fname, "r");
-
-  if (!f) PFATAL("Unable to open '%s'", fname);
-
-  while ((lptr = fgets(buf, MAX_LINE, f))) {
-
-    u8 *rptr, *wptr;
-    u32 klen = 0;
-
-    ++cur_line;
-
-    /* Trim on left and right. */
-
-    while (isspace(*lptr)) ++lptr;
-
-    rptr = lptr + strlen(lptr) - 1;
-    while (rptr >= lptr && isspace(*rptr)) --rptr;
-    ++rptr;
-    *rptr = 0;
-
-    /* Skip empty lines and comments. */
-
-    if (!*lptr || *lptr == '#') continue;
-
-    /* All other lines must end with '"', which we can consume. */
-
-    --rptr;
-
-    if (rptr < lptr || *rptr != '"')
-      FATAL("Malformed name=\"value\" pair in line %u.", cur_line);
-
-    *rptr = 0;
-
-    /* Skip alphanumerics and dashes (label). */
-
-    while (isalnum(*lptr) || *lptr == '_') ++lptr;
-
-    /* If @number follows, parse that. */
-
-    if (*lptr == '@') {
-
-      ++lptr;
-      if (atoi(lptr) > dict_level) continue;
-      while (isdigit(*lptr)) ++lptr;
-
-    }
-
-    /* Skip whitespace and = signs. */
-
-    while (isspace(*lptr) || *lptr == '=') ++lptr;
-
-    /* Consume opening '"'. */
-
-    if (*lptr != '"')
-      FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line);
-
-    ++lptr;
-
-    if (!*lptr) FATAL("Empty keyword in line %u.", cur_line);
-
-    /* Okay, let's allocate memory and copy data between "...", handling
-       \xNN escaping, \\, and \". */
-
-    extras = ck_realloc_block(extras, (extras_cnt + 1) *
-               sizeof(struct extra_data));
-
-    wptr = extras[extras_cnt].data = ck_alloc(rptr - lptr);
-
-    while (*lptr) {
-
-      char* hexdigits = "0123456789abcdef";
-
-      switch (*lptr) {
-
-        case 1 ... 31:
-        case 128 ... 255:
-          FATAL("Non-printable characters in line %u.", cur_line);
-
-        case '\\':
-
-          ++lptr;
-
-          if (*lptr == '\\' || *lptr == '"') {
-            *(wptr++) = *(lptr++);
-            klen++;
-            break;
-          }
-
-          if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2]))
-            FATAL("Invalid escaping (not \\xNN) in line %u.", cur_line);
-
-          *(wptr++) =
-            ((strchr(hexdigits, tolower(lptr[1])) - hexdigits) << 4) |
-            (strchr(hexdigits, tolower(lptr[2])) - hexdigits);
-
-          lptr += 3;
-          ++klen;
-
-          break;
-
-        default:
-
-          *(wptr++) = *(lptr++);
-          ++klen;
-
-      }
-
-    }
-
-    extras[extras_cnt].len = klen;
-
-    if (extras[extras_cnt].len > MAX_DICT_FILE)
-      FATAL("Keyword too big in line %u (%s, limit is %s)", cur_line,
-            DMS(klen), DMS(MAX_DICT_FILE));
-
-    if (*min_len > klen) *min_len = klen;
-    if (*max_len < klen) *max_len = klen;
-
-    ++extras_cnt;
-
-  }
-
-  fclose(f);
-
-}
-
-
-/* Read extras from the extras directory and sort them by size. */
-
-static void load_extras(u8* dir) {
-
-  DIR* d;
-  struct dirent* de;
-  u32 min_len = MAX_DICT_FILE, max_len = 0, dict_level = 0;
-  u8* x;
-
-  /* If the name ends with @, extract level and continue. */
-
-  if ((x = strchr(dir, '@'))) {
-
-    *x = 0;
-    dict_level = atoi(x + 1);
-
-  }
-
-  ACTF("Loading extra dictionary from '%s' (level %u)...", dir, dict_level);
-
-  d = opendir(dir);
-
-  if (!d) {
-
-    if (errno == ENOTDIR) {
-      load_extras_file(dir, &min_len, &max_len, dict_level);
-      goto check_and_sort;
-    }
-
-    PFATAL("Unable to open '%s'", dir);
-
-  }
-
-  if (x) FATAL("Dictionary levels not supported for directories.");
-
-  while ((de = readdir(d))) {
-
-    struct stat st;
-    u8* fn = alloc_printf("%s/%s", dir, de->d_name);
-    s32 fd;
-
-    if (lstat(fn, &st) || access(fn, R_OK))
-      PFATAL("Unable to access '%s'", fn);
-
-    /* This also takes care of . and .. */
-    if (!S_ISREG(st.st_mode) || !st.st_size) {
-
-      ck_free(fn);
-      continue;
-
-    }
-
-    if (st.st_size > MAX_DICT_FILE)
-      FATAL("Extra '%s' is too big (%s, limit is %s)", fn,
-            DMS(st.st_size), DMS(MAX_DICT_FILE));
-
-    if (min_len > st.st_size) min_len = st.st_size;
-    if (max_len < st.st_size) max_len = st.st_size;
-
-    extras = ck_realloc_block(extras, (extras_cnt + 1) *
-               sizeof(struct extra_data));
-
-    extras[extras_cnt].data = ck_alloc(st.st_size);
-    extras[extras_cnt].len  = st.st_size;
-
-    fd = open(fn, O_RDONLY);
-
-    if (fd < 0) PFATAL("Unable to open '%s'", fn);
-
-    ck_read(fd, extras[extras_cnt].data, st.st_size, fn);
-
-    close(fd);
-    ck_free(fn);
-
-    ++extras_cnt;
-
-  }
-
-  closedir(d);
-
-check_and_sort:
-
-  if (!extras_cnt) FATAL("No usable files in '%s'", dir);
-
-  qsort(extras, extras_cnt, sizeof(struct extra_data), compare_extras_len);
-
-  OKF("Loaded %u extra tokens, size range %s to %s.", extras_cnt,
-      DMS(min_len), DMS(max_len));
-
-  if (max_len > 32)
-    WARNF("Some tokens are relatively large (%s) - consider trimming.",
-          DMS(max_len));
-
-  if (extras_cnt > MAX_DET_EXTRAS)
-    WARNF("More than %d tokens - will use them probabilistically.",
-          MAX_DET_EXTRAS);
-
-}
-
-
-
-
-/* Helper function for maybe_add_auto() */
-
-static inline u8 memcmp_nocase(u8* m1, u8* m2, u32 len) {
-
-  while (len--) if (tolower(*(m1++)) ^ tolower(*(m2++))) return 1;
-  return 0;
-
-}
-
-
-/* Maybe add automatic extra. */
-
-static void maybe_add_auto(u8* mem, u32 len) {
-
-  u32 i;
-
-  /* Allow users to specify that they don't want auto dictionaries. */
-
-  if (!MAX_AUTO_EXTRAS || !USE_AUTO_EXTRAS) return;
-
-  /* Skip runs of identical bytes. */
-
-  for (i = 1; i < len; ++i)
-    if (mem[0] ^ mem[i]) break;
-
-  if (i == len) return;
-
-  /* Reject builtin interesting values. */
-
-  if (len == 2) {
-
-    i = sizeof(interesting_16) >> 1;
-
-    while (i--) 
-      if (*((u16*)mem) == interesting_16[i] ||
-          *((u16*)mem) == SWAP16(interesting_16[i])) return;
-
-  }
-
-  if (len == 4) {
-
-    i = sizeof(interesting_32) >> 2;
-
-    while (i--) 
-      if (*((u32*)mem) == interesting_32[i] ||
-          *((u32*)mem) == SWAP32(interesting_32[i])) return;
-
-  }
-
-  /* Reject anything that matches existing extras. Do a case-insensitive
-     match. We optimize by exploiting the fact that extras[] are sorted
-     by size. */
-
-  for (i = 0; i < extras_cnt; ++i)
-    if (extras[i].len >= len) break;
-
-  for (; i < extras_cnt && extras[i].len == len; ++i)
-    if (!memcmp_nocase(extras[i].data, mem, len)) return;
-
-  /* Last but not least, check a_extras[] for matches. There are no
-     guarantees of a particular sort order. */
-
-  auto_changed = 1;
-
-  for (i = 0; i < a_extras_cnt; ++i) {
-
-    if (a_extras[i].len == len && !memcmp_nocase(a_extras[i].data, mem, len)) {
-
-      a_extras[i].hit_cnt++;
-      goto sort_a_extras;
-
-    }
-
-  }
-
-  /* At this point, looks like we're dealing with a new entry. So, let's
-     append it if we have room. Otherwise, let's randomly evict some other
-     entry from the bottom half of the list. */
-
-  if (a_extras_cnt < MAX_AUTO_EXTRAS) {
-
-    a_extras = ck_realloc_block(a_extras, (a_extras_cnt + 1) *
-                                sizeof(struct extra_data));
-
-    a_extras[a_extras_cnt].data = ck_memdup(mem, len);
-    a_extras[a_extras_cnt].len  = len;
-    ++a_extras_cnt;
-
-  } else {
-
-    i = MAX_AUTO_EXTRAS / 2 +
-        UR((MAX_AUTO_EXTRAS + 1) / 2);
-
-    ck_free(a_extras[i].data);
-
-    a_extras[i].data    = ck_memdup(mem, len);
-    a_extras[i].len     = len;
-    a_extras[i].hit_cnt = 0;
-
-  }
-
-sort_a_extras:
-
-  /* First, sort all auto extras by use count, descending order. */
-
-  qsort(a_extras, a_extras_cnt, sizeof(struct extra_data),
-        compare_extras_use_d);
-
-  /* Then, sort the top USE_AUTO_EXTRAS entries by size. */
-
-  qsort(a_extras, MIN(USE_AUTO_EXTRAS, a_extras_cnt),
-        sizeof(struct extra_data), compare_extras_len);
-
-}
-
-
-/* Save automatically generated extras. */
-
-static void save_auto(void) {
-
-  u32 i;
-
-  if (!auto_changed) return;
-  auto_changed = 0;
-
-  for (i = 0; i < MIN(USE_AUTO_EXTRAS, a_extras_cnt); ++i) {
-
-    u8* fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", out_dir, i);
-    s32 fd;
-
-    fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600);
-
-    if (fd < 0) PFATAL("Unable to create '%s'", fn);
-
-    ck_write(fd, a_extras[i].data, a_extras[i].len, fn);
-
-    close(fd);
-    ck_free(fn);
-
-  }
-
-}
-
-
-/* Load automatically generated extras. */
-
-static void load_auto(void) {
-
-  u32 i;
-
-  for (i = 0; i < USE_AUTO_EXTRAS; ++i) {
-
-    u8  tmp[MAX_AUTO_EXTRA + 1];
-    u8* fn = alloc_printf("%s/.state/auto_extras/auto_%06u", in_dir, i);
-    s32 fd, len;
-
-    fd = open(fn, O_RDONLY, 0600);
-
-    if (fd < 0) {
-
-      if (errno != ENOENT) PFATAL("Unable to open '%s'", fn);
-      ck_free(fn);
-      break;
-
-    }
-
-    /* We read one byte more to cheaply detect tokens that are too
-       long (and skip them). */
-
-    len = read(fd, tmp, MAX_AUTO_EXTRA + 1);
-
-    if (len < 0) PFATAL("Unable to read from '%s'", fn);
-
-    if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA)
-      maybe_add_auto(tmp, len);
-
-    close(fd);
-    ck_free(fn);
-
-  }
-
-  if (i) OKF("Loaded %u auto-discovered dictionary tokens.", i);
-  else OKF("No auto-generated dictionary tokens to reuse.");
-
-}
-
-
-/* Destroy extras. */
-
-static void destroy_extras(void) {
-
-  u32 i;
-
-  for (i = 0; i < extras_cnt; ++i) 
-    ck_free(extras[i].data);
-
-  ck_free(extras);
-
-  for (i = 0; i < a_extras_cnt; ++i) 
-    ck_free(a_extras[i].data);
-
-  ck_free(a_extras);
-
-}
-
-
 /* Execute target application, monitoring for timeouts. Return status
    information. The called program will update trace_bits[]. */
 
diff --git a/src/afl-fuzz-src/extras.c b/src/afl-fuzz-src/extras.c
new file mode 100644
index 00000000..1f52181d
--- /dev/null
+++ b/src/afl-fuzz-src/extras.c
@@ -0,0 +1,484 @@
+/*
+   american fuzzy lop - fuzzer code
+   --------------------------------
+
+   Written and maintained by Michal Zalewski <lcamtuf@google.com>
+
+   Forkserver design by Jann Horn <jannhorn@googlemail.com>
+
+   Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. All rights reserved.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at:
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   This is the real deal: the program takes an instrumented binary and
+   attempts a variety of basic fuzzing tricks, paying close attention to
+   how they affect the execution path.
+
+ */
+
+#include "afl-fuzz.h"
+
+
+/* Helper function for load_extras. */
+
+static int compare_extras_len(const void* p1, const void* p2) {
+  struct extra_data *e1 = (struct extra_data*)p1,
+                    *e2 = (struct extra_data*)p2;
+
+  return e1->len - e2->len;
+}
+
+static int compare_extras_use_d(const void* p1, const void* p2) {
+  struct extra_data *e1 = (struct extra_data*)p1,
+                    *e2 = (struct extra_data*)p2;
+
+  return e2->hit_cnt - e1->hit_cnt;
+}
+
+
+/* Read extras from a file, sort by size. */
+
+void load_extras_file(u8* fname, u32* min_len, u32* max_len, u32 dict_level) {
+
+  FILE* f;
+  u8  buf[MAX_LINE];
+  u8  *lptr;
+  u32 cur_line = 0;
+
+  f = fopen(fname, "r");
+
+  if (!f) PFATAL("Unable to open '%s'", fname);
+
+  while ((lptr = fgets(buf, MAX_LINE, f))) {
+
+    u8 *rptr, *wptr;
+    u32 klen = 0;
+
+    ++cur_line;
+
+    /* Trim on left and right. */
+
+    while (isspace(*lptr)) ++lptr;
+
+    rptr = lptr + strlen(lptr) - 1;
+    while (rptr >= lptr && isspace(*rptr)) --rptr;
+    ++rptr;
+    *rptr = 0;
+
+    /* Skip empty lines and comments. */
+
+    if (!*lptr || *lptr == '#') continue;
+
+    /* All other lines must end with '"', which we can consume. */
+
+    --rptr;
+
+    if (rptr < lptr || *rptr != '"')
+      FATAL("Malformed name=\"value\" pair in line %u.", cur_line);
+
+    *rptr = 0;
+
+    /* Skip alphanumerics and dashes (label). */
+
+    while (isalnum(*lptr) || *lptr == '_') ++lptr;
+
+    /* If @number follows, parse that. */
+
+    if (*lptr == '@') {
+
+      ++lptr;
+      if (atoi(lptr) > dict_level) continue;
+      while (isdigit(*lptr)) ++lptr;
+
+    }
+
+    /* Skip whitespace and = signs. */
+
+    while (isspace(*lptr) || *lptr == '=') ++lptr;
+
+    /* Consume opening '"'. */
+
+    if (*lptr != '"')
+      FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line);
+
+    ++lptr;
+
+    if (!*lptr) FATAL("Empty keyword in line %u.", cur_line);
+
+    /* Okay, let's allocate memory and copy data between "...", handling
+       \xNN escaping, \\, and \". */
+
+    extras = ck_realloc_block(extras, (extras_cnt + 1) *
+               sizeof(struct extra_data));
+
+    wptr = extras[extras_cnt].data = ck_alloc(rptr - lptr);
+
+    while (*lptr) {
+
+      char* hexdigits = "0123456789abcdef";
+
+      switch (*lptr) {
+
+        case 1 ... 31:
+        case 128 ... 255:
+          FATAL("Non-printable characters in line %u.", cur_line);
+
+        case '\\':
+
+          ++lptr;
+
+          if (*lptr == '\\' || *lptr == '"') {
+            *(wptr++) = *(lptr++);
+            klen++;
+            break;
+          }
+
+          if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2]))
+            FATAL("Invalid escaping (not \\xNN) in line %u.", cur_line);
+
+          *(wptr++) =
+            ((strchr(hexdigits, tolower(lptr[1])) - hexdigits) << 4) |
+            (strchr(hexdigits, tolower(lptr[2])) - hexdigits);
+
+          lptr += 3;
+          ++klen;
+
+          break;
+
+        default:
+
+          *(wptr++) = *(lptr++);
+          ++klen;
+
+      }
+
+    }
+
+    extras[extras_cnt].len = klen;
+
+    if (extras[extras_cnt].len > MAX_DICT_FILE)
+      FATAL("Keyword too big in line %u (%s, limit is %s)", cur_line,
+            DMS(klen), DMS(MAX_DICT_FILE));
+
+    if (*min_len > klen) *min_len = klen;
+    if (*max_len < klen) *max_len = klen;
+
+    ++extras_cnt;
+
+  }
+
+  fclose(f);
+
+}
+
+
+/* Read extras from the extras directory and sort them by size. */
+
+void load_extras(u8* dir) {
+
+  DIR* d;
+  struct dirent* de;
+  u32 min_len = MAX_DICT_FILE, max_len = 0, dict_level = 0;
+  u8* x;
+
+  /* If the name ends with @, extract level and continue. */
+
+  if ((x = strchr(dir, '@'))) {
+
+    *x = 0;
+    dict_level = atoi(x + 1);
+
+  }
+
+  ACTF("Loading extra dictionary from '%s' (level %u)...", dir, dict_level);
+
+  d = opendir(dir);
+
+  if (!d) {
+
+    if (errno == ENOTDIR) {
+      load_extras_file(dir, &min_len, &max_len, dict_level);
+      goto check_and_sort;
+    }
+
+    PFATAL("Unable to open '%s'", dir);
+
+  }
+
+  if (x) FATAL("Dictionary levels not supported for directories.");
+
+  while ((de = readdir(d))) {
+
+    struct stat st;
+    u8* fn = alloc_printf("%s/%s", dir, de->d_name);
+    s32 fd;
+
+    if (lstat(fn, &st) || access(fn, R_OK))
+      PFATAL("Unable to access '%s'", fn);
+
+    /* This also takes care of . and .. */
+    if (!S_ISREG(st.st_mode) || !st.st_size) {
+
+      ck_free(fn);
+      continue;
+
+    }
+
+    if (st.st_size > MAX_DICT_FILE)
+      FATAL("Extra '%s' is too big (%s, limit is %s)", fn,
+            DMS(st.st_size), DMS(MAX_DICT_FILE));
+
+    if (min_len > st.st_size) min_len = st.st_size;
+    if (max_len < st.st_size) max_len = st.st_size;
+
+    extras = ck_realloc_block(extras, (extras_cnt + 1) *
+               sizeof(struct extra_data));
+
+    extras[extras_cnt].data = ck_alloc(st.st_size);
+    extras[extras_cnt].len  = st.st_size;
+
+    fd = open(fn, O_RDONLY);
+
+    if (fd < 0) PFATAL("Unable to open '%s'", fn);
+
+    ck_read(fd, extras[extras_cnt].data, st.st_size, fn);
+
+    close(fd);
+    ck_free(fn);
+
+    ++extras_cnt;
+
+  }
+
+  closedir(d);
+
+check_and_sort:
+
+  if (!extras_cnt) FATAL("No usable files in '%s'", dir);
+
+  qsort(extras, extras_cnt, sizeof(struct extra_data), compare_extras_len);
+
+  OKF("Loaded %u extra tokens, size range %s to %s.", extras_cnt,
+      DMS(min_len), DMS(max_len));
+
+  if (max_len > 32)
+    WARNF("Some tokens are relatively large (%s) - consider trimming.",
+          DMS(max_len));
+
+  if (extras_cnt > MAX_DET_EXTRAS)
+    WARNF("More than %d tokens - will use them probabilistically.",
+          MAX_DET_EXTRAS);
+
+}
+
+
+
+/* Helper function for maybe_add_auto() */
+
+static inline u8 memcmp_nocase(u8* m1, u8* m2, u32 len) {
+
+  while (len--) if (tolower(*(m1++)) ^ tolower(*(m2++))) return 1;
+  return 0;
+
+}
+
+
+/* Maybe add automatic extra. */
+
+void maybe_add_auto(u8* mem, u32 len) {
+
+  u32 i;
+
+  /* Allow users to specify that they don't want auto dictionaries. */
+
+  if (!MAX_AUTO_EXTRAS || !USE_AUTO_EXTRAS) return;
+
+  /* Skip runs of identical bytes. */
+
+  for (i = 1; i < len; ++i)
+    if (mem[0] ^ mem[i]) break;
+
+  if (i == len) return;
+
+  /* Reject builtin interesting values. */
+
+  if (len == 2) {
+
+    i = sizeof(interesting_16) >> 1;
+
+    while (i--) 
+      if (*((u16*)mem) == interesting_16[i] ||
+          *((u16*)mem) == SWAP16(interesting_16[i])) return;
+
+  }
+
+  if (len == 4) {
+
+    i = sizeof(interesting_32) >> 2;
+
+    while (i--) 
+      if (*((u32*)mem) == interesting_32[i] ||
+          *((u32*)mem) == SWAP32(interesting_32[i])) return;
+
+  }
+
+  /* Reject anything that matches existing extras. Do a case-insensitive
+     match. We optimize by exploiting the fact that extras[] are sorted
+     by size. */
+
+  for (i = 0; i < extras_cnt; ++i)
+    if (extras[i].len >= len) break;
+
+  for (; i < extras_cnt && extras[i].len == len; ++i)
+    if (!memcmp_nocase(extras[i].data, mem, len)) return;
+
+  /* Last but not least, check a_extras[] for matches. There are no
+     guarantees of a particular sort order. */
+
+  auto_changed = 1;
+
+  for (i = 0; i < a_extras_cnt; ++i) {
+
+    if (a_extras[i].len == len && !memcmp_nocase(a_extras[i].data, mem, len)) {
+
+      a_extras[i].hit_cnt++;
+      goto sort_a_extras;
+
+    }
+
+  }
+
+  /* At this point, looks like we're dealing with a new entry. So, let's
+     append it if we have room. Otherwise, let's randomly evict some other
+     entry from the bottom half of the list. */
+
+  if (a_extras_cnt < MAX_AUTO_EXTRAS) {
+
+    a_extras = ck_realloc_block(a_extras, (a_extras_cnt + 1) *
+                                sizeof(struct extra_data));
+
+    a_extras[a_extras_cnt].data = ck_memdup(mem, len);
+    a_extras[a_extras_cnt].len  = len;
+    ++a_extras_cnt;
+
+  } else {
+
+    i = MAX_AUTO_EXTRAS / 2 +
+        UR((MAX_AUTO_EXTRAS + 1) / 2);
+
+    ck_free(a_extras[i].data);
+
+    a_extras[i].data    = ck_memdup(mem, len);
+    a_extras[i].len     = len;
+    a_extras[i].hit_cnt = 0;
+
+  }
+
+sort_a_extras:
+
+  /* First, sort all auto extras by use count, descending order. */
+
+  qsort(a_extras, a_extras_cnt, sizeof(struct extra_data),
+        compare_extras_use_d);
+
+  /* Then, sort the top USE_AUTO_EXTRAS entries by size. */
+
+  qsort(a_extras, MIN(USE_AUTO_EXTRAS, a_extras_cnt),
+        sizeof(struct extra_data), compare_extras_len);
+
+}
+
+
+/* Save automatically generated extras. */
+
+void save_auto(void) {
+
+  u32 i;
+
+  if (!auto_changed) return;
+  auto_changed = 0;
+
+  for (i = 0; i < MIN(USE_AUTO_EXTRAS, a_extras_cnt); ++i) {
+
+    u8* fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", out_dir, i);
+    s32 fd;
+
+    fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+
+    if (fd < 0) PFATAL("Unable to create '%s'", fn);
+
+    ck_write(fd, a_extras[i].data, a_extras[i].len, fn);
+
+    close(fd);
+    ck_free(fn);
+
+  }
+
+}
+
+
+/* Load automatically generated extras. */
+
+void load_auto(void) {
+
+  u32 i;
+
+  for (i = 0; i < USE_AUTO_EXTRAS; ++i) {
+
+    u8  tmp[MAX_AUTO_EXTRA + 1];
+    u8* fn = alloc_printf("%s/.state/auto_extras/auto_%06u", in_dir, i);
+    s32 fd, len;
+
+    fd = open(fn, O_RDONLY, 0600);
+
+    if (fd < 0) {
+
+      if (errno != ENOENT) PFATAL("Unable to open '%s'", fn);
+      ck_free(fn);
+      break;
+
+    }
+
+    /* We read one byte more to cheaply detect tokens that are too
+       long (and skip them). */
+
+    len = read(fd, tmp, MAX_AUTO_EXTRA + 1);
+
+    if (len < 0) PFATAL("Unable to read from '%s'", fn);
+
+    if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA)
+      maybe_add_auto(tmp, len);
+
+    close(fd);
+    ck_free(fn);
+
+  }
+
+  if (i) OKF("Loaded %u auto-discovered dictionary tokens.", i);
+  else OKF("No auto-generated dictionary tokens to reuse.");
+
+}
+
+
+/* Destroy extras. */
+
+void destroy_extras(void) {
+
+  u32 i;
+
+  for (i = 0; i < extras_cnt; ++i) 
+    ck_free(extras[i].data);
+
+  ck_free(extras);
+
+  for (i = 0; i < a_extras_cnt; ++i) 
+    ck_free(a_extras[i].data);
+
+  ck_free(a_extras);
+
+}
+
diff --git a/src/afl-fuzz-src/misc.c b/src/afl-fuzz-src/misc.c
index 58e57c8f..69ff2f6b 100644
--- a/src/afl-fuzz-src/misc.c
+++ b/src/afl-fuzz-src/misc.c
@@ -22,3 +22,155 @@
 
 #include "afl-fuzz.h"
 
+/* Describe integer. Uses 12 cyclic static buffers for return values. The value
+   returned should be five characters or less for all the integers we reasonably
+   expect to see. */
+
+u8* DI(u64 val) {
+
+  static u8 tmp[12][16];
+  static u8 cur;
+
+  cur = (cur + 1) % 12;
+
+#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) do { \
+    if (val < (_divisor) * (_limit_mult)) { \
+      sprintf(tmp[cur], _fmt, ((_cast)val) / (_divisor)); \
+      return tmp[cur]; \
+    } \
+  } while (0)
+
+  /* 0-9999 */
+  CHK_FORMAT(1, 10000, "%llu", u64);
+
+  /* 10.0k - 99.9k */
+  CHK_FORMAT(1000, 99.95, "%0.01fk", double);
+
+  /* 100k - 999k */
+  CHK_FORMAT(1000, 1000, "%lluk", u64);
+
+  /* 1.00M - 9.99M */
+  CHK_FORMAT(1000 * 1000, 9.995, "%0.02fM", double);
+
+  /* 10.0M - 99.9M */
+  CHK_FORMAT(1000 * 1000, 99.95, "%0.01fM", double);
+
+  /* 100M - 999M */
+  CHK_FORMAT(1000 * 1000, 1000, "%lluM", u64);
+
+  /* 1.00G - 9.99G */
+  CHK_FORMAT(1000LL * 1000 * 1000, 9.995, "%0.02fG", double);
+
+  /* 10.0G - 99.9G */
+  CHK_FORMAT(1000LL * 1000 * 1000, 99.95, "%0.01fG", double);
+
+  /* 100G - 999G */
+  CHK_FORMAT(1000LL * 1000 * 1000, 1000, "%lluG", u64);
+
+  /* 1.00T - 9.99G */
+  CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 9.995, "%0.02fT", double);
+
+  /* 10.0T - 99.9T */
+  CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 99.95, "%0.01fT", double);
+
+  /* 100T+ */
+  strcpy(tmp[cur], "infty");
+  return tmp[cur];
+
+}
+
+
+/* Describe float. Similar to the above, except with a single 
+   static buffer. */
+
+u8* DF(double val) {
+
+  static u8 tmp[16];
+
+  if (val < 99.995) {
+    sprintf(tmp, "%0.02f", val);
+    return tmp;
+  }
+
+  if (val < 999.95) {
+    sprintf(tmp, "%0.01f", val);
+    return tmp;
+  }
+
+  return DI((u64)val);
+
+}
+
+
+/* Describe integer as memory size. */
+
+u8* DMS(u64 val) {
+
+  static u8 tmp[12][16];
+  static u8 cur;
+
+  cur = (cur + 1) % 12;
+
+  /* 0-9999 */
+  CHK_FORMAT(1, 10000, "%llu B", u64);
+
+  /* 10.0k - 99.9k */
+  CHK_FORMAT(1024, 99.95, "%0.01f kB", double);
+
+  /* 100k - 999k */
+  CHK_FORMAT(1024, 1000, "%llu kB", u64);
+
+  /* 1.00M - 9.99M */
+  CHK_FORMAT(1024 * 1024, 9.995, "%0.02f MB", double);
+
+  /* 10.0M - 99.9M */
+  CHK_FORMAT(1024 * 1024, 99.95, "%0.01f MB", double);
+
+  /* 100M - 999M */
+  CHK_FORMAT(1024 * 1024, 1000, "%llu MB", u64);
+
+  /* 1.00G - 9.99G */
+  CHK_FORMAT(1024LL * 1024 * 1024, 9.995, "%0.02f GB", double);
+
+  /* 10.0G - 99.9G */
+  CHK_FORMAT(1024LL * 1024 * 1024, 99.95, "%0.01f GB", double);
+
+  /* 100G - 999G */
+  CHK_FORMAT(1024LL * 1024 * 1024, 1000, "%llu GB", u64);
+
+  /* 1.00T - 9.99G */
+  CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 9.995, "%0.02f TB", double);
+
+  /* 10.0T - 99.9T */
+  CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 99.95, "%0.01f TB", double);
+
+#undef CHK_FORMAT
+
+  /* 100T+ */
+  strcpy(tmp[cur], "infty");
+  return tmp[cur];
+
+}
+
+
+/* Describe time delta. Returns one static buffer, 34 chars of less. */
+
+u8* DTD(u64 cur_ms, u64 event_ms) {
+
+  static u8 tmp[64];
+  u64 delta;
+  s32 t_d, t_h, t_m, t_s;
+
+  if (!event_ms) return "none seen yet";
+
+  delta = cur_ms - event_ms;
+
+  t_d = delta / 1000 / 60 / 60 / 24;
+  t_h = (delta / 1000 / 60 / 60) % 24;
+  t_m = (delta / 1000 / 60) % 60;
+  t_s = (delta / 1000) % 60;
+
+  sprintf(tmp, "%s days, %d hrs, %d min, %d sec", DI(t_d), t_h, t_m, t_s);
+  return tmp;
+
+}