diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index f475498a..806aecf9 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -721,6 +721,8 @@ typedef struct afl_state { struct queue_entry **top_rated; /* Top entries for bitmap bytes */ + u32 **top_rated_candidates; /* Candidate IDs per bitmap index */ + struct extra_data *extras; /* Extra tokens to fuzz with */ u32 extras_cnt; /* Total number of tokens read */ @@ -862,6 +864,8 @@ typedef struct afl_state { struct skipdet_global *skipdet_g; + s64 last_scored_idx; /* Index of the last queue entry re-scored */ + #ifdef INTROSPECTION char mutation[8072]; char m_tmp[4096]; @@ -1189,6 +1193,8 @@ void destroy_queue(afl_state_t *); void update_bitmap_score(afl_state_t *, struct queue_entry *, bool); void cull_queue(afl_state_t *); u32 calculate_score(afl_state_t *, struct queue_entry *); +void recalculate_all_scores(afl_state_t *); +void update_bitmap_rescore(afl_state_t *, struct queue_entry *, u32); /* Bitmap */ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index ea6d902a..74d35fe5 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -992,6 +992,178 @@ void cull_queue(afl_state_t *afl) { } +/* Re-selects top_rated[] entries based on the current fuzzing schedule. + Each queued entry is executed once to collect trace_bits, and potential + candidates for each bitmap index are stored. + + The candidate list format is [count][id1][id2]... as a u32 array, + where 'count' indicates how many queue IDs hit that index. */ + +void recalculate_all_scores(afl_state_t *afl) { + + u8 *in_buf; + u32 i; + u32 j; + + for (i = afl->last_scored_idx + 1; i < afl->queued_items; i++) { + + if (likely(!afl->queue_buf[i]->disabled)) { + + in_buf = queue_testcase_get(afl, afl->queue_buf[i]); + (void)write_to_testcase(afl, in_buf, afl->queue_buf[i]->len, 1); + (void)fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); + + for (j = 0; j < afl->fsrv.map_size; ++j) { + + if (afl->fsrv.trace_bits[j]) { + + u32 *candidate_ids = afl->top_rated_candidates[j]; + u32 id = afl->queue_buf[i]->id; + + if (!candidate_ids) { + // first candidate: [count][id] + candidate_ids = ck_alloc(sizeof(u32) * 2); + candidate_ids[0] = 1; // count = 1 + candidate_ids[1] = id; // first ID + } else { + u32 count = candidate_ids[0]; + + candidate_ids = ck_realloc(candidate_ids, sizeof(u32) * (count + 2)); + candidate_ids[0] = count + 1; // increment the count + candidate_ids[count + 1] = id; // append the new ID to the end + + //fprintf(stderr, "enroll candidate[%u][%u] %u\n", i, j, id); + } + + afl->top_rated_candidates[j] = candidate_ids; + + } + } + + + } + + afl->last_scored_idx = i; + + } + + for (i = 0; i < afl->fsrv.map_size; ++i) { + + u32 *candidate_ids = afl->top_rated_candidates[i]; + if(candidate_ids) { + u32 count = candidate_ids[0]; + + for(u32 k = 0; k < count; k++) { + u32 id = candidate_ids[k + 1]; + struct queue_entry *entry = afl->queue_buf[id]; + update_bitmap_rescore(afl, entry, i); + } + + } + + } + +} + +/* Re-evaluates top-rated entries without checking trace_bits. + Unlike update_bitmap_score(), this function assumes the trace + information is already known and only compares entries */ + +void update_bitmap_rescore(afl_state_t *afl, struct queue_entry *q, u32 index) { + + u32 i = index; + u64 fav_factor; + u64 fuzz_p2; + + if (unlikely(afl->schedule >= FAST && afl->schedule < RARE)) { + + fuzz_p2 = 0; // Skip the fuzz_p2 comparison + + } else if (unlikely(afl->schedule == RARE)) { + + fuzz_p2 = next_pow2(afl->n_fuzz[q->n_fuzz_entry]); + + } else { + + fuzz_p2 = q->fuzz_level; + + } + + if (unlikely(afl->schedule >= RARE) || unlikely(afl->fixed_seed)) { + + fav_factor = q->len << 2; + + } else { + + fav_factor = q->exec_us * q->len; + + } + + if (afl->top_rated[i]) { + + /* Faster-executing or smaller test cases are favored. */ + u64 top_rated_fav_factor; + u64 top_rated_fuzz_p2; + + if (unlikely(afl->schedule >= FAST && afl->schedule < RARE)) { + + top_rated_fuzz_p2 = 0; // Skip the fuzz_p2 comparison + + } else if (unlikely(afl->schedule == RARE)) { + + top_rated_fuzz_p2 = + next_pow2(afl->n_fuzz[afl->top_rated[i]->n_fuzz_entry]); + + } else { + + top_rated_fuzz_p2 = afl->top_rated[i]->fuzz_level; + + } + + if (unlikely(afl->schedule >= RARE) || unlikely(afl->fixed_seed)) { + + top_rated_fav_factor = afl->top_rated[i]->len << 2; + + } else { + + top_rated_fav_factor = + afl->top_rated[i]->exec_us * afl->top_rated[i]->len; + + } + + if (likely(fuzz_p2 > top_rated_fuzz_p2)) { return; } + + if (likely(fav_factor > top_rated_fav_factor)) { return; } + + /* Looks like we're going to win. Decrease ref count for the + previous winner, discard its afl->fsrv.trace_bits[] if necessary. */ + + if (!--afl->top_rated[i]->tc_ref) { + + ck_free(afl->top_rated[i]->trace_mini); + afl->top_rated[i]->trace_mini = NULL; + + } + + } + + /* Insert ourselves as the new winner. */ + + afl->top_rated[i] = q; + ++q->tc_ref; + + if (!q->trace_mini) { + + u32 len = (afl->fsrv.map_size >> 3); + q->trace_mini = (u8 *)ck_alloc(len); + minimize_bits(afl, q->trace_mini, afl->fsrv.trace_bits); + + } + + afl->score_changed = 1; + +} + /* Calculate case desirability score to adjust the length of havoc fuzzing. A helper function for fuzz_one(). Maybe some of these constants should go into config.h. */ diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 11554369..b346d86d 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -106,7 +106,8 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->switch_fuzz_mode = STRATEGY_SWITCH_TIME * 1000; afl->q_testcase_max_cache_size = TESTCASE_CACHE_SIZE * 1048576UL; afl->q_testcase_max_cache_entries = 64 * 1024; - + afl->last_scored_idx = -1; + #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ #endif /* HAVE_AFFINITY */ @@ -116,6 +117,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->virgin_crash = ck_alloc(map_size); afl->var_bytes = ck_alloc(map_size); afl->top_rated = ck_alloc(map_size * sizeof(void *)); + afl->top_rated_candidates = ck_alloc(map_size * sizeof(u32)); afl->clean_trace = ck_alloc(map_size); afl->clean_trace_custom = ck_alloc(map_size); afl->first_trace = ck_alloc(map_size); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 584b664e..0f044c66 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -3227,15 +3227,7 @@ int main(int argc, char **argv_orig, char **envp) { } // we must recalculate the scores of all queue entries - for (u32 i = 0; i < afl->queued_items; i++) { - - if (likely(!afl->queue_buf[i]->disabled)) { - - update_bitmap_score(afl, afl->queue_buf[i], false); - - } - - } + recalculate_all_scores(afl); }