mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-16 03:48:08 +00:00
fix: correct rescoring logic with minimal executions
Previous scoring logic did not correctly rescore all queue entries. This patch ensures rescoring works under the updated scheduling logic, while minimizing executions per feedback from PR #2363. Based on feedback from: https://github.com/AFLplusplus/AFLplusplus/pull/2363
This commit is contained in:
@ -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 */
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user