diff options
Diffstat (limited to 'src')
-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-state.c | 15 | ||||
-rw-r--r-- | src/afl-fuzz-stats.c | 3 | ||||
-rw-r--r-- | src/afl-fuzz.c | 81 |
6 files changed, 494 insertions, 69 deletions
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-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 2546a57a..7b30b5ea 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -115,6 +115,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "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" @@ -148,7 +149,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, #else -1, #endif - t_bytes, afl->var_byte_count, afl->use_banner, + 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 " : "", diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index eb4b6a87..5bedf6e1 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -916,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) { @@ -1271,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 { @@ -1289,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 && |