From 1c95804985732f36739da423472e99dbe320e412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulf=20Karg=C3=A9n?= <ulf.kargen@liu.se> Date: Tue, 14 Nov 2023 20:46:30 +0100 Subject: [PATCH] implement sync delay feature --- include/afl-fuzz.h | 3 ++- include/config.h | 5 +++++ include/envs.h | 1 + src/afl-fuzz-init.c | 22 +++++++++++++++++++++ src/afl-fuzz-one.c | 2 ++ src/afl-fuzz-queue.c | 14 ++++++++++++++ src/afl-fuzz-run.c | 46 +++++++++++++++++++++++++++++++++++++++++++- src/afl-fuzz-state.c | 7 +++++++ src/afl-fuzz.c | 3 ++- 9 files changed, 100 insertions(+), 3 deletions(-) diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 8b6502b44..f7d564b6c 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -399,7 +399,7 @@ typedef struct afl_env_vars { afl_cycle_schedules, afl_expand_havoc, afl_statsd, afl_cmplog_only_new, afl_exit_on_seed_issues, afl_try_affinity, afl_ignore_problems, afl_keep_timeouts, afl_no_crash_readme, afl_ignore_timeouts, - afl_no_startup_calibration, afl_no_warn_instability; + afl_no_startup_calibration, afl_no_warn_instability, afl_delay_sync; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload, @@ -1088,6 +1088,7 @@ void deinit_py(void *); /* Queue */ void mark_as_det_done(afl_state_t *, struct queue_entry *); +void mark_as_fuzzed(afl_state_t *, struct queue_entry *); void mark_as_variable(afl_state_t *, struct queue_entry *); void mark_as_redundant(afl_state_t *, struct queue_entry *, u8); void add_to_queue(afl_state_t *, u8 *, u32, u8); diff --git a/include/config.h b/include/config.h index b6249a0fc..1360ba256 100644 --- a/include/config.h +++ b/include/config.h @@ -309,6 +309,11 @@ #define SYNC_TIME (30 * 60 * 1000) +/* Approximate number of queue cycles to delay syncing of new discoveries + when AFL_DELAY_SYNC is in effect. */ + +#define SYNC_DELAY_CYCLES 3 + /* Output directory reuse grace period (minutes): */ #define OUTPUT_GRACE 25 diff --git a/include/envs.h b/include/envs.h index 066921b9f..6f5576f4a 100644 --- a/include/envs.h +++ b/include/envs.h @@ -221,6 +221,7 @@ static char *afl_environment_variables[] = { "AFL_STATSD_PORT", "AFL_STATSD_TAGS_FLAVOR", "AFL_SYNC_TIME", + "AFL_DELAY_SYNC", "AFL_TESTCACHE_SIZE", "AFL_TESTCACHE_ENTRIES", "AFL_TMIN_EXACT", diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 01d1e82ec..d600fff2c 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -928,6 +928,8 @@ void perform_dry_run(afl_state_t *afl) { --afl->pending_not_fuzzed; --afl->active_items; + mark_as_fuzzed(afl, q); + } break; @@ -1059,6 +1061,8 @@ void perform_dry_run(afl_state_t *afl) { --afl->pending_not_fuzzed; --afl->active_items; + mark_as_fuzzed(afl, q); + } q->disabled = 1; @@ -1178,6 +1182,8 @@ void perform_dry_run(afl_state_t *afl) { --afl->pending_not_fuzzed; --afl->active_items; + mark_as_fuzzed(afl, p); + } p->disabled = 1; @@ -1191,6 +1197,8 @@ void perform_dry_run(afl_state_t *afl) { --afl->pending_not_fuzzed; --afl->active_items; + mark_as_fuzzed(afl, q); + } q->disabled = 1; @@ -1556,6 +1564,10 @@ void nuke_resume_dir(afl_state_t *afl) { if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } ck_free(fn); + fn = alloc_printf("%s/_resume/.state/was_fuzzed", afl->out_dir); + if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } + ck_free(fn); + fn = alloc_printf("%s/_resume/.state/auto_extras", afl->out_dir); if (delete_files(fn, "auto_")) { goto dir_cleanup_failed; } ck_free(fn); @@ -1716,6 +1728,10 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } ck_free(fn); + fn = alloc_printf("%s/queue/.state/was_fuzzed", afl->out_dir); + if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } + ck_free(fn); + fn = alloc_printf("%s/queue/.state/auto_extras", afl->out_dir); if (delete_files(fn, "auto_")) { goto dir_cleanup_failed; } ck_free(fn); @@ -2007,6 +2023,12 @@ void setup_dirs_fds(afl_state_t *afl) { if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); } ck_free(tmp); + /* Directory for flagging queue entries that have been fuzzed */ + + tmp = alloc_printf("%s/queue/.state/was_fuzzed/", afl->out_dir); + if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); } + ck_free(tmp); + /* Directory with the auto-selected dictionary entries. */ tmp = alloc_printf("%s/queue/.state/auto_extras/", afl->out_dir); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index ee562f960..9496a0520 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -3091,6 +3091,8 @@ abandon_entry: afl->reinit_table = 1; if (afl->queue_cur->favored) { --afl->pending_favored; } + mark_as_fuzzed(afl, afl->queue_cur); + } ++afl->queue_cur->fuzz_level; diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 8ad7cd976..b57fe0e9d 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -292,6 +292,20 @@ void mark_as_det_done(afl_state_t *afl, struct queue_entry *q) { } +void mark_as_fuzzed(afl_state_t *afl, struct queue_entry *q) { + + char fn[PATH_MAX]; + s32 fd; + + snprintf(fn, PATH_MAX, "%s/queue/.state/was_fuzzed/%s", afl->out_dir, + strrchr((char *)q->fname, '/') + 1); + + fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, DEFAULT_PERMISSION); + if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + close(fd); + +} + /* Mark as variable. Create symlinks if possible to make it easier to examine the files. */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 4d56f3a77..eef580a5d 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -648,6 +648,7 @@ void sync_fuzzers(afl_state_t *afl) { struct dirent *sd_ent; u32 sync_cnt = 0, synced = 0, entries = 0; u8 path[PATH_MAX + 1 + NAME_MAX]; + u8 wf_path[PATH_MAX + 1 + NAME_MAX]; sd = opendir(afl->sync_dir); if (!sd) { PFATAL("Unable to open '%s'", afl->sync_dir); } @@ -703,6 +704,7 @@ void sync_fuzzers(afl_state_t *afl) { /* document the attempt to sync to this instance */ sprintf(qd_synced_path, "%s/.synced/%s.last", afl->out_dir, sd_ent->d_name); + id_fd = open(qd_synced_path, O_RDWR | O_CREAT | O_TRUNC, DEFAULT_PERMISSION); if (id_fd >= 0) close(id_fd); @@ -746,6 +748,12 @@ void sync_fuzzers(afl_state_t *afl) { afl->stage_cur = 0; afl->stage_max = 0; + if(afl->afl_env.afl_delay_sync && !afl->last_sync_time) { + + goto close_sync; + + } + /* For every file queued by this fuzzer, parse ID and see if we have looked at it before; exec a test case if not. */ @@ -768,15 +776,50 @@ void sync_fuzzers(afl_state_t *afl) { if (m >= n) { goto close_sync; } // nothing new + u8 retry_remaining = 0; + for (o = m; o < n; o++) { s32 fd; struct stat st; + struct stat wf_st; + u8 skip; snprintf(path, sizeof(path), "%s/%s", qd_path, namelist[o]->d_name); + + /* Skip syncing of recent discoveries if AFL_DELAY_SYNC is true. */ + + skip = 0; + + if (afl->afl_env.afl_delay_sync) { + + u64 cycle_time = (get_cur_time() - + (!afl->last_sync_time ? afl->start_time : afl->last_sync_time)) / 1000; + + snprintf(wf_path, sizeof(wf_path), "%s/.state/was_fuzzed/%s", + qd_path, namelist[o]->d_name); + + if (lstat(wf_path, &wf_st) || + ((u64)wf_st.st_mtime > + get_cur_time() / 1000 - SYNC_DELAY_CYCLES * cycle_time)) { + + skip = 1; + + /* All entries beyond this one would need to be retried at the next + sync. (Pretty wasteful, would be better to document each synced + test case individually, e.g. in the .synced dir.) */ + + retry_remaining = 1; + ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); + + } + } + afl->syncing_case = next_min_accept; next_min_accept++; + if (skip) { continue; } + /* Allow this to fail in case the other fuzzer is resuming or so... */ fd = open(path, O_RDONLY); @@ -816,7 +859,8 @@ void sync_fuzzers(afl_state_t *afl) { } - ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); + if (!retry_remaining) + ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); close_sync: close(id_fd); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 46b67defc..b6bb0fdfb 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -567,6 +567,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { } + } else if (!strncmp(env, "AFL_DELAY_SYNC", + + afl_environment_variable_len)) { + + afl->afl_env.afl_delay_sync = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_FUZZER_STATS_UPDATE_INTERVAL", afl_environment_variable_len)) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index f6628851c..9866642b1 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -2578,7 +2578,8 @@ int main(int argc, char **argv_orig, char **envp) { } while (skipped_fuzz && afl->queue_cur && !afl->stop_soon); - if (likely(!afl->stop_soon && afl->sync_id)) { + if (likely(!afl->stop_soon && afl->sync_id && + !afl->afl_env.afl_delay_sync)) { if (likely(afl->skip_deterministic)) { -- GitLab