dev-suhongye-2
Satori5ama 8 months ago
parent 3d0d161d4e
commit c825f975dd

@ -11,6 +11,9 @@
"alloc-inl.h": "c", "alloc-inl.h": "c",
"debug.h": "c", "debug.h": "c",
"android-ashmem.h": "c", "android-ashmem.h": "c",
"limits": "c" "limits": "c",
"stdio.h": "c",
"file.h": "c",
"config.h": "c"
} }
} }

@ -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 void maybe_update_plot_file(double bitmap_cvg, double eps) {
// 静态变量,用于存储上一次的状态值
static u32 prev_qp, prev_pf, prev_pnf, prev_ce, prev_md; static u32 prev_qp, prev_pf, prev_pnf, prev_ce, prev_md;
static u64 prev_qc, prev_uc, prev_uh; static u64 prev_qc, prev_uc, prev_uh;
// 如果当前状态与上一次状态相同,则直接返回,避免重复更新
if (prev_qp == queued_paths && prev_pf == pending_favored && if (prev_qp == queued_paths && prev_pf == pending_favored &&
prev_pnf == pending_not_fuzzed && prev_ce == current_entry && prev_pnf == pending_not_fuzzed && prev_ce == current_entry &&
prev_qc == queue_cycle && prev_uc == unique_crashes && prev_qc == queue_cycle && prev_uc == unique_crashes &&
prev_uh == unique_hangs && prev_md == max_depth) return; prev_uh == unique_hangs && prev_md == max_depth) return;
// 更新上一次的状态值为当前状态值
prev_qp = queued_paths; prev_qp = queued_paths;
prev_pf = pending_favored; prev_pf = pending_favored;
prev_pnf = pending_not_fuzzed; 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_uh = unique_hangs;
prev_md = max_depth; prev_md = max_depth;
/* Fields in the file: /* 文件中的字段:
unix_time, cycles_done, cur_path, paths_total, paths_not_fuzzed, unix_time, cycles_done, cur_path, paths_total, paths_not_fuzzed,
favored_not_fuzzed, unique_crashes, unique_hangs, max_depth, favored_not_fuzzed, unique_crashes, unique_hangs, max_depth,
execs_per_sec */ execs_per_sec */
// 将当前状态写入绘图文件
fprintf(plot_file, fprintf(plot_file,
"%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f\n", "%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, get_cur_time() / 1000, queue_cycle - 1, current_entry, queued_paths,
pending_not_fuzzed, pending_favored, bitmap_cvg, unique_crashes, pending_not_fuzzed, pending_favored, bitmap_cvg, unique_crashes,
unique_hangs, max_depth, eps); /* ignore errors */ unique_hangs, max_depth, eps); /* 忽略错误 */
// 刷新文件缓冲区,确保数据写入文件
fflush(plot_file); 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. */ /* Get the number of runnable processes, with some simple smoothing. */
static double get_runnable_processes(void) { 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 number of runnable processes; the 1-minute load average can be a
semi-decent approximation, though. */ semi-decent approximation, though. */
if (getloadavg(&res, 1) != 1) return 0; if (getloadavg(&res, 1) != 1) return 0; // 获取1分钟的负载平均值如果失败则返回0
#else #else
@ -3595,74 +3567,74 @@ static double get_runnable_processes(void) {
computed in funny ways and sometimes don't reflect extremely short-lived computed in funny ways and sometimes don't reflect extremely short-lived
processes well. */ processes well. */
FILE* f = fopen("/proc/stat", "r"); FILE* f = fopen("/proc/stat", "r"); // 打开/proc/stat文件以读取系统统计信息
u8 tmp[1024]; u8 tmp[1024]; // 用于存储读取的每一行内容
u32 val = 0; 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) || if (!strncmp(tmp, "procs_running ", 14) || // 如果行以"procs_running "开头
!strncmp(tmp, "procs_blocked ", 14)) val += atoi(tmp + 14); !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); ((double)val) * (1.0 / AVG_SMOOTHING);
} }
#endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */ #endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */
return res; return res; // 返回计算得到的res值
} }
/* Delete the temporary directory used for in-place session resume. */ /* 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); fn = alloc_printf("%s/_resume/.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); ck_free(fn); // 释放内存
fn = alloc_printf("%s/_resume/.state/auto_extras", out_dir); fn = alloc_printf("%s/_resume/.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); ck_free(fn); // 释放内存
fn = alloc_printf("%s/_resume/.state/redundant_edges", out_dir); fn = alloc_printf("%s/_resume/.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); ck_free(fn); // 释放内存
fn = alloc_printf("%s/_resume/.state/variable_behavior", out_dir); fn = alloc_printf("%s/_resume/.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); ck_free(fn); // 释放内存
fn = alloc_printf("%s/_resume/.state", out_dir); fn = alloc_printf("%s/_resume/.state", out_dir); // 构建文件路径
if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed; if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除目录,如果失败且错误不是目录不存在则跳转到清理失败处理
ck_free(fn); ck_free(fn); // 释放内存
fn = alloc_printf("%s/_resume", out_dir); fn = alloc_printf("%s/_resume", 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); 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 /* 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. */ 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; FILE* f; // 文件指针
u8 *fn = alloc_printf("%s/fuzzer_stats", out_dir); u8 *fn = alloc_printf("%s/fuzzer_stats", out_dir); // 构建文件路径
/* See if the output directory is locked. If yes, bail out. If not, /* 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 create a lock that will persist for the lifetime of the process
(this requires leaving the descriptor open).*/ (this requires leaving the descriptor open).*/
out_dir_fd = open(out_dir, O_RDONLY); out_dir_fd = open(out_dir, O_RDONLY); // 打开输出目录
if (out_dir_fd < 0) PFATAL("Unable to open '%s'", out_dir); 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" "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" " instance of afl-fuzz. You will need to choose a different %s\n"
" or stop the other process first.\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) "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" "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" " than %u minutes worth of fuzzing. To avoid data loss, afl-fuzz will *NOT*\n"
" automatically delete this data for you.\n\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" " session, put '-' as the input directory in the command line ('-i -') and\n"
" try again.\n", OUTPUT_GRACE); " 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 /* 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/ 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 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/ 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 /* 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); fn = alloc_printf("%s/.synced", out_dir); // 分配内存并格式化字符串
if (delete_files(fn, NULL)) goto dir_cleanup_failed; if (delete_files(fn, NULL)) goto dir_cleanup_failed; // 删除文件如果失败则跳转到dir_cleanup_failed
ck_free(fn); ck_free(fn); // 释放fn的内存
} }
/* Next, we need to clean up <out_dir>/queue/.state/ subdirectories: */ /* Next, we need to clean up <out_dir>/queue/.state/ subdirectories: */
fn = alloc_printf("%s/queue/.state/deterministic_done", out_dir); 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); ck_free(fn);
fn = alloc_printf("%s/queue/.state/auto_extras", out_dir); 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); ck_free(fn);
fn = alloc_printf("%s/queue/.state/redundant_edges", out_dir); 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); ck_free(fn);
fn = alloc_printf("%s/queue/.state/variable_behavior", out_dir); 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); ck_free(fn);
/* Then, get rid of the .state subdirectory itself (should be empty by now) /* 然后,删除.state子目录本身此时应该为空
and everything matching <out_dir>/queue/id:*. */ <out_dir>/queue/id:* */
fn = alloc_printf("%s/queue/.state", out_dir); 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); ck_free(fn);
fn = alloc_printf("%s/queue", out_dir); 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); 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) { if (!in_place_resume) {
fn = alloc_printf("%s/crashes/README.txt", out_dir); fn = alloc_printf("%s/crashes/README.txt", out_dir);
unlink(fn); /* Ignore errors */ unlink(fn); /* 忽略错误 */ // 删除crashes目录下的README.txt文件
ck_free(fn); ck_free(fn);
} }
fn = alloc_printf("%s/crashes", out_dir); fn = alloc_printf("%s/crashes", out_dir);
/* Make backup of the crashes directory if it's not empty and if we're /* 如果crashes目录不为空且正在进行原地恢复则备份crashes目录。 */
doing in-place resume. */
if (in_place_resume && rmdir(fn)) { if (in_place_resume && rmdir(fn)) {
@ -3832,17 +3805,17 @@ static void maybe_delete_out_dir(void) {
#endif /* ^!SIMPLE_FILES */ #endif /* ^!SIMPLE_FILES */
rename(fn, nfn); /* Ignore errors. */ rename(fn, nfn); /* 忽略错误。 */ // 重命名crashes目录以备份
ck_free(nfn); 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); ck_free(fn);
fn = alloc_printf("%s/hangs", out_dir); fn = alloc_printf("%s/hangs", out_dir);
/* Backup hangs, too. */ /* 同样备份hangs目录。 */
if (in_place_resume && rmdir(fn)) { if (in_place_resume && rmdir(fn)) {
@ -3863,53 +3836,49 @@ static void maybe_delete_out_dir(void) {
#endif /* ^!SIMPLE_FILES */ #endif /* ^!SIMPLE_FILES */
rename(fn, nfn); /* Ignore errors. */ rename(fn, nfn); /* 忽略错误。 */ // 重命名hangs目录以备份
ck_free(nfn); 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); ck_free(fn);
/* And now, for some finishing touches. */ /* 现在,进行一些收尾工作。 */
fn = alloc_printf("%s/.cur_input", out_dir); 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); ck_free(fn);
fn = alloc_printf("%s/fuzz_bitmap", out_dir); 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); ck_free(fn);
if (!in_place_resume) { if (!in_place_resume) {
fn = alloc_printf("%s/fuzzer_stats", out_dir); 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); ck_free(fn);
} }
fn = alloc_printf("%s/plot_data", out_dir); 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); ck_free(fn);
OKF("Output dir cleanup successful."); OKF("Output dir cleanup successful."); // 输出目录清理成功
/* Wow... is that all? If yes, celebrate! */ /* 哇...这就完了吗?如果是,庆祝一下! */
return; return;
dir_cleanup_failed: dir_cleanup_failed:
SAYF("\n" cLRD "[-] " cRST SAYF("\n" cLRD "[-] " cRST
"Whoops, the fuzzer tried to reuse your output directory, but bumped into\n" "哎呀fuzzer试图重用你的输出目录但遇到了一些不应该存在的文件或无法删除的文件 - 所以它决定中止!这发生在处理以下路径时:\n\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"
" %s\n\n" " %s\n\n"
" Please examine and manually delete the files, or specify a different\n" " 请检查并手动删除这些文件,或为工具指定一个不同的输出位置。\n", fn); // 输出目录清理失败的错误信息
" output location for the tool.\n", fn);
FATAL("Output directory cleanup failed");
FATAL("Output directory cleanup failed"); // 输出目录清理失败
} }
@ -3921,148 +3890,137 @@ static void check_term_size(void);
static void show_stats(void) { static void show_stats(void) {
static u64 last_stats_ms, last_plot_ms, last_ms, last_execs; static u64 last_stats_ms, last_plot_ms, last_ms, last_execs; // 上一次统计时间、绘图时间、UI更新时间、执行次数
static double avg_exec; static double avg_exec; // 平均执行速度
double t_byte_ratio, stab_ratio; double t_byte_ratio, stab_ratio; // 字节覆盖率和稳定性比率
u64 cur_ms;
u32 t_bytes, t_bits;
u32 banner_len, banner_pad; u64 cur_ms; // 当前时间
u8 tmp[256]; 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; 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; if (cur_ms - start_time > 10 * 60 * 1000) run_over10m = 1;
/* Calculate smoothed exec speed stats. */ /* 计算平滑的执行速度统计 */
if (!last_execs) { if (!last_execs) {
avg_exec = ((double)total_execs) * 1000 / (cur_ms - start_time); avg_exec = ((double)total_execs) * 1000 / (cur_ms - start_time); // 初始平均执行速度
} else { } else {
double cur_avg = ((double)(total_execs - last_execs)) * 1000 / double cur_avg = ((double)(total_execs - last_execs)) * 1000 /
(cur_ms - last_ms); (cur_ms - last_ms); // 当前平均执行速度
/* If there is a dramatic (5x+) jump in speed, reset the indicator
more quickly. */
/* 如果速度有显著变化5倍以上快速重置指示器 */
if (cur_avg * 5 < avg_exec || cur_avg / 5 > avg_exec) if (cur_avg * 5 < avg_exec || cur_avg / 5 > avg_exec)
avg_exec = cur_avg; avg_exec = cur_avg;
avg_exec = avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) + 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_ms = cur_ms; // 更新上一次UI更新时间
last_execs = total_execs; last_execs = total_execs; // 更新上一次执行次数
/* Tell the callers when to contact us (as measured in execs). */
/* 告诉调用者何时联系我们(以执行次数衡量) */
stats_update_freq = avg_exec / (UI_TARGET_HZ * 10); stats_update_freq = avg_exec / (UI_TARGET_HZ * 10);
if (!stats_update_freq) stats_update_freq = 1; if (!stats_update_freq) stats_update_freq = 1;
/* Do some bitmap stats. */ /* 计算一些位图统计 */
t_bytes = count_non_255_bytes(virgin_bits); // 计算非255字节数
t_bytes = count_non_255_bytes(virgin_bits); t_byte_ratio = ((double)t_bytes * 100) / MAP_SIZE; // 计算字节覆盖率
t_byte_ratio = ((double)t_bytes * 100) / MAP_SIZE;
if (t_bytes) if (t_bytes)
stab_ratio = 100 - ((double)var_byte_count) * 100 / t_bytes; stab_ratio = 100 - ((double)var_byte_count) * 100 / t_bytes; // 计算稳定性比率
else else
stab_ratio = 100; 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) { if (cur_ms - last_stats_ms > STATS_UPDATE_SEC * 1000) {
last_stats_ms = cur_ms; last_stats_ms = cur_ms;
write_stats_file(t_byte_ratio, stab_ratio, avg_exec); write_stats_file(t_byte_ratio, stab_ratio, avg_exec); // 写入统计文件
save_auto(); save_auto(); // 保存自动生成的token
write_bitmap(); write_bitmap(); // 写入位图
} }
/* Every now and then, write plot data. */ /* 每隔一段时间,写入绘图数据 */
if (cur_ms - last_plot_ms > PLOT_UPDATE_SEC * 1000) { if (cur_ms - last_plot_ms > PLOT_UPDATE_SEC * 1000) {
last_plot_ms = cur_ms; 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 && if (!dumb_mode && cycles_wo_finds > 100 && !pending_not_fuzzed &&
getenv("AFL_EXIT_WHEN_DONE")) stop_soon = 2; getenv("AFL_EXIT_WHEN_DONE")) stop_soon = 2;
if (total_crashes && getenv("AFL_BENCH_UNTIL_CRASH")) 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; if (not_on_tty) return;
/* Compute some mildly useful bitmap stats. */ /* 计算一些有用的位图统计 */
t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits); // 计算非255位数
t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits);
/* Now, for the visuals... */
/* 现在,处理视觉效果... */
if (clear_screen) { if (clear_screen) {
SAYF(TERM_CLEAR CURSOR_HIDE); SAYF(TERM_CLEAR CURSOR_HIDE); // 清屏并隐藏光标
clear_screen = 0; 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" 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_len = (crash_mode ? 24 : 22) + strlen(VERSION) + strlen(use_banner); // 计算横幅的总长度根据是否处于crash_mode调整基础长度
banner_pad = (80 - banner_len) / 2; banner_pad = (80 - banner_len) / 2; // 计算横幅两侧的填充空格数,使横幅居中
memset(tmp, ' ', banner_pad); memset(tmp, ' ', banner_pad); // 在tmp数组的前banner_pad个位置填充空格
sprintf(tmp + banner_pad, "%s " cLCY VERSION cLGN sprintf(tmp + banner_pad, "%s " cLCY VERSION cLGN
" (%s)", crash_mode ? cPIN "peruvian were-rabbit" : " (%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 bSTG bSTART cGRA // 定义bSTG为bSTART和cGRA的组合用于绘制框的起始部分
#define bH2 bH bH #define bH2 bH bH // 定义bH2为两个bH的组合用于绘制水平线
#define bH5 bH2 bH2 bH #define bH5 bH2 bH2 bH // 定义bH5为两个bH2和一个bH的组合用于绘制更长的水平线
#define bH10 bH5 bH5 #define bH10 bH5 bH5 // 定义bH10为两个bH5的组合用于绘制更长的水平线
#define bH20 bH10 bH10 #define bH20 bH10 bH10 // 定义bH20为两个bH10的组合用于绘制更长的水平线
#define bH30 bH20 bH10 #define bH30 bH20 bH10 // 定义bH30为一个bH20和一个bH10的组合用于绘制更长的水平线
#define SP5 " " #define SP5 " " // 定义SP5为5个空格用于填充空白
#define SP10 SP5 SP5 #define SP10 SP5 SP5 // 定义SP10为两个SP5的组合用于填充更多空白
#define SP20 SP10 SP10 #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 SAYF(SET_G1 bSTG bLT bH bSTOP cCYA " process timing " bSTG bH30 bH5 bH2 bHB
bH bSTOP cCYA " overall results " bSTG bH5 bRT "\n"); bH bSTOP cCYA " overall results " bSTG bH5 bRT "\n");
@ -4394,42 +4352,51 @@ static void show_stats(void) {
static void show_init_stats(void) { static void show_init_stats(void) {
struct queue_entry* q = queue; struct queue_entry* q = queue; // 指向队列的当前条目
u32 min_bits = 0, max_bits = 0; u32 min_bits = 0, max_bits = 0; // 最小和最大位图大小
u64 min_us = 0, max_us = 0; u64 min_us = 0, max_us = 0; // 最小和最大执行时间(微秒)
u64 avg_us = 0; u64 avg_us = 0; // 平均执行时间(微秒)
u32 max_len = 0; u32 max_len = 0; // 最大测试用例长度
// 计算平均执行时间
if (total_cal_cycles) avg_us = total_cal_us / total_cal_cycles; if (total_cal_cycles) avg_us = total_cal_us / total_cal_cycles;
// 遍历队列中的所有条目
while (q) { while (q) {
// 更新最小执行时间
if (!min_us || q->exec_us < min_us) min_us = q->exec_us; 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 (q->exec_us > max_us) max_us = q->exec_us;
// 更新最小位图大小
if (!min_bits || q->bitmap_size < min_bits) min_bits = q->bitmap_size; 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->bitmap_size > max_bits) max_bits = q->bitmap_size;
// 更新最大测试用例长度
if (q->len > max_len) max_len = q->len; 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)) if (avg_us > (qemu_mode ? 50000 : 10000))
WARNF(cLRD "The target binary is pretty slow! See %s/perf_tips.txt.", WARNF(cLRD "The target binary is pretty slow! See %s/perf_tips.txt.",
doc_path); doc_path);
/* Let's keep things moving with slow binaries. */ /* 根据平均执行时间调整 havoc_div 的值,以保持测试的进度 */
if (avg_us > 50000) havoc_div = 10; /* 0-19 execs/sec */ 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 > 20000) havoc_div = 5; /* 20-49 execs/sec */
else if (avg_us > 10000) havoc_div = 2; /* 50-100 execs/sec */ else if (avg_us > 10000) havoc_div = 2; /* 50-100 execs/sec */
// 如果不是恢复模式,检查并发出相关警告
if (!resuming_fuzz) { if (!resuming_fuzz) {
// 如果测试用例过大,发出警告
if (max_len > 50 * 1024) if (max_len > 50 * 1024)
WARNF(cLRD "Some test cases are huge (%s) - see %s/perf_tips.txt!", WARNF(cLRD "Some test cases are huge (%s) - see %s/perf_tips.txt!",
DMS(max_len), doc_path); 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.", WARNF("Some test cases are big (%s) - see %s/perf_tips.txt.",
DMS(max_len), doc_path); DMS(max_len), doc_path);
// 如果初始测试用例无用,发出警告
if (useless_at_start && !in_bitmap) if (useless_at_start && !in_bitmap)
WARNF(cLRD "Some test cases look useless. Consider using a smaller set."); WARNF(cLRD "Some test cases look useless. Consider using a smaller set.");
// 如果输入文件过多,发出警告
if (queued_paths > 100) if (queued_paths > 100)
WARNF(cLRD "You probably have far too many input files! Consider trimming down."); WARNF(cLRD "You probably have far too many input files! Consider trimming down.");
else if (queued_paths > 20) else if (queued_paths > 20)
@ -4447,6 +4416,7 @@ static void show_init_stats(void) {
} }
// 输出有用的统计信息
OKF("Here are some useful stats:\n\n" OKF("Here are some useful stats:\n\n"
cGRA " Test case count : " cRST "%u favored, %u variable, %u total\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), ((double)total_bitmap_size) / (total_bitmap_entries ? total_bitmap_entries : 1),
DI(min_us), DI(max_us), DI(avg_us)); DI(min_us), DI(max_us), DI(avg_us));
// 如果没有指定超时时间,计算并设置默认超时时间
if (!timeout_given) { if (!timeout_given) {
/* Figure out the appropriate timeout. The basic idea is: 5x average or /* 计算适当的超时时间。基本思路是5倍平均时间或1倍最大时间
1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second. EXEC_TM_ROUND 1
If the program is slow, the multiplier is lowered to 2x or 3x, because 23
random scheduler jitter is less likely to have any impact, and because =) */
our patience is wearing thin =) */
if (avg_us > 50000) exec_tmout = avg_us * 2 / 1000; if (avg_us > 50000) exec_tmout = avg_us * 2 / 1000; // 如果平均时间大于50000微秒设置超时为2倍平均时间
else if (avg_us > 10000) exec_tmout = avg_us * 3 / 1000; else if (avg_us > 10000) exec_tmout = avg_us * 3 / 1000; // 如果平均时间大于10000微秒设置超时为3倍平均时间
else exec_tmout = avg_us * 5 / 1000; else exec_tmout = avg_us * 5 / 1000; // 否则设置超时为5倍平均时间
exec_tmout = MAX(exec_tmout, max_us / 1000); exec_tmout = MAX(exec_tmout, max_us / 1000); // 取最大值,确保超时时间不小于最大执行时间
exec_tmout = (exec_tmout + EXEC_TM_ROUND) / EXEC_TM_ROUND * EXEC_TM_ROUND; 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.", 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) { } 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 /* 在 dumb 模式下,重新运行每个超时的测试用例非常昂贵,
limit is very expensive, so let's select a more conservative default. */ */
if (dumb_mode && !getenv("AFL_HANG_TMOUT")) 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 /* 找到大于或等于 val 的第一个2的幂假设 val 小于 2^31。 */
2^31). */
static u32 next_p2(u32 val) { static u32 next_p2(u32 val) {
u32 ret = 1; u32 ret = 1; // 初始化 ret 为1
while (val > ret) ret <<= 1; while (val > ret) ret <<= 1; // 左移直到 ret >= val
return ret; return ret; // 返回结果
} }
/* Trim all new test cases to save cycles when doing deterministic checks. The /* 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 trimmer uses power-of-two increments somewhere between 1/16 and 1/1024 of
file size, to keep the stage short and sweet. */ file size, to keep the stage short and sweet. */

Loading…
Cancel
Save