aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrea Fioraldi <andreafioraldi@gmail.com>2019-09-02 00:15:12 +0200
committerAndrea Fioraldi <andreafioraldi@gmail.com>2019-09-02 00:15:12 +0200
commitaf5fd8c819617f8232e9a78e6d41fa65ea2d7235 (patch)
tree9cb27bbff65b1520ef178515685297232b1ba440 /src
parent3b3df4e3cb0ce3e6ea728b68694b579e15cd00f7 (diff)
downloadafl++-af5fd8c819617f8232e9a78e6d41fa65ea2d7235.tar.gz
split afl-fuzz: extras
Diffstat (limited to 'src')
-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
3 files changed, 636 insertions, 615 deletions
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;
+
+}