diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/afl-fuzz-init.c | 154 | ||||
-rw-r--r-- | src/afl-fuzz-mutators.c | 5 | ||||
-rw-r--r-- | src/afl-fuzz-one.c | 250 | ||||
-rw-r--r-- | src/afl-fuzz-queue.c | 112 | ||||
-rw-r--r-- | src/afl-fuzz-redqueen.c | 102 | ||||
-rw-r--r-- | src/afl-fuzz-run.c | 2 | ||||
-rw-r--r-- | src/afl-fuzz-state.c | 15 | ||||
-rw-r--r-- | src/afl-fuzz-stats.c | 155 | ||||
-rw-r--r-- | src/afl-fuzz.c | 106 |
9 files changed, 757 insertions, 144 deletions
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 609e16ba..65ad0c9f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -438,6 +438,159 @@ static void shuffle_ptrs(afl_state_t *afl, void **ptrs, u32 cnt) { } +/* Read all testcases from foreign input directories, then queue them for + testing. Called at startup and at sync intervals. + Does not descend into subdirectories! */ + +void read_foreign_testcases(afl_state_t *afl, int first) { + + if (!afl->foreign_sync_cnt) return; + + struct dirent **nl; + s32 nl_cnt; + u32 i, iter; + + u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; + + for (iter = 0; iter < afl->foreign_sync_cnt; iter++) { + + if (afl->foreign_syncs[iter].dir != NULL && + afl->foreign_syncs[iter].dir[0] != 0) { + + if (first) ACTF("Scanning '%s'...", afl->foreign_syncs[iter].dir); + time_t ctime_max = 0; + + /* We use scandir() + alphasort() rather than readdir() because otherwise, + the ordering of test cases would vary somewhat randomly and would be + difficult to control. */ + + nl_cnt = scandir(afl->foreign_syncs[iter].dir, &nl, NULL, NULL); + + if (nl_cnt < 0) { + + if (first) { + + WARNF("Unable to open directory '%s'", afl->foreign_syncs[iter].dir); + sleep(1); + + } + + continue; + + } + + if (nl_cnt == 0) { + + if (first) + WARNF("directory %s is currently empty", + afl->foreign_syncs[iter].dir); + continue; + + } + + /* Show stats */ + + snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "foreign sync %u", iter); + + afl->stage_name = afl->stage_name_buf; + afl->stage_cur = 0; + afl->stage_max = 0; + + for (i = 0; i < nl_cnt; ++i) { + + struct stat st; + + u8 *fn2 = + alloc_printf("%s/%s", afl->foreign_syncs[iter].dir, nl[i]->d_name); + + free(nl[i]); /* not tracked */ + + if (unlikely(lstat(fn2, &st) || access(fn2, R_OK))) { + + if (first) PFATAL("Unable to access '%s'", fn2); + continue; + + } + + /* we detect new files by their ctime */ + if (likely(st.st_ctime <= afl->foreign_syncs[iter].ctime)) { + + ck_free(fn2); + continue; + + } + + /* This also takes care of . and .. */ + + if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn2, "/README.txt")) { + + ck_free(fn2); + continue; + + } + + if (st.st_size > MAX_FILE) { + + if (first) + WARNF( + "Test case '%s' is too big (%s, limit is %s), skipping", fn2, + stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size), + stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE)); + ck_free(fn2); + continue; + + } + + // lets do not use add_to_queue(afl, fn2, st.st_size, 0); + // as this could add duplicates of the startup input corpus + + int fd = open(fn2, O_RDONLY); + if (fd < 0) { + + ck_free(fn2); + continue; + + } + + u8 fault; + u8 *mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + + if (mem == MAP_FAILED) { + + ck_free(fn2); + continue; + + } + + write_to_testcase(afl, mem, st.st_size); + fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); + afl->syncing_party = "foreign"; + afl->queued_imported += + save_if_interesting(afl, mem, st.st_size, fault); + afl->syncing_party = 0; + munmap(mem, st.st_size); + close(fd); + + if (st.st_ctime > ctime_max) ctime_max = st.st_ctime; + + } + + afl->foreign_syncs[iter].ctime = ctime_max; + free(nl); /* not tracked */ + + } + + } + + if (first) { + + afl->last_path_time = 0; + afl->queued_at_start = afl->queued_paths; + + } + +} + /* Read all testcases from the input directory, then queue them for testing. Called at startup. */ @@ -530,6 +683,7 @@ void read_testcases(afl_state_t *afl) { WARNF("Test case '%s' is too big (%s, limit is %s), skipping", fn2, stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size), stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE)); + ck_free(fn2); continue; } diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 17a68ff8..b288cf9f 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -40,7 +40,7 @@ void setup_custom_mutators(afl_state_t *afl) { if (fn) { - if (afl->limit_time_sig) + if (afl->limit_time_sig && afl->limit_time_sig != -1) FATAL( "MOpt and custom mutator are mutually exclusive. We accept pull " "requests that integrates MOpt with the optional mutators " @@ -168,7 +168,8 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { /* "afl_custom_deinit", optional for backward compatibility */ mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit"); - if (!mutator->afl_custom_deinit) FATAL("Symbol 'afl_custom_init' not found."); + if (!mutator->afl_custom_deinit) + FATAL("Symbol 'afl_custom_deinit' not found."); /* "afl_custom_post_process", optional */ mutator->afl_custom_post_process = dlsym(dh, "afl_custom_post_process"); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 72383727..1f0bf30e 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -24,6 +24,8 @@ */ #include "afl-fuzz.h" +#include <string.h> +#include <limits.h> /* MOpt */ @@ -362,6 +364,8 @@ 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 + /* 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 +1858,21 @@ 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); + + } 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 +1915,7 @@ havoc_stage: } - switch (rand_below( - afl, 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0))) { + switch ((r = rand_below(afl, r_max))) { case 0: @@ -2192,85 +2210,198 @@ havoc_stage: } - /* Values 15 and 16 can be selected only if there are any extras - present in the dictionaries. */ + default: - case 15: { + if (likely(r <= 16 && (afl->extras_cnt || afl->a_extras_cnt))) { - /* Overwrite bytes with an extra. */ + /* Values 15 and 16 can be selected only if there are any extras + present in the dictionaries. */ - if (!afl->extras_cnt || (afl->a_extras_cnt && rand_below(afl, 2))) { + if (r == 15) { - /* No user-specified extras or odds in our favor. Let's use an - auto-detected one. */ + /* Overwrite bytes with an extra. */ - u32 use_extra = rand_below(afl, afl->a_extras_cnt); - u32 extra_len = afl->a_extras[use_extra].len; - u32 insert_at; + if (!afl->extras_cnt || + (afl->a_extras_cnt && rand_below(afl, 2))) { - if (extra_len > temp_len) { break; } + /* No user-specified extras or odds in our favor. Let's use an + auto-detected one. */ - insert_at = rand_below(afl, temp_len - extra_len + 1); - memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, - extra_len); + u32 use_extra = rand_below(afl, afl->a_extras_cnt); + u32 extra_len = afl->a_extras[use_extra].len; + u32 insert_at; - } else { + if (extra_len > temp_len) { break; } - /* No auto extras or odds in our favor. Use the dictionary. */ + insert_at = rand_below(afl, temp_len - extra_len + 1); + memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, + extra_len); - u32 use_extra = rand_below(afl, afl->extras_cnt); - u32 extra_len = afl->extras[use_extra].len; - u32 insert_at; + } else { - if (extra_len > temp_len) { break; } + /* No auto extras or odds in our favor. Use the dictionary. */ - insert_at = rand_below(afl, temp_len - extra_len + 1); - memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); + u32 use_extra = rand_below(afl, afl->extras_cnt); + u32 extra_len = afl->extras[use_extra].len; + u32 insert_at; - } + if (extra_len > temp_len) { break; } - break; + insert_at = rand_below(afl, temp_len - extra_len + 1); + memcpy(out_buf + insert_at, afl->extras[use_extra].data, + extra_len); - } + } - case 16: { + break; - u32 use_extra, extra_len, insert_at = rand_below(afl, temp_len + 1); - u8 *ptr; + } else { // case 16 + + u32 use_extra, extra_len, + insert_at = rand_below(afl, temp_len + 1); + u8 *ptr; + + /* Insert an extra. Do the same dice-rolling stuff as for the + previous case. */ + + if (!afl->extras_cnt || + (afl->a_extras_cnt && rand_below(afl, 2))) { + + use_extra = rand_below(afl, afl->a_extras_cnt); + extra_len = afl->a_extras[use_extra].len; + ptr = afl->a_extras[use_extra].data; + + } else { + + use_extra = rand_below(afl, afl->extras_cnt); + extra_len = afl->extras[use_extra].len; + ptr = afl->extras[use_extra].data; + + } + + if (temp_len + extra_len >= MAX_FILE) { break; } + + out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + + /* Tail */ + memmove(out_buf + insert_at + extra_len, out_buf + insert_at, + temp_len - insert_at); + + /* Inserted part */ + memcpy(out_buf + insert_at, ptr, extra_len); - /* Insert an extra. Do the same dice-rolling stuff as for the - previous case. */ + temp_len += extra_len; - if (!afl->extras_cnt || (afl->a_extras_cnt && rand_below(afl, 2))) { + break; - use_extra = rand_below(afl, afl->a_extras_cnt); - extra_len = afl->a_extras[use_extra].len; - ptr = afl->a_extras[use_extra].data; + } } else { - use_extra = rand_below(afl, afl->extras_cnt); - extra_len = afl->extras[use_extra].len; - ptr = afl->extras[use_extra].data; + /* + switch (r) { - } + case 15: // fall through + case 16: + case 17: {*/ - if (temp_len + extra_len >= MAX_FILE) { break; } + /* Overwrite bytes with a randomly selected chunk from another + testcase or insert that chunk. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + if (afl->queued_paths < 4) break; - /* Tail */ - memmove(out_buf + insert_at + extra_len, out_buf + insert_at, - temp_len - insert_at); + /* Pick a random queue entry and seek to it. */ - /* Inserted part */ - memcpy(out_buf + insert_at, ptr, extra_len); + u32 tid; + do + tid = rand_below(afl, afl->queued_paths); + while (tid == afl->current_entry); - temp_len += extra_len; + struct queue_entry *target = afl->queue_buf[tid]; - break; + /* Make sure that the target has a reasonable length. */ - } + while (target && (target->len < 2 || target == afl->queue_cur)) + target = target->next; + + if (!target) break; + + /* Read the testcase into a new buffer. */ + + fd = open(target->fname, O_RDONLY); + + if (unlikely(fd < 0)) { + + PFATAL("Unable to open '%s'", target->fname); + + } + + u32 new_len = target->len; + u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); + + ck_read(fd, new_buf, new_len, target->fname); + + close(fd); + + 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; + + } + + if (overwrite) { + + u32 copy_from, copy_to, copy_len; + + copy_len = choose_block_len(afl, new_len - 1); + if (copy_len > temp_len) copy_len = temp_len; + + copy_from = rand_below(afl, new_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); + + memmove(out_buf + copy_to, new_buf + copy_from, copy_len); + + } 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); + + 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; + + } + + // end of default: } @@ -2357,20 +2488,7 @@ retry_splicing: } while (tid == afl->current_entry); afl->splicing_with = tid; - target = afl->queue; - - while (tid >= 100) { - - target = target->next_100; - tid -= 100; - - } - - while (tid--) { - - target = target->next; - - } + target = afl->queue_buf[tid]; /* Make sure that the target has a reasonable length. */ @@ -4750,7 +4868,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 7afdd9f1..38e95ac8 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -24,6 +24,9 @@ #include "afl-fuzz.h" #include <limits.h> +#include <ctype.h> + +#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size /* Mark deterministic checks as done for a particular queue entry. We use the .state file to avoid repeating deterministic fuzzing when resuming aborted @@ -100,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) { @@ -138,6 +243,10 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } + 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(); if (afl->custom_mutators_count) { @@ -159,6 +268,9 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } + /* only redqueen currently uses is_ascii */ + if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q); + } /* Destroy the entire queue. */ diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index c53e0e06..57e60c3d 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -24,6 +24,7 @@ */ +#include <limits.h> #include "afl-fuzz.h" #include "cmplog.h" @@ -262,6 +263,58 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { } +static long long strntoll(const char *str, size_t sz, char **end, int base) { + + char buf[64]; + long long ret; + const char *beg = str; + + for (; beg && sz && *beg == ' '; beg++, sz--) + ; + + if (!sz || sz >= sizeof(buf)) { + + if (end) *end = (char *)str; + return 0; + + } + + memcpy(buf, beg, sz); + buf[sz] = '\0'; + ret = strtoll(buf, end, base); + if (ret == LLONG_MIN || ret == LLONG_MAX) return ret; + if (end) *end = (char *)beg + (*end - buf); + return ret; + +} + +static unsigned long long strntoull(const char *str, size_t sz, char **end, + int base) { + + char buf[64]; + unsigned long long ret; + const char * beg = str; + + for (; beg && sz && *beg == ' '; beg++, sz--) + ; + + if (!sz || sz >= sizeof(buf)) { + + if (end) *end = (char *)str; + return 0; + + } + + memcpy(buf, beg, sz); + buf[sz] = '\0'; + ret = strtoull(buf, end, base); + if (end) *end = (char *)beg + (*end - buf); + return ret; + +} + +#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size + static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u64 pattern, u64 repl, u64 o_pattern, u32 idx, u8 *orig_buf, u8 *buf, u32 len, u8 do_reverse, @@ -279,7 +332,54 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u32 its_len = len - idx; // *status = 0; - if (SHAPE_BYTES(h->shape) >= 8) { + u8 * endptr; + u8 use_num = 0, use_unum = 0; + unsigned long long unum; + long long num; + if (afl->queue_cur->is_ascii) { + + endptr = buf_8; + num = strntoll(buf_8, len - idx, (char **)&endptr, 0); + if (endptr == buf_8) { + + unum = strntoull(buf_8, len - idx, (char **)&endptr, 0); + if (endptr == buf_8) use_unum = 1; + + } else + + use_num = 1; + + } + + if (use_num && num == pattern) { + + size_t old_len = endptr - buf_8; + size_t num_len = snprintf(NULL, 0, "%lld", num); + + u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), len + num_len); + memcpy(new_buf, buf, idx); + + snprintf(new_buf + idx, num_len, "%lld", num); + memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + + if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + + } else if (use_unum && unum == pattern) { + + size_t old_len = endptr - buf_8; + size_t num_len = snprintf(NULL, 0, "%llu", unum); + + u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), len + num_len); + memcpy(new_buf, buf, idx); + + snprintf(new_buf + idx, num_len, "%llu", unum); + memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + + if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + + } + + if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { if (its_len >= 8 && *buf_64 == pattern && *o_buf_64 == o_pattern) { diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index e4ddab1b..01963f8f 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -679,6 +679,8 @@ void sync_fuzzers(afl_state_t *afl) { } + if (afl->foreign_sync_cnt) read_foreign_testcases(afl, 0); + } /* Trim all new test cases to save cycles when doing deterministic checks. The diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index e0e43f54..66280ed1 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -293,6 +293,20 @@ 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_EXPAND_HAVOC_NOW", + + afl_environment_variable_len)) { + + afl->expand_havoc = afl->afl_env.afl_expand_havoc = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_CAL_FAST", afl_environment_variable_len)) { @@ -405,6 +419,7 @@ void afl_state_deinit(afl_state_t *afl) { if (afl->pass_stats) { ck_free(afl->pass_stats); } if (afl->orig_cmp_map) { ck_free(afl->orig_cmp_map); } + if (afl->queue_buf) { free(afl->queue_buf); } if (afl->out_buf) { free(afl->out_buf); } if (afl->out_scratch_buf) { free(afl->out_scratch_buf); } if (afl->eff_buf) { free(afl->eff_buf); } diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index fc93011b..7b30b5ea 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -39,7 +39,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, u8 fn[PATH_MAX]; s32 fd; FILE * f; - uint32_t t_bytes = count_non_255_bytes(afl, afl->virgin_bits); + u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); @@ -67,89 +67,102 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, } + if ((unlikely(!afl->last_avg_exec_update || + cur_time - afl->last_avg_exec_update >= 60000))) { + + afl->last_avg_execs_saved = + (float)(1000 * (afl->fsrv.total_execs - afl->last_avg_execs)) / + (float)(cur_time - afl->last_avg_exec_update); + afl->last_avg_execs = afl->fsrv.total_execs; + afl->last_avg_exec_update = cur_time; + + } + #ifndef __HAIKU__ if (getrusage(RUSAGE_CHILDREN, &rus)) { rus.ru_maxrss = 0; } #endif - fprintf( - f, - "start_time : %llu\n" - "last_update : %llu\n" - "run_time : %llu\n" - "fuzzer_pid : %u\n" - "cycles_done : %llu\n" - "cycles_wo_finds : %llu\n" - "execs_done : %llu\n" - "execs_per_sec : %0.02f\n" - // "real_execs_per_sec: %0.02f\n" // damn the name is too long - "paths_total : %u\n" - "paths_favored : %u\n" - "paths_found : %u\n" - "paths_imported : %u\n" - "max_depth : %u\n" - "cur_path : %u\n" /* Must match find_start_position() */ - "pending_favs : %u\n" - "pending_total : %u\n" - "variable_paths : %u\n" - "stability : %0.02f%%\n" - "bitmap_cvg : %0.02f%%\n" - "unique_crashes : %llu\n" - "unique_hangs : %llu\n" - "last_path : %llu\n" - "last_crash : %llu\n" - "last_hang : %llu\n" - "execs_since_crash : %llu\n" - "exec_timeout : %u\n" - "slowest_exec_ms : %u\n" - "peak_rss_mb : %lu\n" - "cpu_affinity : %d\n" - "edges_found : %u\n" - "var_byte_count : %u\n" - "afl_banner : %s\n" - "afl_version : " VERSION - "\n" - "target_mode : %s%s%s%s%s%s%s%s%s\n" - "command_line : %s\n", - afl->start_time / 1000, cur_time / 1000, - (cur_time - afl->start_time) / 1000, (u32)getpid(), - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds, - afl->fsrv.total_execs, - afl->fsrv.total_execs / - ((double)(get_cur_time() - afl->start_time) / 1000), - afl->queued_paths, afl->queued_favored, afl->queued_discovered, - afl->queued_imported, afl->max_depth, afl->current_entry, - afl->pending_favored, afl->pending_not_fuzzed, afl->queued_variable, - stability, bitmap_cvg, afl->unique_crashes, afl->unique_hangs, - afl->last_path_time / 1000, afl->last_crash_time / 1000, - afl->last_hang_time / 1000, afl->fsrv.total_execs - afl->last_crash_execs, - afl->fsrv.exec_tmout, afl->slowest_exec_ms, + fprintf(f, + "start_time : %llu\n" + "last_update : %llu\n" + "run_time : %llu\n" + "fuzzer_pid : %u\n" + "cycles_done : %llu\n" + "cycles_wo_finds : %llu\n" + "execs_done : %llu\n" + "execs_per_sec : %0.02f\n" + "execs_ps_last_min : %0.02f\n" + "paths_total : %u\n" + "paths_favored : %u\n" + "paths_found : %u\n" + "paths_imported : %u\n" + "max_depth : %u\n" + "cur_path : %u\n" /* Must match find_start_position() */ + "pending_favs : %u\n" + "pending_total : %u\n" + "variable_paths : %u\n" + "stability : %0.02f%%\n" + "bitmap_cvg : %0.02f%%\n" + "unique_crashes : %llu\n" + "unique_hangs : %llu\n" + "last_path : %llu\n" + "last_crash : %llu\n" + "last_hang : %llu\n" + "execs_since_crash : %llu\n" + "exec_timeout : %u\n" + "slowest_exec_ms : %u\n" + "peak_rss_mb : %lu\n" + "cpu_affinity : %d\n" + "edges_found : %u\n" + "var_byte_count : %u\n" + "havoc_expansion : %u\n" + "afl_banner : %s\n" + "afl_version : " VERSION + "\n" + "target_mode : %s%s%s%s%s%s%s%s%s\n" + "command_line : %s\n", + afl->start_time / 1000, cur_time / 1000, + (cur_time - afl->start_time) / 1000, (u32)getpid(), + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds, + afl->fsrv.total_execs, + afl->fsrv.total_execs / + ((double)(get_cur_time() - afl->start_time) / 1000), + afl->last_avg_execs_saved, afl->queued_paths, afl->queued_favored, + afl->queued_discovered, afl->queued_imported, afl->max_depth, + afl->current_entry, afl->pending_favored, afl->pending_not_fuzzed, + afl->queued_variable, stability, bitmap_cvg, afl->unique_crashes, + afl->unique_hangs, afl->last_path_time / 1000, + afl->last_crash_time / 1000, afl->last_hang_time / 1000, + afl->fsrv.total_execs - afl->last_crash_execs, afl->fsrv.exec_tmout, + afl->slowest_exec_ms, #ifndef __HAIKU__ #ifdef __APPLE__ - (unsigned long int)(rus.ru_maxrss >> 20), + (unsigned long int)(rus.ru_maxrss >> 20), #else - (unsigned long int)(rus.ru_maxrss >> 10), + (unsigned long int)(rus.ru_maxrss >> 10), #endif #else - -1UL, + -1UL, #endif #ifdef HAVE_AFFINITY - afl->cpu_aff, + afl->cpu_aff, #else - -1, + -1, #endif - t_bytes, afl->var_byte_count, afl->use_banner, - afl->unicorn_mode ? "unicorn" : "", afl->fsrv.qemu_mode ? "qemu " : "", - afl->non_instrumented_mode ? " non_instrumented " : "", - afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "", - afl->persistent_mode ? "persistent " : "", - afl->shmem_testcase_mode ? "shmem_testcase " : "", - afl->deferred_mode ? "deferred " : "", - (afl->unicorn_mode || afl->fsrv.qemu_mode || afl->non_instrumented_mode || - afl->no_forkserver || afl->crash_mode || afl->persistent_mode || - afl->deferred_mode) - ? "" - : "default", - afl->orig_cmdline); + t_bytes, afl->var_byte_count, afl->expand_havoc, afl->use_banner, + afl->unicorn_mode ? "unicorn" : "", + afl->fsrv.qemu_mode ? "qemu " : "", + afl->non_instrumented_mode ? " non_instrumented " : "", + afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "", + afl->persistent_mode ? "persistent " : "", + afl->shmem_testcase_mode ? "shmem_testcase " : "", + afl->deferred_mode ? "deferred " : "", + (afl->unicorn_mode || afl->fsrv.qemu_mode || + afl->non_instrumented_mode || afl->no_forkserver || + afl->crash_mode || afl->persistent_mode || afl->deferred_mode) + ? "" + : "default", + afl->orig_cmdline); /* ignore errors */ if (afl->debug) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index df2896d2..5bedf6e1 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -131,10 +131,13 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { "executions.\n\n" "Other stuff:\n" - " -T text - text banner to show on the screen\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" " use -D to force -S secondary to perform deterministic " "fuzzing\n" + " -F path - sync to a foreign fuzzer queue directory (requires " + "-M, can\n" + " be specified up to %u times)\n" + " -T text - text banner to show on the screen\n" " -I command - execute this command/script when a new crash is " "found\n" //" -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap @@ -142,7 +145,7 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { " -C - crash exploration mode (the peruvian rabbit thing)\n" " -e ext - file extension for the fuzz test input file (if " "needed)\n\n", - argv0, EXEC_TIMEOUT, MEM_LIMIT); + argv0, EXEC_TIMEOUT, MEM_LIMIT, FOREIGN_SYNCS_MAX); if (more_help > 1) { @@ -269,7 +272,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->shmem_testcase_mode = 1; // we always try to perform shmem fuzzing while ((opt = getopt(argc, argv, - "+c:i:I:o:f:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > + "+c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > 0) { switch (opt) { @@ -403,6 +406,19 @@ int main(int argc, char **argv_orig, char **envp) { afl->use_splicing = 1; break; + case 'F': /* foreign sync dir */ + + if (!afl->is_main_node) + FATAL( + "Option -F can only be specified after the -M option for the " + "main fuzzer of a fuzzing campaign"); + if (afl->foreign_sync_cnt >= FOREIGN_SYNCS_MAX) + FATAL("Maximum %u entried of -F option can be specified", + FOREIGN_SYNCS_MAX); + afl->foreign_syncs[afl->foreign_sync_cnt].dir = optarg; + afl->foreign_sync_cnt++; + break; + case 'f': /* target file */ if (afl->fsrv.out_file) { FATAL("Multiple -f options not supported"); } @@ -900,6 +916,7 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_NO_ARITH")) { afl->no_arith = 1; } if (get_afl_env("AFL_SHUFFLE_QUEUE")) { afl->shuffle_queue = 1; } if (get_afl_env("AFL_FAST_CAL")) { afl->fast_cal = 1; } + if (get_afl_env("AFL_EXPAND_HAVOC_NOW")) { afl->expand_havoc = 1; } if (afl->afl_env.afl_autoresume) { @@ -1059,6 +1076,8 @@ int main(int argc, char **argv_orig, char **envp) { setup_cmdline_file(afl, argv + optind); read_testcases(afl); + // read_foreign_testcases(afl, 1); for the moment dont do this + load_auto(afl); pivot_inputs(afl); @@ -1216,6 +1235,7 @@ int main(int argc, char **argv_orig, char **envp) { } + // (void)nice(-20); // does not improve the speed // real start time, we reset, so this works correctly with -V afl->start_time = get_cur_time(); @@ -1252,11 +1272,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 +1321,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 && |