|
|
|
@ -3508,14 +3508,17 @@ static void write_stats_file(double bitmap_cvg, double stability, double eps) {
|
|
|
|
|
|
|
|
|
|
static void maybe_update_plot_file(double bitmap_cvg, double eps) {
|
|
|
|
|
|
|
|
|
|
// 静态变量,用于存储上一次的状态值
|
|
|
|
|
static u32 prev_qp, prev_pf, prev_pnf, prev_ce, prev_md;
|
|
|
|
|
static u64 prev_qc, prev_uc, prev_uh;
|
|
|
|
|
|
|
|
|
|
// 如果当前状态与上一次状态相同,则直接返回,避免重复更新
|
|
|
|
|
if (prev_qp == queued_paths && prev_pf == pending_favored &&
|
|
|
|
|
prev_pnf == pending_not_fuzzed && prev_ce == current_entry &&
|
|
|
|
|
prev_qc == queue_cycle && prev_uc == unique_crashes &&
|
|
|
|
|
prev_uh == unique_hangs && prev_md == max_depth) return;
|
|
|
|
|
|
|
|
|
|
// 更新上一次的状态值为当前状态值
|
|
|
|
|
prev_qp = queued_paths;
|
|
|
|
|
prev_pf = pending_favored;
|
|
|
|
|
prev_pnf = pending_not_fuzzed;
|
|
|
|
@ -3525,56 +3528,25 @@ static void maybe_update_plot_file(double bitmap_cvg, double eps) {
|
|
|
|
|
prev_uh = unique_hangs;
|
|
|
|
|
prev_md = max_depth;
|
|
|
|
|
|
|
|
|
|
/* Fields in the file:
|
|
|
|
|
|
|
|
|
|
/* 文件中的字段:
|
|
|
|
|
unix_time, cycles_done, cur_path, paths_total, paths_not_fuzzed,
|
|
|
|
|
favored_not_fuzzed, unique_crashes, unique_hangs, max_depth,
|
|
|
|
|
execs_per_sec */
|
|
|
|
|
|
|
|
|
|
// 将当前状态写入绘图文件
|
|
|
|
|
fprintf(plot_file,
|
|
|
|
|
"%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f\n",
|
|
|
|
|
get_cur_time() / 1000, queue_cycle - 1, current_entry, queued_paths,
|
|
|
|
|
pending_not_fuzzed, pending_favored, bitmap_cvg, unique_crashes,
|
|
|
|
|
unique_hangs, max_depth, eps); /* ignore errors */
|
|
|
|
|
unique_hangs, max_depth, eps); /* 忽略错误 */
|
|
|
|
|
|
|
|
|
|
// 刷新文件缓冲区,确保数据写入文件
|
|
|
|
|
fflush(plot_file);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* A helper function for maybe_delete_out_dir(), deleting all prefixed
|
|
|
|
|
files in a directory. */
|
|
|
|
|
|
|
|
|
|
static u8 delete_files(u8* path, u8* prefix) {
|
|
|
|
|
|
|
|
|
|
DIR* d;
|
|
|
|
|
struct dirent* d_ent;
|
|
|
|
|
|
|
|
|
|
d = opendir(path);
|
|
|
|
|
|
|
|
|
|
if (!d) return 0;
|
|
|
|
|
|
|
|
|
|
while ((d_ent = readdir(d))) {
|
|
|
|
|
|
|
|
|
|
if (d_ent->d_name[0] != '.' && (!prefix ||
|
|
|
|
|
!strncmp(d_ent->d_name, prefix, strlen(prefix)))) {
|
|
|
|
|
|
|
|
|
|
u8* fname = alloc_printf("%s/%s", path, d_ent->d_name);
|
|
|
|
|
if (unlink(fname)) PFATAL("Unable to delete '%s'", fname);
|
|
|
|
|
ck_free(fname);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closedir(d);
|
|
|
|
|
|
|
|
|
|
return !!rmdir(path);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get the number of runnable processes, with some simple smoothing. */
|
|
|
|
|
|
|
|
|
|
static double get_runnable_processes(void) {
|
|
|
|
@ -3587,7 +3559,7 @@ static double get_runnable_processes(void) {
|
|
|
|
|
number of runnable processes; the 1-minute load average can be a
|
|
|
|
|
semi-decent approximation, though. */
|
|
|
|
|
|
|
|
|
|
if (getloadavg(&res, 1) != 1) return 0;
|
|
|
|
|
if (getloadavg(&res, 1) != 1) return 0; // 获取1分钟的负载平均值,如果失败则返回0
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
@ -3595,74 +3567,74 @@ static double get_runnable_processes(void) {
|
|
|
|
|
computed in funny ways and sometimes don't reflect extremely short-lived
|
|
|
|
|
processes well. */
|
|
|
|
|
|
|
|
|
|
FILE* f = fopen("/proc/stat", "r");
|
|
|
|
|
u8 tmp[1024];
|
|
|
|
|
u32 val = 0;
|
|
|
|
|
FILE* f = fopen("/proc/stat", "r"); // 打开/proc/stat文件以读取系统统计信息
|
|
|
|
|
u8 tmp[1024]; // 用于存储读取的每一行内容
|
|
|
|
|
u32 val = 0; // 用于存储当前运行或阻塞的进程数
|
|
|
|
|
|
|
|
|
|
if (!f) return 0;
|
|
|
|
|
if (!f) return 0; // 如果文件打开失败,返回0
|
|
|
|
|
|
|
|
|
|
while (fgets(tmp, sizeof(tmp), f)) {
|
|
|
|
|
while (fgets(tmp, sizeof(tmp), f)) { // 逐行读取文件内容
|
|
|
|
|
|
|
|
|
|
if (!strncmp(tmp, "procs_running ", 14) ||
|
|
|
|
|
!strncmp(tmp, "procs_blocked ", 14)) val += atoi(tmp + 14);
|
|
|
|
|
if (!strncmp(tmp, "procs_running ", 14) || // 如果行以"procs_running "开头
|
|
|
|
|
!strncmp(tmp, "procs_blocked ", 14)) val += atoi(tmp + 14); // 或者以"procs_blocked "开头,提取数值并累加
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
fclose(f); // 关闭文件
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
if (!res) { // 如果res为0(即第一次计算)
|
|
|
|
|
|
|
|
|
|
res = val;
|
|
|
|
|
res = val; // 直接将当前值赋给res
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
} else { // 否则,进行平滑处理
|
|
|
|
|
|
|
|
|
|
res = res * (1.0 - 1.0 / AVG_SMOOTHING) +
|
|
|
|
|
res = res * (1.0 - 1.0 / AVG_SMOOTHING) + // 使用平滑公式计算新的res值
|
|
|
|
|
((double)val) * (1.0 / AVG_SMOOTHING);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
return res; // 返回计算得到的res值
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Delete the temporary directory used for in-place session resume. */
|
|
|
|
|
|
|
|
|
|
static void nuke_resume_dir(void) {
|
|
|
|
|
static void nuke_resume_dir(void) { // 删除用于恢复会话的临时目录
|
|
|
|
|
|
|
|
|
|
u8* fn;
|
|
|
|
|
u8* fn; // 用于存储文件路径
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state/deterministic_done", out_dir);
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state/deterministic_done", out_dir); // 构建文件路径
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除文件,如果失败则跳转到清理失败处理
|
|
|
|
|
ck_free(fn); // 释放内存
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state/auto_extras", out_dir);
|
|
|
|
|
if (delete_files(fn, "auto_")) goto dir_cleanup_failed;
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state/auto_extras", out_dir); // 构建文件路径
|
|
|
|
|
if (delete_files(fn, "auto_")) goto dir_cleanup_failed; // 删除文件,如果失败则跳转到清理失败处理
|
|
|
|
|
ck_free(fn); // 释放内存
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state/redundant_edges", out_dir);
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state/redundant_edges", out_dir); // 构建文件路径
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除文件,如果失败则跳转到清理失败处理
|
|
|
|
|
ck_free(fn); // 释放内存
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state/variable_behavior", out_dir);
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state/variable_behavior", out_dir); // 构建文件路径
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除文件,如果失败则跳转到清理失败处理
|
|
|
|
|
ck_free(fn); // 释放内存
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state", out_dir);
|
|
|
|
|
if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed;
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
fn = alloc_printf("%s/_resume/.state", out_dir); // 构建文件路径
|
|
|
|
|
if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除目录,如果失败且错误不是目录不存在则跳转到清理失败处理
|
|
|
|
|
ck_free(fn); // 释放内存
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/_resume", out_dir);
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
fn = alloc_printf("%s/_resume", out_dir); // 构建文件路径
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除文件,如果失败则跳转到清理失败处理
|
|
|
|
|
ck_free(fn); // 释放内存
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
return; // 成功完成清理,返回
|
|
|
|
|
|
|
|
|
|
dir_cleanup_failed:
|
|
|
|
|
dir_cleanup_failed: // 清理失败处理
|
|
|
|
|
|
|
|
|
|
FATAL("_resume directory cleanup failed");
|
|
|
|
|
FATAL("_resume directory cleanup failed"); // 输出致命错误信息并终止程序
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3670,51 +3642,52 @@ dir_cleanup_failed:
|
|
|
|
|
/* Delete fuzzer output directory if we recognize it as ours, if the fuzzer
|
|
|
|
|
is not currently running, and if the last run time isn't too great. */
|
|
|
|
|
|
|
|
|
|
static void maybe_delete_out_dir(void) {
|
|
|
|
|
static void maybe_delete_out_dir(void) { // 可能删除fuzzer输出目录
|
|
|
|
|
|
|
|
|
|
FILE* f;
|
|
|
|
|
u8 *fn = alloc_printf("%s/fuzzer_stats", out_dir);
|
|
|
|
|
FILE* f; // 文件指针
|
|
|
|
|
u8 *fn = alloc_printf("%s/fuzzer_stats", out_dir); // 构建文件路径
|
|
|
|
|
|
|
|
|
|
/* See if the output directory is locked. If yes, bail out. If not,
|
|
|
|
|
create a lock that will persist for the lifetime of the process
|
|
|
|
|
(this requires leaving the descriptor open).*/
|
|
|
|
|
|
|
|
|
|
out_dir_fd = open(out_dir, O_RDONLY);
|
|
|
|
|
if (out_dir_fd < 0) PFATAL("Unable to open '%s'", out_dir);
|
|
|
|
|
out_dir_fd = open(out_dir, O_RDONLY); // 打开输出目录
|
|
|
|
|
if (out_dir_fd < 0) PFATAL("Unable to open '%s'", out_dir); // 如果打开失败,输出致命错误信息并终止程序
|
|
|
|
|
|
|
|
|
|
#ifndef __sun
|
|
|
|
|
|
|
|
|
|
if (flock(out_dir_fd, LOCK_EX | LOCK_NB) && errno == EWOULDBLOCK) {
|
|
|
|
|
#ifndef __sun // 如果不是在SunOS系统上
|
|
|
|
|
|
|
|
|
|
SAYF("\n" cLRD "[-] " cRST
|
|
|
|
|
if (flock(out_dir_fd, LOCK_EX | LOCK_NB) && errno == EWOULDBLOCK) { // 尝试对输出目录文件描述符加锁,如果锁被占用且错误码为EWOULDBLOCK
|
|
|
|
|
|
|
|
|
|
SAYF("\n" cLRD "[-] " cRST // 输出错误信息
|
|
|
|
|
"Looks like the job output directory is being actively used by another\n"
|
|
|
|
|
" instance of afl-fuzz. You will need to choose a different %s\n"
|
|
|
|
|
" or stop the other process first.\n",
|
|
|
|
|
sync_id ? "fuzzer ID" : "output location");
|
|
|
|
|
sync_id ? "fuzzer ID" : "output location"); // 根据sync_id选择输出不同的提示信息
|
|
|
|
|
|
|
|
|
|
FATAL("Directory '%s' is in use", out_dir);
|
|
|
|
|
FATAL("Directory '%s' is in use", out_dir); // 输出致命错误信息并退出
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* !__sun */
|
|
|
|
|
#endif /* !__sun */ // 结束条件编译
|
|
|
|
|
|
|
|
|
|
f = fopen(fn, "r");
|
|
|
|
|
f = fopen(fn, "r"); // 打开文件fn以读取
|
|
|
|
|
|
|
|
|
|
if (f) {
|
|
|
|
|
if (f) { // 如果文件成功打开
|
|
|
|
|
|
|
|
|
|
u64 start_time, last_update;
|
|
|
|
|
u64 start_time, last_update; // 定义两个64位无符号整数变量
|
|
|
|
|
|
|
|
|
|
if (fscanf(f, "start_time : %llu\n"
|
|
|
|
|
if (fscanf(f, "start_time : %llu\n" // 从文件中读取start_time和last_update
|
|
|
|
|
"last_update : %llu\n", &start_time, &last_update) != 2)
|
|
|
|
|
FATAL("Malformed data in '%s'", fn);
|
|
|
|
|
FATAL("Malformed data in '%s'", fn); // 如果读取失败,输出致命错误信息并退出
|
|
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
fclose(f); // 关闭文件
|
|
|
|
|
|
|
|
|
|
/* Let's see how much work is at stake. */
|
|
|
|
|
/* Let's see how much work is at stake. */ // 检查是否有大量工作数据
|
|
|
|
|
|
|
|
|
|
if (!in_place_resume && last_update - start_time > OUTPUT_GRACE * 60) {
|
|
|
|
|
if (!in_place_resume && last_update - start_time > OUTPUT_GRACE * 60) { // 如果不是原地恢复模式且时间差超过OUTPUT_GRACE分钟
|
|
|
|
|
|
|
|
|
|
SAYF("\n" cLRD "[-] " cRST
|
|
|
|
|
SAYF("\n" cLRD "[-] " cRST // 输出警告信息
|
|
|
|
|
"The job output directory already exists and contains the results of more\n"
|
|
|
|
|
" than %u minutes worth of fuzzing. To avoid data loss, afl-fuzz will *NOT*\n"
|
|
|
|
|
" automatically delete this data for you.\n\n"
|
|
|
|
@ -3724,94 +3697,94 @@ static void maybe_delete_out_dir(void) {
|
|
|
|
|
" session, put '-' as the input directory in the command line ('-i -') and\n"
|
|
|
|
|
" try again.\n", OUTPUT_GRACE);
|
|
|
|
|
|
|
|
|
|
FATAL("At-risk data found in '%s'", out_dir);
|
|
|
|
|
FATAL("At-risk data found in '%s'", out_dir); // 输出致命错误信息并退出
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
ck_free(fn); // 释放fn的内存
|
|
|
|
|
|
|
|
|
|
/* The idea for in-place resume is pretty simple: we temporarily move the old
|
|
|
|
|
queue/ to a new location that gets deleted once import to the new queue/
|
|
|
|
|
is finished. If _resume/ already exists, the current queue/ may be
|
|
|
|
|
incomplete due to an earlier abort, so we want to use the old _resume/
|
|
|
|
|
dir instead, and we let rename() fail silently. */
|
|
|
|
|
dir instead, and we let rename() fail silently. */ // 原地恢复的逻辑
|
|
|
|
|
|
|
|
|
|
if (in_place_resume) {
|
|
|
|
|
if (in_place_resume) { // 如果是原地恢复模式
|
|
|
|
|
|
|
|
|
|
u8* orig_q = alloc_printf("%s/queue", out_dir);
|
|
|
|
|
u8* orig_q = alloc_printf("%s/queue", out_dir); // 分配内存并格式化字符串
|
|
|
|
|
|
|
|
|
|
in_dir = alloc_printf("%s/_resume", out_dir);
|
|
|
|
|
in_dir = alloc_printf("%s/_resume", out_dir); // 分配内存并格式化字符串
|
|
|
|
|
|
|
|
|
|
rename(orig_q, in_dir); /* Ignore errors */
|
|
|
|
|
rename(orig_q, in_dir); /* Ignore errors */ // 将orig_q重命名为in_dir,忽略错误
|
|
|
|
|
|
|
|
|
|
OKF("Output directory exists, will attempt session resume.");
|
|
|
|
|
OKF("Output directory exists, will attempt session resume."); // 输出成功信息
|
|
|
|
|
|
|
|
|
|
ck_free(orig_q);
|
|
|
|
|
ck_free(orig_q); // 释放orig_q的内存
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
} else { // 如果不是原地恢复模式
|
|
|
|
|
|
|
|
|
|
OKF("Output directory exists but deemed OK to reuse.");
|
|
|
|
|
OKF("Output directory exists but deemed OK to reuse."); // 输出成功信息
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ACTF("Deleting old session data...");
|
|
|
|
|
ACTF("Deleting old session data..."); // 输出动作信息
|
|
|
|
|
|
|
|
|
|
/* Okay, let's get the ball rolling! First, we need to get rid of the entries
|
|
|
|
|
in <out_dir>/.synced/.../id:*, if any are present. */
|
|
|
|
|
in <out_dir>/.synced/.../id:*, if any are present. */ // 开始清理旧会话数据
|
|
|
|
|
|
|
|
|
|
if (!in_place_resume) {
|
|
|
|
|
if (!in_place_resume) { // 如果不是原地恢复模式
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/.synced", out_dir);
|
|
|
|
|
if (delete_files(fn, NULL)) goto dir_cleanup_failed;
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
fn = alloc_printf("%s/.synced", out_dir); // 分配内存并格式化字符串
|
|
|
|
|
if (delete_files(fn, NULL)) goto dir_cleanup_failed; // 删除文件,如果失败则跳转到dir_cleanup_failed
|
|
|
|
|
ck_free(fn); // 释放fn的内存
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Next, we need to clean up <out_dir>/queue/.state/ subdirectories: */
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/queue/.state/deterministic_done", out_dir);
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除确定性测试完成标志文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/queue/.state/auto_extras", out_dir);
|
|
|
|
|
if (delete_files(fn, "auto_")) goto dir_cleanup_failed;
|
|
|
|
|
if (delete_files(fn, "auto_")) goto dir_cleanup_failed; // 删除自动生成的额外文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/queue/.state/redundant_edges", out_dir);
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除冗余边文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/queue/.state/variable_behavior", out_dir);
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除变量行为文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
/* Then, get rid of the .state subdirectory itself (should be empty by now)
|
|
|
|
|
and everything matching <out_dir>/queue/id:*. */
|
|
|
|
|
/* 然后,删除.state子目录本身(此时应该为空)
|
|
|
|
|
并删除所有匹配<out_dir>/queue/id:*的文件。 */
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/queue/.state", out_dir);
|
|
|
|
|
if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed;
|
|
|
|
|
if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除.state目录
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/queue", out_dir);
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除queue目录下的文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
/* All right, let's do <out_dir>/crashes/id:* and <out_dir>/hangs/id:*. */
|
|
|
|
|
/* 接下来,处理<out_dir>/crashes/id:*和<out_dir>/hangs/id:*。 */
|
|
|
|
|
|
|
|
|
|
if (!in_place_resume) {
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/crashes/README.txt", out_dir);
|
|
|
|
|
unlink(fn); /* Ignore errors */
|
|
|
|
|
unlink(fn); /* 忽略错误 */ // 删除crashes目录下的README.txt文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/crashes", out_dir);
|
|
|
|
|
|
|
|
|
|
/* Make backup of the crashes directory if it's not empty and if we're
|
|
|
|
|
doing in-place resume. */
|
|
|
|
|
/* 如果crashes目录不为空且正在进行原地恢复,则备份crashes目录。 */
|
|
|
|
|
|
|
|
|
|
if (in_place_resume && rmdir(fn)) {
|
|
|
|
|
|
|
|
|
@ -3832,17 +3805,17 @@ static void maybe_delete_out_dir(void) {
|
|
|
|
|
|
|
|
|
|
#endif /* ^!SIMPLE_FILES */
|
|
|
|
|
|
|
|
|
|
rename(fn, nfn); /* Ignore errors. */
|
|
|
|
|
rename(fn, nfn); /* 忽略错误。 */ // 重命名crashes目录以备份
|
|
|
|
|
ck_free(nfn);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除crashes目录下的文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/hangs", out_dir);
|
|
|
|
|
|
|
|
|
|
/* Backup hangs, too. */
|
|
|
|
|
/* 同样备份hangs目录。 */
|
|
|
|
|
|
|
|
|
|
if (in_place_resume && rmdir(fn)) {
|
|
|
|
|
|
|
|
|
@ -3863,53 +3836,49 @@ static void maybe_delete_out_dir(void) {
|
|
|
|
|
|
|
|
|
|
#endif /* ^!SIMPLE_FILES */
|
|
|
|
|
|
|
|
|
|
rename(fn, nfn); /* Ignore errors. */
|
|
|
|
|
rename(fn, nfn); /* 忽略错误。 */ // 重命名hangs目录以备份
|
|
|
|
|
ck_free(nfn);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
|
|
|
|
|
if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除hangs目录下的文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
/* And now, for some finishing touches. */
|
|
|
|
|
/* 现在,进行一些收尾工作。 */
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/.cur_input", out_dir);
|
|
|
|
|
if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed;
|
|
|
|
|
if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除当前输入文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/fuzz_bitmap", out_dir);
|
|
|
|
|
if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed;
|
|
|
|
|
if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除fuzz位图文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
if (!in_place_resume) {
|
|
|
|
|
fn = alloc_printf("%s/fuzzer_stats", out_dir);
|
|
|
|
|
if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed;
|
|
|
|
|
if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除fuzzer统计文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn = alloc_printf("%s/plot_data", out_dir);
|
|
|
|
|
if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed;
|
|
|
|
|
if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除绘图数据文件
|
|
|
|
|
ck_free(fn);
|
|
|
|
|
|
|
|
|
|
OKF("Output dir cleanup successful.");
|
|
|
|
|
OKF("Output dir cleanup successful."); // 输出目录清理成功
|
|
|
|
|
|
|
|
|
|
/* Wow... is that all? If yes, celebrate! */
|
|
|
|
|
/* 哇...这就完了吗?如果是,庆祝一下! */
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
dir_cleanup_failed:
|
|
|
|
|
|
|
|
|
|
SAYF("\n" cLRD "[-] " cRST
|
|
|
|
|
"Whoops, the fuzzer tried to reuse your output directory, but bumped into\n"
|
|
|
|
|
" some files that shouldn't be there or that couldn't be removed - so it\n"
|
|
|
|
|
" decided to abort! This happened while processing this path:\n\n"
|
|
|
|
|
"哎呀,fuzzer试图重用你的输出目录,但遇到了一些不应该存在的文件或无法删除的文件 - 所以它决定中止!这发生在处理以下路径时:\n\n"
|
|
|
|
|
|
|
|
|
|
" %s\n\n"
|
|
|
|
|
" Please examine and manually delete the files, or specify a different\n"
|
|
|
|
|
" output location for the tool.\n", fn);
|
|
|
|
|
|
|
|
|
|
FATAL("Output directory cleanup failed");
|
|
|
|
|
" 请检查并手动删除这些文件,或为工具指定一个不同的输出位置。\n", fn); // 输出目录清理失败的错误信息
|
|
|
|
|
|
|
|
|
|
FATAL("Output directory cleanup failed"); // 输出目录清理失败
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -3921,148 +3890,137 @@ static void check_term_size(void);
|
|
|
|
|
|
|
|
|
|
static void show_stats(void) {
|
|
|
|
|
|
|
|
|
|
static u64 last_stats_ms, last_plot_ms, last_ms, last_execs;
|
|
|
|
|
static double avg_exec;
|
|
|
|
|
double t_byte_ratio, stab_ratio;
|
|
|
|
|
|
|
|
|
|
u64 cur_ms;
|
|
|
|
|
u32 t_bytes, t_bits;
|
|
|
|
|
static u64 last_stats_ms, last_plot_ms, last_ms, last_execs; // 上一次统计时间、绘图时间、UI更新时间、执行次数
|
|
|
|
|
static double avg_exec; // 平均执行速度
|
|
|
|
|
double t_byte_ratio, stab_ratio; // 字节覆盖率和稳定性比率
|
|
|
|
|
|
|
|
|
|
u32 banner_len, banner_pad;
|
|
|
|
|
u8 tmp[256];
|
|
|
|
|
u64 cur_ms; // 当前时间
|
|
|
|
|
u32 t_bytes, t_bits; // 非255字节数和非255位数
|
|
|
|
|
|
|
|
|
|
cur_ms = get_cur_time();
|
|
|
|
|
u32 banner_len, banner_pad; // 横幅长度和填充
|
|
|
|
|
u8 tmp[256]; // 临时缓冲区
|
|
|
|
|
|
|
|
|
|
/* If not enough time has passed since last UI update, bail out. */
|
|
|
|
|
cur_ms = get_cur_time(); // 获取当前时间
|
|
|
|
|
|
|
|
|
|
/* 如果距离上一次UI更新的时间不足,直接返回 */
|
|
|
|
|
if (cur_ms - last_ms < 1000 / UI_TARGET_HZ) return;
|
|
|
|
|
|
|
|
|
|
/* Check if we're past the 10 minute mark. */
|
|
|
|
|
|
|
|
|
|
/* 检查是否已经运行超过10分钟 */
|
|
|
|
|
if (cur_ms - start_time > 10 * 60 * 1000) run_over10m = 1;
|
|
|
|
|
|
|
|
|
|
/* Calculate smoothed exec speed stats. */
|
|
|
|
|
|
|
|
|
|
/* 计算平滑的执行速度统计 */
|
|
|
|
|
if (!last_execs) {
|
|
|
|
|
|
|
|
|
|
avg_exec = ((double)total_execs) * 1000 / (cur_ms - start_time);
|
|
|
|
|
avg_exec = ((double)total_execs) * 1000 / (cur_ms - start_time); // 初始平均执行速度
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
double cur_avg = ((double)(total_execs - last_execs)) * 1000 /
|
|
|
|
|
(cur_ms - last_ms);
|
|
|
|
|
|
|
|
|
|
/* If there is a dramatic (5x+) jump in speed, reset the indicator
|
|
|
|
|
more quickly. */
|
|
|
|
|
(cur_ms - last_ms); // 当前平均执行速度
|
|
|
|
|
|
|
|
|
|
/* 如果速度有显著变化(5倍以上),快速重置指示器 */
|
|
|
|
|
if (cur_avg * 5 < avg_exec || cur_avg / 5 > avg_exec)
|
|
|
|
|
avg_exec = cur_avg;
|
|
|
|
|
|
|
|
|
|
avg_exec = avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) +
|
|
|
|
|
cur_avg * (1.0 / AVG_SMOOTHING);
|
|
|
|
|
cur_avg * (1.0 / AVG_SMOOTHING); // 平滑处理
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_ms = cur_ms;
|
|
|
|
|
last_execs = total_execs;
|
|
|
|
|
|
|
|
|
|
/* Tell the callers when to contact us (as measured in execs). */
|
|
|
|
|
last_ms = cur_ms; // 更新上一次UI更新时间
|
|
|
|
|
last_execs = total_execs; // 更新上一次执行次数
|
|
|
|
|
|
|
|
|
|
/* 告诉调用者何时联系我们(以执行次数衡量) */
|
|
|
|
|
stats_update_freq = avg_exec / (UI_TARGET_HZ * 10);
|
|
|
|
|
if (!stats_update_freq) stats_update_freq = 1;
|
|
|
|
|
|
|
|
|
|
/* Do some bitmap stats. */
|
|
|
|
|
|
|
|
|
|
t_bytes = count_non_255_bytes(virgin_bits);
|
|
|
|
|
t_byte_ratio = ((double)t_bytes * 100) / MAP_SIZE;
|
|
|
|
|
/* 计算一些位图统计 */
|
|
|
|
|
t_bytes = count_non_255_bytes(virgin_bits); // 计算非255字节数
|
|
|
|
|
t_byte_ratio = ((double)t_bytes * 100) / MAP_SIZE; // 计算字节覆盖率
|
|
|
|
|
|
|
|
|
|
if (t_bytes)
|
|
|
|
|
stab_ratio = 100 - ((double)var_byte_count) * 100 / t_bytes;
|
|
|
|
|
stab_ratio = 100 - ((double)var_byte_count) * 100 / t_bytes; // 计算稳定性比率
|
|
|
|
|
else
|
|
|
|
|
stab_ratio = 100;
|
|
|
|
|
|
|
|
|
|
/* Roughly every minute, update fuzzer stats and save auto tokens. */
|
|
|
|
|
|
|
|
|
|
/* 大约每分钟更新一次fuzzer统计并保存自动生成的token */
|
|
|
|
|
if (cur_ms - last_stats_ms > STATS_UPDATE_SEC * 1000) {
|
|
|
|
|
|
|
|
|
|
last_stats_ms = cur_ms;
|
|
|
|
|
write_stats_file(t_byte_ratio, stab_ratio, avg_exec);
|
|
|
|
|
save_auto();
|
|
|
|
|
write_bitmap();
|
|
|
|
|
write_stats_file(t_byte_ratio, stab_ratio, avg_exec); // 写入统计文件
|
|
|
|
|
save_auto(); // 保存自动生成的token
|
|
|
|
|
write_bitmap(); // 写入位图
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Every now and then, write plot data. */
|
|
|
|
|
|
|
|
|
|
/* 每隔一段时间,写入绘图数据 */
|
|
|
|
|
if (cur_ms - last_plot_ms > PLOT_UPDATE_SEC * 1000) {
|
|
|
|
|
|
|
|
|
|
last_plot_ms = cur_ms;
|
|
|
|
|
maybe_update_plot_file(t_byte_ratio, avg_exec);
|
|
|
|
|
maybe_update_plot_file(t_byte_ratio, avg_exec); // 更新绘图文件
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */
|
|
|
|
|
|
|
|
|
|
/* 处理AFL_EXIT_WHEN_DONE和AFL_BENCH_UNTIL_CRASH环境变量 */
|
|
|
|
|
if (!dumb_mode && cycles_wo_finds > 100 && !pending_not_fuzzed &&
|
|
|
|
|
getenv("AFL_EXIT_WHEN_DONE")) stop_soon = 2;
|
|
|
|
|
|
|
|
|
|
if (total_crashes && getenv("AFL_BENCH_UNTIL_CRASH")) stop_soon = 2;
|
|
|
|
|
|
|
|
|
|
/* If we're not on TTY, bail out. */
|
|
|
|
|
|
|
|
|
|
/* 如果不是在TTY上运行,直接返回 */
|
|
|
|
|
if (not_on_tty) return;
|
|
|
|
|
|
|
|
|
|
/* Compute some mildly useful bitmap stats. */
|
|
|
|
|
|
|
|
|
|
t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits);
|
|
|
|
|
|
|
|
|
|
/* Now, for the visuals... */
|
|
|
|
|
/* 计算一些有用的位图统计 */
|
|
|
|
|
t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits); // 计算非255位数
|
|
|
|
|
|
|
|
|
|
/* 现在,处理视觉效果... */
|
|
|
|
|
if (clear_screen) {
|
|
|
|
|
|
|
|
|
|
SAYF(TERM_CLEAR CURSOR_HIDE);
|
|
|
|
|
SAYF(TERM_CLEAR CURSOR_HIDE); // 清屏并隐藏光标
|
|
|
|
|
clear_screen = 0;
|
|
|
|
|
|
|
|
|
|
check_term_size();
|
|
|
|
|
check_term_size(); // 检查终端大小
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SAYF(TERM_HOME);
|
|
|
|
|
|
|
|
|
|
if (term_too_small) {
|
|
|
|
|
SAYF(TERM_HOME); // 将光标移动到终端窗口的左上角
|
|
|
|
|
|
|
|
|
|
if (term_too_small) { // 检查终端窗口是否太小
|
|
|
|
|
|
|
|
|
|
SAYF(cBRI "Your terminal is too small to display the UI.\n"
|
|
|
|
|
"Please resize terminal window to at least 80x25.\n" cRST);
|
|
|
|
|
"Please resize terminal window to at least 80x25.\n" cRST); // 输出提示信息,要求用户调整终端窗口大小
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
return; // 如果终端窗口太小,直接返回,不继续绘制UI
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Let's start by drawing a centered banner. */
|
|
|
|
|
/* Let's start by drawing a centered banner. */ // 开始绘制居中的横幅
|
|
|
|
|
|
|
|
|
|
banner_len = (crash_mode ? 24 : 22) + strlen(VERSION) + strlen(use_banner);
|
|
|
|
|
banner_pad = (80 - banner_len) / 2;
|
|
|
|
|
memset(tmp, ' ', banner_pad);
|
|
|
|
|
banner_len = (crash_mode ? 24 : 22) + strlen(VERSION) + strlen(use_banner); // 计算横幅的总长度,根据是否处于crash_mode调整基础长度
|
|
|
|
|
banner_pad = (80 - banner_len) / 2; // 计算横幅两侧的填充空格数,使横幅居中
|
|
|
|
|
memset(tmp, ' ', banner_pad); // 在tmp数组的前banner_pad个位置填充空格
|
|
|
|
|
|
|
|
|
|
sprintf(tmp + banner_pad, "%s " cLCY VERSION cLGN
|
|
|
|
|
" (%s)", crash_mode ? cPIN "peruvian were-rabbit" :
|
|
|
|
|
cYEL "american fuzzy lop", use_banner);
|
|
|
|
|
cYEL "american fuzzy lop", use_banner); // 格式化横幅内容,包含版本号和banner信息,根据crash_mode选择不同的显示文本
|
|
|
|
|
|
|
|
|
|
SAYF("\n%s\n\n", tmp);
|
|
|
|
|
SAYF("\n%s\n\n", tmp); // 输出横幅内容,前后添加换行符
|
|
|
|
|
|
|
|
|
|
/* "Handy" shortcuts for drawing boxes... */
|
|
|
|
|
/* "Handy" shortcuts for drawing boxes... */ // 定义绘制框的快捷方式
|
|
|
|
|
|
|
|
|
|
#define bSTG bSTART cGRA
|
|
|
|
|
#define bH2 bH bH
|
|
|
|
|
#define bH5 bH2 bH2 bH
|
|
|
|
|
#define bH10 bH5 bH5
|
|
|
|
|
#define bH20 bH10 bH10
|
|
|
|
|
#define bH30 bH20 bH10
|
|
|
|
|
#define SP5 " "
|
|
|
|
|
#define SP10 SP5 SP5
|
|
|
|
|
#define SP20 SP10 SP10
|
|
|
|
|
#define bSTG bSTART cGRA // 定义bSTG为bSTART和cGRA的组合,用于绘制框的起始部分
|
|
|
|
|
#define bH2 bH bH // 定义bH2为两个bH的组合,用于绘制水平线
|
|
|
|
|
#define bH5 bH2 bH2 bH // 定义bH5为两个bH2和一个bH的组合,用于绘制更长的水平线
|
|
|
|
|
#define bH10 bH5 bH5 // 定义bH10为两个bH5的组合,用于绘制更长的水平线
|
|
|
|
|
#define bH20 bH10 bH10 // 定义bH20为两个bH10的组合,用于绘制更长的水平线
|
|
|
|
|
#define bH30 bH20 bH10 // 定义bH30为一个bH20和一个bH10的组合,用于绘制更长的水平线
|
|
|
|
|
#define SP5 " " // 定义SP5为5个空格,用于填充空白
|
|
|
|
|
#define SP10 SP5 SP5 // 定义SP10为两个SP5的组合,用于填充更多空白
|
|
|
|
|
#define SP20 SP10 SP10 // 定义SP20为两个SP10的组合,用于填充更多空白
|
|
|
|
|
|
|
|
|
|
/* Lord, forgive me this. */ // 注释:主啊,请原谅我这样做(可能是开发者对代码复杂性的调侃)
|
|
|
|
|
|
|
|
|
|
/* Lord, forgive me this. */
|
|
|
|
|
|
|
|
|
|
SAYF(SET_G1 bSTG bLT bH bSTOP cCYA " process timing " bSTG bH30 bH5 bH2 bHB
|
|
|
|
|
bH bSTOP cCYA " overall results " bSTG bH5 bRT "\n");
|
|
|
|
@ -4394,42 +4352,51 @@ static void show_stats(void) {
|
|
|
|
|
|
|
|
|
|
static void show_init_stats(void) {
|
|
|
|
|
|
|
|
|
|
struct queue_entry* q = queue;
|
|
|
|
|
u32 min_bits = 0, max_bits = 0;
|
|
|
|
|
u64 min_us = 0, max_us = 0;
|
|
|
|
|
u64 avg_us = 0;
|
|
|
|
|
u32 max_len = 0;
|
|
|
|
|
struct queue_entry* q = queue; // 指向队列的当前条目
|
|
|
|
|
u32 min_bits = 0, max_bits = 0; // 最小和最大位图大小
|
|
|
|
|
u64 min_us = 0, max_us = 0; // 最小和最大执行时间(微秒)
|
|
|
|
|
u64 avg_us = 0; // 平均执行时间(微秒)
|
|
|
|
|
u32 max_len = 0; // 最大测试用例长度
|
|
|
|
|
|
|
|
|
|
// 计算平均执行时间
|
|
|
|
|
if (total_cal_cycles) avg_us = total_cal_us / total_cal_cycles;
|
|
|
|
|
|
|
|
|
|
// 遍历队列中的所有条目
|
|
|
|
|
while (q) {
|
|
|
|
|
|
|
|
|
|
// 更新最小执行时间
|
|
|
|
|
if (!min_us || q->exec_us < min_us) min_us = q->exec_us;
|
|
|
|
|
// 更新最大执行时间
|
|
|
|
|
if (q->exec_us > max_us) max_us = q->exec_us;
|
|
|
|
|
|
|
|
|
|
// 更新最小位图大小
|
|
|
|
|
if (!min_bits || q->bitmap_size < min_bits) min_bits = q->bitmap_size;
|
|
|
|
|
// 更新最大位图大小
|
|
|
|
|
if (q->bitmap_size > max_bits) max_bits = q->bitmap_size;
|
|
|
|
|
|
|
|
|
|
// 更新最大测试用例长度
|
|
|
|
|
if (q->len > max_len) max_len = q->len;
|
|
|
|
|
|
|
|
|
|
q = q->next;
|
|
|
|
|
q = q->next; // 移动到下一个队列条目
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SAYF("\n");
|
|
|
|
|
SAYF("\n"); // 输出换行符
|
|
|
|
|
|
|
|
|
|
// 如果平均执行时间较长,发出警告
|
|
|
|
|
if (avg_us > (qemu_mode ? 50000 : 10000))
|
|
|
|
|
WARNF(cLRD "The target binary is pretty slow! See %s/perf_tips.txt.",
|
|
|
|
|
doc_path);
|
|
|
|
|
|
|
|
|
|
/* Let's keep things moving with slow binaries. */
|
|
|
|
|
|
|
|
|
|
/* 根据平均执行时间调整 havoc_div 的值,以保持测试的进度 */
|
|
|
|
|
if (avg_us > 50000) havoc_div = 10; /* 0-19 execs/sec */
|
|
|
|
|
else if (avg_us > 20000) havoc_div = 5; /* 20-49 execs/sec */
|
|
|
|
|
else if (avg_us > 10000) havoc_div = 2; /* 50-100 execs/sec */
|
|
|
|
|
|
|
|
|
|
// 如果不是恢复模式,检查并发出相关警告
|
|
|
|
|
if (!resuming_fuzz) {
|
|
|
|
|
|
|
|
|
|
// 如果测试用例过大,发出警告
|
|
|
|
|
if (max_len > 50 * 1024)
|
|
|
|
|
WARNF(cLRD "Some test cases are huge (%s) - see %s/perf_tips.txt!",
|
|
|
|
|
DMS(max_len), doc_path);
|
|
|
|
@ -4437,9 +4404,11 @@ static void show_init_stats(void) {
|
|
|
|
|
WARNF("Some test cases are big (%s) - see %s/perf_tips.txt.",
|
|
|
|
|
DMS(max_len), doc_path);
|
|
|
|
|
|
|
|
|
|
// 如果初始测试用例无用,发出警告
|
|
|
|
|
if (useless_at_start && !in_bitmap)
|
|
|
|
|
WARNF(cLRD "Some test cases look useless. Consider using a smaller set.");
|
|
|
|
|
|
|
|
|
|
// 如果输入文件过多,发出警告
|
|
|
|
|
if (queued_paths > 100)
|
|
|
|
|
WARNF(cLRD "You probably have far too many input files! Consider trimming down.");
|
|
|
|
|
else if (queued_paths > 20)
|
|
|
|
@ -4447,6 +4416,7 @@ static void show_init_stats(void) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 输出有用的统计信息
|
|
|
|
|
OKF("Here are some useful stats:\n\n"
|
|
|
|
|
|
|
|
|
|
cGRA " Test case count : " cRST "%u favored, %u variable, %u total\n"
|
|
|
|
@ -4456,58 +4426,58 @@ static void show_init_stats(void) {
|
|
|
|
|
((double)total_bitmap_size) / (total_bitmap_entries ? total_bitmap_entries : 1),
|
|
|
|
|
DI(min_us), DI(max_us), DI(avg_us));
|
|
|
|
|
|
|
|
|
|
// 如果没有指定超时时间,计算并设置默认超时时间
|
|
|
|
|
if (!timeout_given) {
|
|
|
|
|
|
|
|
|
|
/* Figure out the appropriate timeout. The basic idea is: 5x average or
|
|
|
|
|
1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second.
|
|
|
|
|
/* 计算适当的超时时间。基本思路是:5倍平均时间或1倍最大时间,
|
|
|
|
|
向上取整到 EXEC_TM_ROUND 毫秒,并限制在1秒内。
|
|
|
|
|
|
|
|
|
|
If the program is slow, the multiplier is lowered to 2x or 3x, because
|
|
|
|
|
random scheduler jitter is less likely to have any impact, and because
|
|
|
|
|
our patience is wearing thin =) */
|
|
|
|
|
如果程序较慢,乘数降低到2倍或3倍,因为随机调度抖动不太可能产生影响,
|
|
|
|
|
而且我们的耐心已经不足了 =) */
|
|
|
|
|
|
|
|
|
|
if (avg_us > 50000) exec_tmout = avg_us * 2 / 1000;
|
|
|
|
|
else if (avg_us > 10000) exec_tmout = avg_us * 3 / 1000;
|
|
|
|
|
else exec_tmout = avg_us * 5 / 1000;
|
|
|
|
|
if (avg_us > 50000) exec_tmout = avg_us * 2 / 1000; // 如果平均时间大于50000微秒,设置超时为2倍平均时间
|
|
|
|
|
else if (avg_us > 10000) exec_tmout = avg_us * 3 / 1000; // 如果平均时间大于10000微秒,设置超时为3倍平均时间
|
|
|
|
|
else exec_tmout = avg_us * 5 / 1000; // 否则设置超时为5倍平均时间
|
|
|
|
|
|
|
|
|
|
exec_tmout = MAX(exec_tmout, max_us / 1000);
|
|
|
|
|
exec_tmout = (exec_tmout + EXEC_TM_ROUND) / EXEC_TM_ROUND * EXEC_TM_ROUND;
|
|
|
|
|
exec_tmout = MAX(exec_tmout, max_us / 1000); // 取最大值,确保超时时间不小于最大执行时间
|
|
|
|
|
exec_tmout = (exec_tmout + EXEC_TM_ROUND) / EXEC_TM_ROUND * EXEC_TM_ROUND; // 向上取整到 EXEC_TM_ROUND 毫秒
|
|
|
|
|
|
|
|
|
|
if (exec_tmout > EXEC_TIMEOUT) exec_tmout = EXEC_TIMEOUT;
|
|
|
|
|
if (exec_tmout > EXEC_TIMEOUT) exec_tmout = EXEC_TIMEOUT; // 限制超时时间不超过1秒
|
|
|
|
|
|
|
|
|
|
ACTF("No -t option specified, so I'll use exec timeout of %u ms.",
|
|
|
|
|
exec_tmout);
|
|
|
|
|
exec_tmout); // 输出设置的超时时间
|
|
|
|
|
|
|
|
|
|
timeout_given = 1;
|
|
|
|
|
timeout_given = 1; // 标记超时时间已设置
|
|
|
|
|
|
|
|
|
|
} else if (timeout_given == 3) {
|
|
|
|
|
|
|
|
|
|
ACTF("Applying timeout settings from resumed session (%u ms).", exec_tmout);
|
|
|
|
|
ACTF("Applying timeout settings from resumed session (%u ms).", exec_tmout); // 应用恢复会话中的超时设置
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* In dumb mode, re-running every timing out test case with a generous time
|
|
|
|
|
limit is very expensive, so let's select a more conservative default. */
|
|
|
|
|
/* 在 dumb 模式下,重新运行每个超时的测试用例非常昂贵,
|
|
|
|
|
因此选择一个更保守的默认值。 */
|
|
|
|
|
|
|
|
|
|
if (dumb_mode && !getenv("AFL_HANG_TMOUT"))
|
|
|
|
|
hang_tmout = MIN(EXEC_TIMEOUT, exec_tmout * 2 + 100);
|
|
|
|
|
hang_tmout = MIN(EXEC_TIMEOUT, exec_tmout * 2 + 100); // 设置 hang_tmout 为较小的值
|
|
|
|
|
|
|
|
|
|
OKF("All set and ready to roll!");
|
|
|
|
|
OKF("All set and ready to roll!"); // 输出准备就绪信息
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Find first power of two greater or equal to val (assuming val under
|
|
|
|
|
2^31). */
|
|
|
|
|
/* 找到大于或等于 val 的第一个2的幂(假设 val 小于 2^31)。 */
|
|
|
|
|
|
|
|
|
|
static u32 next_p2(u32 val) {
|
|
|
|
|
|
|
|
|
|
u32 ret = 1;
|
|
|
|
|
while (val > ret) ret <<= 1;
|
|
|
|
|
return ret;
|
|
|
|
|
u32 ret = 1; // 初始化 ret 为1
|
|
|
|
|
while (val > ret) ret <<= 1; // 左移直到 ret >= val
|
|
|
|
|
return ret; // 返回结果
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|