diff options
Diffstat (limited to 'src/afl-fuzz-run.c')
-rw-r--r-- | src/afl-fuzz-run.c | 178 |
1 files changed, 102 insertions, 76 deletions
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index c14ecc87..4093d991 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -28,8 +28,8 @@ u8 run_target(char** argv, u32 timeout) { static struct itimerval it; - static u32 prev_timed_out = 0; - static u64 exec_ms = 0; + static u32 prev_timed_out = 0; + static u64 exec_ms = 0; int status = 0; u32 tb4; @@ -45,7 +45,7 @@ u8 run_target(char** argv, u32 timeout) { /* If we're running in "dumb" mode, we can't rely on the fork server logic compiled into the target program, so we will just keep calling - execve(). There is a bit of code duplication between here and + execve(). There is a bit of code duplication between here and init_forkserver(), but c'est la vie. */ if (dumb_mode == 1 || no_forkserver) { @@ -64,11 +64,11 @@ u8 run_target(char** argv, u32 timeout) { #ifdef RLIMIT_AS - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + setrlimit(RLIMIT_AS, &r); /* Ignore errors */ #else - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ + setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ #endif /* ^RLIMIT_AS */ @@ -76,7 +76,7 @@ u8 run_target(char** argv, u32 timeout) { r.rlim_max = r.rlim_cur = 0; - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ /* Isolate the process and configure standard descriptors. If out_file is specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */ @@ -108,10 +108,12 @@ u8 run_target(char** argv, u32 timeout) { /* Set sane defaults for ASAN if nothing else specified. */ - setenv("ASAN_OPTIONS", "abort_on_error=1:" - "detect_leaks=0:" - "symbolize=0:" - "allocator_may_return_null=1", 0); + setenv("ASAN_OPTIONS", + "abort_on_error=1:" + "detect_leaks=0:" + "symbolize=0:" + "allocator_may_return_null=1", + 0); setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" @@ -152,7 +154,8 @@ u8 run_target(char** argv, u32 timeout) { } - /* Configure timeout, as requested by user, then wait for child to terminate. */ + /* Configure timeout, as requested by user, then wait for child to terminate. + */ it.it_value.tv_sec = (timeout / 1000); it.it_value.tv_usec = (timeout % 1000) * 1000; @@ -179,9 +182,10 @@ u8 run_target(char** argv, u32 timeout) { } if (!WIFSTOPPED(status)) child_pid = 0; - + getitimer(ITIMER_REAL, &it); - exec_ms = (u64) timeout - (it.it_value.tv_sec * 1000 + it.it_value.tv_usec / 1000); + exec_ms = + (u64)timeout - (it.it_value.tv_sec * 1000 + it.it_value.tv_usec / 1000); if (slowest_exec_ms < exec_ms) slowest_exec_ms = exec_ms; it.it_value.tv_sec = 0; @@ -223,8 +227,10 @@ u8 run_target(char** argv, u32 timeout) { must use a special exit code. */ if (uses_asan && WEXITSTATUS(status) == MSAN_ERROR) { + kill_signal = 0; return FAULT_CRASH; + } if ((dumb_mode == 1 || no_forkserver) && tb4 == EXEC_FAIL_SIG) @@ -234,7 +240,6 @@ u8 run_target(char** argv, u32 timeout) { } - /* Write modified data to file for testing. If out_file is set, the old file is unlinked and a new one is created. Otherwise, out_fd is rewound and truncated. */ @@ -245,20 +250,26 @@ void write_to_testcase(void* mem, u32 len) { if (out_file) { - unlink(out_file); /* Ignore errors. */ + unlink(out_file); /* Ignore errors. */ fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0) PFATAL("Unable to create '%s'", out_file); - } else lseek(fd, 0, SEEK_SET); + } else + + lseek(fd, 0, SEEK_SET); if (pre_save_handler) { - u8* new_data; + + u8* new_data; size_t new_size = pre_save_handler(mem, len, &new_data); ck_write(fd, new_data, new_size, out_file); + } else { + ck_write(fd, mem, len, out_file); + } if (!out_file) { @@ -266,10 +277,11 @@ void write_to_testcase(void* mem, u32 len) { if (ftruncate(fd, len)) PFATAL("ftruncate() failed"); lseek(fd, 0, SEEK_SET); - } else close(fd); + } else -} + close(fd); +} /* The same, but with an adjustable gap. Used for trimming. */ @@ -280,17 +292,19 @@ void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) { if (out_file) { - unlink(out_file); /* Ignore errors. */ + unlink(out_file); /* Ignore errors. */ fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0) PFATAL("Unable to create '%s'", out_file); - } else lseek(fd, 0, SEEK_SET); + } else + + lseek(fd, 0, SEEK_SET); if (skip_at) ck_write(fd, mem, skip_at, out_file); - u8 *memu8 = mem; + u8* memu8 = mem; if (tail_len) ck_write(fd, memu8 + skip_at + skip_len, tail_len, out_file); if (!out_file) { @@ -298,22 +312,23 @@ void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) { if (ftruncate(fd, len - skip_len)) PFATAL("ftruncate() failed"); lseek(fd, 0, SEEK_SET); - } else close(fd); + } else -} + close(fd); +} /* Calibrate a new test case. This is done when processing the input directory to warn about flaky or otherwise problematic test cases early on; and when new paths are discovered to detect variable behavior and so on. */ -u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, - u32 handicap, u8 from_queue) { +u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, u32 handicap, + u8 from_queue) { static u8 first_trace[MAP_SIZE]; - u8 fault = 0, new_bits = 0, var_detected = 0, - first_run = (q->exec_cksum == 0); + u8 fault = 0, new_bits = 0, var_detected = 0, + first_run = (q->exec_cksum == 0); u64 start_us, stop_us; @@ -326,19 +341,18 @@ u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, to intermittent latency. */ if (!from_queue || resuming_fuzz) - use_tmout = MAX(exec_tmout + CAL_TMOUT_ADD, - exec_tmout * CAL_TMOUT_PERC / 100); + use_tmout = + MAX(exec_tmout + CAL_TMOUT_ADD, exec_tmout * CAL_TMOUT_PERC / 100); ++q->cal_failed; stage_name = "calibration"; - stage_max = fast_cal ? 3 : CAL_CYCLES; + stage_max = fast_cal ? 3 : CAL_CYCLES; /* Make sure the forkserver is up before we do anything, and let's not count its spin-up time toward binary calibration. */ - if (dumb_mode != 1 && !no_forkserver && !forksrv_pid) - init_forkserver(argv); + if (dumb_mode != 1 && !no_forkserver && !forksrv_pid) init_forkserver(argv); if (q->exec_cksum) memcpy(first_trace, trace_bits, MAP_SIZE); @@ -360,8 +374,10 @@ u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, if (stop_soon || fault != crash_mode) goto abort_calibration; if (!dumb_mode && !stage_cur && !count_bytes(trace_bits)) { + fault = FAULT_NOINST; goto abort_calibration; + } cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); @@ -380,7 +396,7 @@ u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, if (!var_bytes[i] && first_trace[i] != trace_bits[i]) { var_bytes[i] = 1; - stage_max = CAL_CYCLES_LONG; + stage_max = CAL_CYCLES_LONG; } @@ -401,16 +417,16 @@ u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, stop_us = get_cur_time_us(); - total_cal_us += stop_us - start_us; + total_cal_us += stop_us - start_us; total_cal_cycles += stage_max; /* OK, let's collect some stats about the performance of this test case. This is used for fuzzing air time calculations in calculate_score(). */ - q->exec_us = (stop_us - start_us) / stage_max; + q->exec_us = (stop_us - start_us) / stage_max; q->bitmap_size = count_bytes(trace_bits); - q->handicap = handicap; - q->cal_failed = 0; + q->handicap = handicap; + q->cal_failed = 0; total_bitmap_size += q->bitmap_size; ++total_bitmap_entries; @@ -426,8 +442,10 @@ u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, abort_calibration: if (new_bits == 2 && !q->has_new_cov) { + q->has_new_cov = 1; ++queued_with_cov; + } /* Mark variable paths. */ @@ -437,15 +455,17 @@ abort_calibration: var_byte_count = count_bytes(var_bytes); if (!q->var_behavior) { + mark_as_variable(q); ++queued_variable; + } } stage_name = old_sn; - stage_cur = old_sc; - stage_max = old_sm; + stage_cur = old_sc; + stage_max = old_sm; if (!first_run) show_stats(); @@ -453,14 +473,13 @@ abort_calibration: } - /* Grab interesting test cases from other fuzzers. */ void sync_fuzzers(char** argv) { - DIR* sd; + DIR* sd; struct dirent* sd_ent; - u32 sync_cnt = 0; + u32 sync_cnt = 0; sd = opendir(sync_dir); if (!sd) PFATAL("Unable to open '%s'", sync_dir); @@ -468,16 +487,17 @@ void sync_fuzzers(char** argv) { stage_max = stage_cur = 0; cur_depth = 0; - /* Look at the entries created for every other fuzzer in the sync directory. */ + /* Look at the entries created for every other fuzzer in the sync directory. + */ while ((sd_ent = readdir(sd))) { static u8 stage_tmp[128]; - DIR* qd; + DIR* qd; struct dirent* qd_ent; - u8 *qd_path, *qd_synced_path; - u32 min_accept = 0, next_min_accept; + u8 * qd_path, *qd_synced_path; + u32 min_accept = 0, next_min_accept; s32 id_fd; @@ -490,8 +510,10 @@ void sync_fuzzers(char** argv) { qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name); if (!(qd = opendir(qd_path))) { + ck_free(qd_path); continue; + } /* Retrieve the ID of the last seen test case. */ @@ -502,35 +524,34 @@ void sync_fuzzers(char** argv) { if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path); - if (read(id_fd, &min_accept, sizeof(u32)) > 0) - lseek(id_fd, 0, SEEK_SET); + if (read(id_fd, &min_accept, sizeof(u32)) > 0) lseek(id_fd, 0, SEEK_SET); next_min_accept = min_accept; - /* Show stats */ + /* Show stats */ sprintf(stage_tmp, "sync %u", ++sync_cnt); stage_name = stage_tmp; - stage_cur = 0; - stage_max = 0; + stage_cur = 0; + stage_max = 0; - /* For every file queued by this fuzzer, parse ID and see if we have looked at - it before; exec a test case if not. */ + /* For every file queued by this fuzzer, parse ID and see if we have looked + at it before; exec a test case if not. */ while ((qd_ent = readdir(qd))) { - u8* path; - s32 fd; + u8* path; + s32 fd; struct stat st; if (qd_ent->d_name[0] == '.' || - sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || - syncing_case < min_accept) continue; + sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || + syncing_case < min_accept) + continue; /* OK, sounds like a new one. Let's give it a try. */ - if (syncing_case >= next_min_accept) - next_min_accept = syncing_case + 1; + if (syncing_case >= next_min_accept) next_min_accept = syncing_case + 1; path = alloc_printf("%s/%s", qd_path, qd_ent->d_name); @@ -539,8 +560,10 @@ void sync_fuzzers(char** argv) { fd = open(path, O_RDONLY); if (fd < 0) { - ck_free(path); - continue; + + ck_free(path); + continue; + } if (fstat(fd, &st)) PFATAL("fstat() failed"); @@ -584,14 +607,13 @@ void sync_fuzzers(char** argv) { closedir(qd); ck_free(qd_path); ck_free(qd_synced_path); - - } + + } closedir(sd); } - /* Trim all new test cases to save cycles when doing deterministic checks. The trimmer uses power-of-two increments somewhere between 1/16 and 1/1024 of file size, to keep the stage short and sweet. */ @@ -599,8 +621,7 @@ void sync_fuzzers(char** argv) { u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) { #ifdef USE_PYTHON - if (py_functions[PY_FUNC_TRIM]) - return trim_case_python(argv, q, in_buf); + if (py_functions[PY_FUNC_TRIM]) return trim_case_python(argv, q, in_buf); #endif static u8 tmp[64]; @@ -664,9 +685,9 @@ u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) { u32 move_tail = q->len - remove_pos - trim_avail; q->len -= trim_avail; - len_p2 = next_p2(q->len); + len_p2 = next_p2(q->len); - memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, + memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, move_tail); /* Let's save a clean trace, which will be needed by @@ -679,7 +700,9 @@ u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) { } - } else remove_pos += remove_len; + } else + + remove_pos += remove_len; /* Since this can be slow, update the screen every now and then. */ @@ -699,7 +722,7 @@ u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) { s32 fd; - unlink(q->fname); /* ignore errors */ + unlink(q->fname); /* ignore errors */ fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); @@ -720,7 +743,6 @@ abort_trimming: } - /* Write a modified test case, run program, process results. Handle error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ @@ -745,20 +767,24 @@ u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) { if (fault == FAULT_TMOUT) { if (subseq_tmouts++ > TMOUT_LIMIT) { + ++cur_skipped_paths; return 1; + } - } else subseq_tmouts = 0; + } else + + subseq_tmouts = 0; /* Users can hit us with SIGUSR1 to request the current input to be abandoned. */ if (skip_requested) { - skip_requested = 0; - ++cur_skipped_paths; - return 1; + skip_requested = 0; + ++cur_skipped_paths; + return 1; } |