Merge pull request #2128 from AFLplusplus/fastrestart

Fastrestart
This commit is contained in:
van Hauser
2024-06-18 15:28:56 +02:00
committed by GitHub
9 changed files with 251 additions and 36 deletions

View File

@ -514,6 +514,8 @@ checks or alter some of the more exotic semantics of the tool:
- `AFL_NO_SNAPSHOT` will advise afl-fuzz not to use the snapshot feature if - `AFL_NO_SNAPSHOT` will advise afl-fuzz not to use the snapshot feature if
the snapshot lkm is loaded. the snapshot lkm is loaded.
- `AFL_NO_FASTRESUME` will not try to read or write a fast resume file.
- Setting `AFL_NO_UI` inhibits the UI altogether and just periodically prints - Setting `AFL_NO_UI` inhibits the UI altogether and just periodically prints
some basic stats. This behavior is also automatically triggered when the some basic stats. This behavior is also automatically triggered when the
output from afl-fuzz is redirected to a file or to a pipe. output from afl-fuzz is redirected to a file or to a pipe.

View File

@ -236,7 +236,6 @@ struct queue_entry {
custom, /* Marker for custom mutators */ custom, /* Marker for custom mutators */
stats_mutated; /* stats: # of mutations performed */ stats_mutated; /* stats: # of mutations performed */
u8 *trace_mini; /* Trace bytes, if kept */
u32 tc_ref; /* Trace bytes ref count */ u32 tc_ref; /* Trace bytes ref count */
#ifdef INTROSPECTION #ifdef INTROSPECTION
@ -246,13 +245,11 @@ struct queue_entry {
double perf_score, /* performance score */ double perf_score, /* performance score */
weight; weight;
struct queue_entry *mother; /* queue entry this based on */
u8 *trace_mini; /* Trace bytes, if kept */
u8 *testcase_buf; /* The testcase buffer, if loaded. */ u8 *testcase_buf; /* The testcase buffer, if loaded. */
u8 *cmplog_colorinput; /* the result buf of colorization */ u8 *cmplog_colorinput; /* the result buf of colorization */
struct tainted *taint; /* Taint information from CmpLog */ struct tainted *taint; /* Taint information from CmpLog */
struct queue_entry *mother; /* queue entry this based on */
struct skipdet_entry *skipdet_e; struct skipdet_entry *skipdet_e;
}; };
@ -457,7 +454,7 @@ typedef struct afl_env_vars {
afl_no_startup_calibration, afl_no_warn_instability, afl_no_startup_calibration, afl_no_warn_instability,
afl_post_process_keep_original, afl_crashing_seeds_as_new_crash, afl_post_process_keep_original, afl_crashing_seeds_as_new_crash,
afl_final_sync, afl_ignore_seed_problems, afl_disable_redundant, afl_final_sync, afl_ignore_seed_problems, afl_disable_redundant,
afl_sha1_filenames, afl_no_sync; afl_sha1_filenames, afl_no_sync, afl_no_fastresume;
u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path,
*afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload,

View File

@ -115,7 +115,7 @@ static char *afl_environment_variables[] = {
"AFL_TRACE_PC", "AFL_USE_ASAN", "AFL_USE_MSAN", "AFL_USE_TRACE_PC", "AFL_TRACE_PC", "AFL_USE_ASAN", "AFL_USE_MSAN", "AFL_USE_TRACE_PC",
"AFL_USE_UBSAN", "AFL_USE_TSAN", "AFL_USE_CFISAN", "AFL_USE_LSAN", "AFL_USE_UBSAN", "AFL_USE_TSAN", "AFL_USE_CFISAN", "AFL_USE_LSAN",
"AFL_WINE_PATH", "AFL_NO_SNAPSHOT", "AFL_EXPAND_HAVOC_NOW", "AFL_USE_FASAN", "AFL_WINE_PATH", "AFL_NO_SNAPSHOT", "AFL_EXPAND_HAVOC_NOW", "AFL_USE_FASAN",
"AFL_USE_QASAN", "AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", NULL "AFL_USE_QASAN", "AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", "AFL_NO_FASTRESUME", NULL
}; };

View File

@ -1019,7 +1019,7 @@ void perform_dry_run(afl_state_t *afl) {
} }
if (!q->was_fuzzed) { if (unlikely(!q->was_fuzzed)) {
q->was_fuzzed = 1; q->was_fuzzed = 1;
afl->reinit_table = 1; afl->reinit_table = 1;

View File

@ -401,7 +401,7 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) {
} else { } else {
if (unlink(fn)) { PFATAL("Unable to remove '%s'", fn); } if (unlink(fn)) { /*PFATAL("Unable to remove '%s'", fn);*/ }
} }
@ -700,11 +700,10 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
void destroy_queue(afl_state_t *afl) { void destroy_queue(afl_state_t *afl) {
u32 i; u32 i;
struct queue_entry *q;
for (i = 0; i < afl->queued_items; i++) { for (i = 0; i < afl->queued_items; i++) {
struct queue_entry *q;
q = afl->queue_buf[i]; q = afl->queue_buf[i];
ck_free(q->fname); ck_free(q->fname);
ck_free(q->trace_mini); ck_free(q->trace_mini);

View File

@ -286,6 +286,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
afl->afl_env.afl_no_sync = afl->afl_env.afl_no_sync =
get_afl_env(afl_environment_variables[i]) ? 1 : 0; get_afl_env(afl_environment_variables[i]) ? 1 : 0;
} else if (!strncmp(env, "AFL_NO_FASTRESUME",
afl_environment_variable_len)) {
afl->afl_env.afl_no_fastresume =
get_afl_env(afl_environment_variables[i]) ? 1 : 0;
} else if (!strncmp(env, "AFL_CUSTOM_MUTATOR_ONLY", } else if (!strncmp(env, "AFL_CUSTOM_MUTATOR_ONLY",
afl_environment_variable_len)) { afl_environment_variable_len)) {

View File

@ -76,7 +76,13 @@ char *get_fuzzing_state(afl_state_t *afl) {
void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { void write_setup_file(afl_state_t *afl, u32 argc, char **argv) {
u8 fn[PATH_MAX]; u8 fn[PATH_MAX], fn2[PATH_MAX];
snprintf(fn2, PATH_MAX, "%s/target_hash", afl->out_dir);
FILE *f2 = create_ffile(fn2);
fprintf(f2, "%p\n", (void *)get_binary_hash(afl->fsrv.target_path));
fclose(f2);
snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir);
FILE *f = create_ffile(fn); FILE *f = create_ffile(fn);
u32 i; u32 i;

View File

@ -181,7 +181,7 @@ static void usage(u8 *argv0, int more_help) {
"it.\n" "it.\n"
" if using QEMU/FRIDA or the fuzzing target is " " if using QEMU/FRIDA or the fuzzing target is "
"compiled\n" "compiled\n"
" for CmpLog then use '-c 0'. To disable Cmplog use '-c " " for CmpLog then use '-c 0'. To disable CMPLOG use '-c "
"-'.\n" "-'.\n"
" -l cmplog_opts - CmpLog configuration values (e.g. \"2ATR\"):\n" " -l cmplog_opts - CmpLog configuration values (e.g. \"2ATR\"):\n"
" 1=small files, 2=larger files (default), 3=all " " 1=small files, 2=larger files (default), 3=all "
@ -335,6 +335,7 @@ static void usage(u8 *argv0, int more_help) {
"AFL_STATSD_PORT: change default statsd port (default: 8125)\n" "AFL_STATSD_PORT: change default statsd port (default: 8125)\n"
"AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n" "AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n"
" suported formats: dogstatsd, librato, signalfx, influxdb\n" " suported formats: dogstatsd, librato, signalfx, influxdb\n"
"AFL_NO_FASTRESUME: do not read or write a fast resume file\n"
"AFL_NO_SYNC: disables all syncing\n" "AFL_NO_SYNC: disables all syncing\n"
"AFL_SYNC_TIME: sync time between fuzzing instances (in minutes)\n" "AFL_SYNC_TIME: sync time between fuzzing instances (in minutes)\n"
"AFL_FINAL_SYNC: sync a final time when exiting (will delay the exit!)\n" "AFL_FINAL_SYNC: sync a final time when exiting (will delay the exit!)\n"
@ -2105,13 +2106,80 @@ int main(int argc, char **argv_orig, char **envp) {
} }
setup_cmdline_file(afl, argv + optind);
check_binary(afl, argv[optind]);
u64 prev_target_hash = 0;
s32 fast_resume = 0, fr_fd = -1;
if (afl->in_place_resume && !afl->afl_env.afl_no_fastresume) {
u8 fn[PATH_MAX], buf[32];
snprintf(fn, PATH_MAX, "%s/target_hash", afl->out_dir);
fr_fd = open(fn, O_RDONLY);
if (fr_fd >= 0) {
if (read(fr_fd, buf, 32) >= 16) {
sscanf(buf, "%p", (void **)&prev_target_hash);
}
close(fr_fd);
}
}
write_setup_file(afl, argc, argv); write_setup_file(afl, argc, argv);
setup_cmdline_file(afl, argv + optind); if (afl->in_place_resume && !afl->afl_env.afl_no_fastresume) {
u64 target_hash = get_binary_hash(afl->fsrv.target_path);
if (!target_hash || prev_target_hash != target_hash) {
ACTF("Target binary is different, cannot perform FAST RESUME!");
} else {
u8 fn[PATH_MAX];
snprintf(fn, PATH_MAX, "%s/fastresume.bin", afl->out_dir);
if ((fr_fd = open(fn, O_RDONLY)) >= 0) {
u8 ver_string[8];
u64 *ver = (u64 *)ver_string;
u64 expect_ver =
afl->shm.cmplog_mode + (sizeof(struct queue_entry) << 1);
if (read(fr_fd, ver_string, sizeof(ver_string)) != sizeof(ver_string))
WARNF("Emtpy fastresume.bin, ignoring, cannot perform FAST RESUME");
else if (expect_ver != *ver)
WARNF(
"Different AFL++ version or feature usage, cannot perform FAST "
"RESUME");
else {
OKF("Will perform FAST RESUME");
fast_resume = 1;
}
} else {
ACTF("fastresume.bin not found, cannot perform FAST RESUME!");
}
// If the fast resume file is not valid we will be unable to start, so
// we remove the file but keep the file descriptor open.
unlink(fn);
}
}
read_testcases(afl, NULL); read_testcases(afl, NULL);
// read_foreign_testcases(afl, 1); for the moment dont do this
OKF("Loaded a total of %u seeds.", afl->queued_items);
pivot_inputs(afl); pivot_inputs(afl);
@ -2144,6 +2212,9 @@ int main(int argc, char **argv_orig, char **envp) {
} }
// read_foreign_testcases(afl, 1); for the moment dont do this
OKF("Loaded a total of %u seeds.", afl->queued_items);
/* If we don't have a file name chosen yet, use a safe default. */ /* If we don't have a file name chosen yet, use a safe default. */
if (!afl->fsrv.out_file) { if (!afl->fsrv.out_file) {
@ -2200,8 +2271,6 @@ int main(int argc, char **argv_orig, char **envp) {
} }
check_binary(afl, argv[optind]);
#ifdef AFL_PERSISTENT_RECORD #ifdef AFL_PERSISTENT_RECORD
if (unlikely(afl->fsrv.persistent_record)) { if (unlikely(afl->fsrv.persistent_record)) {
@ -2420,7 +2489,7 @@ int main(int argc, char **argv_orig, char **envp) {
} }
OKF("Cmplog forkserver successfully started"); OKF("CMPLOG forkserver successfully started");
} }
@ -2458,6 +2527,77 @@ int main(int argc, char **argv_orig, char **envp) {
dedup_extras(afl); dedup_extras(afl);
if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); } if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); }
if (unlikely(fast_resume)) {
u64 resume_start = get_cur_time_us();
// if we get here then we should abort on errors
ck_read(fr_fd, afl->virgin_bits, afl->fsrv.map_size, "virgin_bits");
ck_read(fr_fd, afl->virgin_tmout, afl->fsrv.map_size, "virgin_tmout");
ck_read(fr_fd, afl->virgin_crash, afl->fsrv.map_size, "virgin_crash");
ck_read(fr_fd, afl->var_bytes, afl->fsrv.map_size, "var_bytes");
u8 res[1] = {0};
u8 *o_start = (u8 *)&(afl->queue_buf[0]->colorized);
u8 *o_end = (u8 *)&(afl->queue_buf[0]->mother);
u32 r = 8 + afl->fsrv.map_size * 4;
u32 q_len = o_end - o_start;
u32 m_len = (afl->fsrv.map_size >> 3);
struct queue_entry *q;
for (u32 i = 0; i < afl->queued_items; i++) {
q = afl->queue_buf[i];
ck_read(fr_fd, (u8 *)&(q->colorized), q_len, "queue data");
ck_read(fr_fd, res, 1, "check map");
if (res[0]) {
q->trace_mini = ck_alloc(m_len);
ck_read(fr_fd, q->trace_mini, m_len, "trace_mini");
r += q_len + m_len + 1;
} else {
r += q_len + 1;
}
afl->total_bitmap_size += q->bitmap_size;
++afl->total_bitmap_entries;
update_bitmap_score(afl, q);
if (q->was_fuzzed) { --afl->pending_not_fuzzed; }
if (q->disabled) {
if (!q->was_fuzzed) { --afl->pending_not_fuzzed; }
--afl->active_items;
}
if (q->var_behavior) { ++afl->queued_variable; }
if (q->favored) {
++afl->queued_favored;
if (!q->was_fuzzed) { ++afl->pending_favored; }
}
}
u8 buf[4];
if (read(fr_fd, buf, 3) > 0) {
FATAL("invalid trailing data in fastresume.bin");
}
OKF("Successfully loaded fastresume.bin (%u bytes)!", r);
close(fr_fd);
afl->reinit_table = 1;
update_calibration_time(afl, &resume_start);
} else {
// after we have the correct bitmap size we can read the bitmap -B option // after we have the correct bitmap size we can read the bitmap -B option
// and set the virgin maps // and set the virgin maps
if (afl->in_bitmap) { if (afl->in_bitmap) {
@ -2479,11 +2619,13 @@ int main(int argc, char **argv_orig, char **envp) {
} else { } else {
ACTF("skipping initial seed calibration due option override!"); ACTF("Skipping initial seed calibration due option override!");
usleep(1000); usleep(1000);
} }
}
if (afl->q_testcase_max_cache_entries) { if (afl->q_testcase_max_cache_entries) {
afl->q_testcase_cache = afl->q_testcase_cache =
@ -3071,6 +3213,66 @@ stop_fuzzing:
fclose(afl->fsrv.det_plot_file); fclose(afl->fsrv.det_plot_file);
#endif #endif
if (!afl->afl_env.afl_no_fastresume) {
/* create fastresume.bin */
u8 fr[PATH_MAX];
snprintf(fr, PATH_MAX, "%s/fastresume.bin", afl->out_dir);
ACTF("Writing %s ...", fr);
if ((fr_fd = open(fr, O_WRONLY | O_TRUNC | O_CREAT, DEFAULT_PERMISSION)) >=
0) {
u8 ver_string[8];
u32 w = 0;
u64 *ver = (u64 *)ver_string;
*ver = afl->shm.cmplog_mode + (sizeof(struct queue_entry) << 1);
w += write(fr_fd, ver_string, sizeof(ver_string));
w += write(fr_fd, afl->virgin_bits, afl->fsrv.map_size);
w += write(fr_fd, afl->virgin_tmout, afl->fsrv.map_size);
w += write(fr_fd, afl->virgin_crash, afl->fsrv.map_size);
w += write(fr_fd, afl->var_bytes, afl->fsrv.map_size);
u8 on[1] = {1}, off[1] = {0};
u8 *o_start = (u8 *)&(afl->queue_buf[0]->colorized);
u8 *o_end = (u8 *)&(afl->queue_buf[0]->mother);
u32 q_len = o_end - o_start;
u32 m_len = (afl->fsrv.map_size >> 3);
struct queue_entry *q;
afl->pending_not_fuzzed = afl->queued_items;
afl->active_items = afl->queued_items;
for (u32 i = 0; i < afl->queued_items; i++) {
q = afl->queue_buf[i];
ck_write(fr_fd, (u8 *)&(q->colorized), q_len, "queue data");
if (!q->trace_mini) {
ck_write(fr_fd, off, 1, "no_mini");
w += q_len + 1;
} else {
ck_write(fr_fd, on, 1, "yes_mini");
ck_write(fr_fd, q->trace_mini, m_len, "trace_mini");
w += q_len + m_len + 1;
}
}
close(fr_fd);
afl->var_byte_count = count_bytes(afl, afl->var_bytes);
OKF("Written fastresume.bin with %u bytes!", w);
} else {
WARNF("Could not create fastresume.bin");
}
}
destroy_queue(afl); destroy_queue(afl);
destroy_extras(afl); destroy_extras(afl);
destroy_custom_mutators(afl); destroy_custom_mutators(afl);

View File

@ -99,11 +99,13 @@ inline u64 hash64(u8 *key, u32 len, u64 seed) {
u64 get_binary_hash(u8 *fn) { u64 get_binary_hash(u8 *fn) {
if (!fn) { return 0; }
int fd = open(fn, O_RDONLY); int fd = open(fn, O_RDONLY);
if (fd < 0) { PFATAL("Unable to open '%s'", fn); } if (fd < 0) { PFATAL("Unable to open '%s'", fn); }
struct stat st; struct stat st;
if (fstat(fd, &st) < 0) { PFATAL("Unable to fstat '%s'", fn); } if (fstat(fd, &st) < 0) { PFATAL("Unable to fstat '%s'", fn); }
u32 f_len = st.st_size; u32 f_len = st.st_size;
if (!f_len) { return 0; }
u8 *f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); u8 *f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0);
if (f_data == MAP_FAILED) { PFATAL("Unable to mmap file '%s'", fn); } if (f_data == MAP_FAILED) { PFATAL("Unable to mmap file '%s'", fn); }
close(fd); close(fd);