skip entried synced from us if we have not restarted

This commit is contained in:
vanhauser-thc
2025-05-20 17:07:30 +02:00
parent 45a7d65207
commit c3d5f3f471
3 changed files with 119 additions and 41 deletions

View File

@ -1259,6 +1259,7 @@ int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen);
/* Run */ /* Run */
void check_sync_fuzzers(afl_state_t *);
void sync_fuzzers(afl_state_t *); void sync_fuzzers(afl_state_t *);
u32 write_to_testcase(afl_state_t *, void **, u32, u32); u32 write_to_testcase(afl_state_t *, void **, u32, u32);
u8 calibrate_case(afl_state_t *, struct queue_entry *, u8 *, u32, u8); u8 calibrate_case(afl_state_t *, struct queue_entry *, u8 *, u32, u8);

View File

@ -699,42 +699,105 @@ abort_calibration:
} }
bool is_known_case(afl_state_t *afl, u8 *name, void *mem, u32 len) { /* Do not sync items that were synced from us */
static bool is_known_case(afl_state_t *afl, u8 *name) {
static char coming_from_me_str[SYNC_ID_MAX_LEN + 2];
static u32 coming_from_me_len = 0;
static u32 min_len = 15 + 4 + 6;
static char coming_from_me_str[16 + SYNC_ID_MAX_LEN];
static int coming_from_me_len = 0;
if (!coming_from_me_len) { if (!coming_from_me_len) {
snprintf(coming_from_me_str, sizeof(coming_from_me_str), snprintf(coming_from_me_str, sizeof(coming_from_me_str), "%s,",
",sync:%s,src:", afl->sync_id); afl->sync_id);
coming_from_me_len = strlen(coming_from_me_str); min_len += coming_from_me_len = strlen(coming_from_me_str);
} }
// 9 = strlen("id:000000"), 6 = strlen("000000") // file name length long enough so it can be ours
if (strlen(name) < 9 + coming_from_me_len + 6) return false; if (unlikely(strlen(name) < min_len)) { return false; }
char *p = name + 9; // is it based on a sync? allow optimizer to make an integer comparison
while ('0' <= *p && *p <= '9') if (likely(memcmp(name + 10, "sync", 4) != 0)) { return false; }
p++; // we jump over the ':' after 'sync' and compare to our sync name
if (unlikely(memcmp(name + 15, coming_from_me_str, coming_from_me_len) !=
0)) {
if (strncmp(p, coming_from_me_str, coming_from_me_len) != 0) return false; return false;
int src_id = atoi(p + coming_from_me_len); }
if (src_id < 0 || src_id >= afl->queued_items) return false;
struct queue_entry *q = afl->queue_buf[src_id]; /* We do not need this as we now look on startup how many files are in sync
if (q->len != len) return false; targets.
int src_id = atoi(name + 15 + coming_from_me_len + 4);
if (unlikely(src_id >= afl->queued_items)) return false;
*/
if (q->testcase_buf) return memcmp(q->testcase_buf, mem, len) == 0; // yes it is highly likely a current testcase we already know
return true;
int fd = open((char *)q->fname, O_RDONLY); }
if (fd < 0) return false;
u8 *buf = malloc(len); void check_sync_fuzzers(afl_state_t *afl) {
ck_read(fd, buf, len, q->fname);
close(fd); if (unlikely(afl->afl_env.afl_no_sync)) { return; }
bool result = (memcmp(buf, mem, len) == 0);
free(buf); DIR *sd, *dir;
return result; struct dirent *sd_ent, *entry;
u8 qd_path[PATH_MAX], qd_synced_maxid[PATH_MAX];
sd = opendir(afl->sync_dir);
if (!sd) { PFATAL("Unable to open '%s'", afl->sync_dir); }
u64 sync_start_us = get_cur_time_us();
// Look at the entries created for every other fuzzer in the sync directory.
while ((sd_ent = readdir(sd))) {
if (sd_ent->d_name[0] == '.' || !strcmp(afl->sync_id, sd_ent->d_name)) {
continue;
}
sprintf(qd_path, "%s/%s/queue", afl->sync_dir, sd_ent->d_name);
dir = opendir(qd_path);
if (dir) {
u32 max_start_id = 0;
while ((entry = readdir(dir)) != NULL) {
max_start_id++;
}
if (max_start_id > 4) {
sprintf(qd_synced_maxid, "%s/.synced/%s.max", afl->out_dir,
sd_ent->d_name);
s32 max_fd = open(qd_synced_maxid, O_WRONLY | O_CREAT | O_TRUNC,
DEFAULT_PERMISSION);
if (max_fd >= 0) {
max_start_id -= 4; // without ".", "..", ".state" and counting from 0
write(max_fd, &max_start_id, sizeof(u32));
close(max_fd);
}
}
}
closedir(dir);
}
closedir(sd);
update_sync_time(afl, &sync_start_us);
} }
@ -764,9 +827,8 @@ void sync_fuzzers(afl_state_t *afl) {
// iteration // iteration
update_sync_time(afl, &sync_start_us); update_sync_time(afl, &sync_start_us);
u8 qd_synced_path[PATH_MAX], qd_path[PATH_MAX]; u8 qd_synced_path[PATH_MAX], qd_path[PATH_MAX], qd_synced_maxid[PATH_MAX];
u32 min_accept = 0, next_min_accept = 0; u32 min_accept = 0, next_min_accept = 0, max_start_id = 0;
s32 id_fd; s32 id_fd;
// Skip dot files and our own output directory. // Skip dot files and our own output directory.
@ -851,17 +913,19 @@ void sync_fuzzers(afl_state_t *afl) {
snprintf(id0, sizeof(id0), "%s/id:000000,*", qd_path); snprintf(id0, sizeof(id0), "%s/id:000000,*", qd_path);
if (glob(id0, 0, NULL, &glob_result) == 0 && glob_result.gl_pathc == 1 && if (likely(glob(id0, 0, NULL, &glob_result) == 0 &&
stat(glob_result.gl_pathv[0], &st) == 0) { glob_result.gl_pathc == 1 &&
stat(glob_result.gl_pathv[0], &st) == 0)) {
// we found exactly one "id:000000,*" file and obtained its mtime // we found exactly one "id:000000,*" file and obtained its mtime
globfree(&glob_result); globfree(&glob_result);
if (last_mtime && last_mtime < st.st_mtime) { if (unlikely(last_mtime && last_mtime < st.st_mtime)) {
// the first entry is newer than when we synced last - instance was // the first entry is newer than when we synced last - instance was
// restarted - we have to reset our counter and will skip this instance // restarted - we have to reset our counter and will skip this instance
// this time // this time. It could also be this was trimmed later, or restated with
// resume-in-place though but better be safe.
min_accept = 0; min_accept = 0;
ck_write(id_fd, &min_accept, sizeof(u32), qd_synced_path); ck_write(id_fd, &min_accept, sizeof(u32), qd_synced_path);
goto close_sync; goto close_sync;
@ -877,6 +941,18 @@ void sync_fuzzers(afl_state_t *afl) {
} }
// check if there is a file documented the maximum id seen on startup
sprintf(qd_synced_maxid, "%s/.synced/%s.max", afl->out_dir, sd_ent->d_name);
s32 max_fd = open(qd_synced_maxid, O_RDONLY, DEFAULT_PERMISSION);
if (max_fd >= 0) {
read(max_fd, &max_start_id, sizeof(u32));
close(max_fd);
if (max_start_id < next_min_accept) { unlink(qd_synced_maxid); }
}
/* Show stats */ /* Show stats */
snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "sync %u", ++sync_cnt); snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "sync %u", ++sync_cnt);
@ -930,19 +1006,19 @@ void sync_fuzzers(afl_state_t *afl) {
if (st.st_size && st.st_size <= MAX_FILE) { if (st.st_size && st.st_size <= MAX_FILE) {
u8 fault; if (likely(next_min_accept < max_start_id ||
u8 *mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); !is_known_case(afl, namelist[o]->d_name))) {
if (mem == MAP_FAILED) { PFATAL("Unable to mmap '%s'", path); }
if (!is_known_case(afl, namelist[o]->d_name, mem, st.st_size)) {
/* See what happens. We rely on save_if_interesting() to catch major /* See what happens. We rely on save_if_interesting() to catch major
errors and save the test case. */ errors and save the test case. */
u8 *mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mem == MAP_FAILED) { PFATAL("Unable to mmap '%s'", path); }
u32 new_len = write_to_testcase(afl, (void **)&mem, st.st_size, 1); u32 new_len = write_to_testcase(afl, (void **)&mem, st.st_size, 1);
fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); u8 fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
if (afl->stop_soon) { if (afl->stop_soon) {
@ -957,11 +1033,10 @@ void sync_fuzzers(afl_state_t *afl) {
afl->queued_imported += save_if_interesting(afl, mem, new_len, fault); afl->queued_imported += save_if_interesting(afl, mem, new_len, fault);
show_stats(afl); show_stats(afl);
afl->syncing_party = 0; afl->syncing_party = 0;
munmap(mem, st.st_size);
} }
munmap(mem, st.st_size);
} }
close(fd); close(fd);

View File

@ -2998,6 +2998,8 @@ int main(int argc, char **argv_orig, char **envp) {
if (afl->stop_soon) { goto stop_fuzzing; } if (afl->stop_soon) { goto stop_fuzzing; }
if (!afl->in_place_resume) { check_sync_fuzzers(afl); }
/* Woop woop woop */ /* Woop woop woop */
if (!afl->not_on_tty) { if (!afl->not_on_tty) {