From c825f975dde292fe6c2b43e4fc455b9c81b95770 Mon Sep 17 00:00:00 2001 From: Satori5ama <1242330740@qq.com> Date: Thu, 9 Jan 2025 08:32:04 +0800 Subject: [PATCH] 3500-4500 --- src/.vscode/settings.json | 5 +- src/afl-fuzz.c | 452 ++++++++++++++++++-------------------- 2 files changed, 215 insertions(+), 242 deletions(-) diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index db0a9aa..717327f 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -11,6 +11,9 @@ "alloc-inl.h": "c", "debug.h": "c", "android-ashmem.h": "c", - "limits": "c" + "limits": "c", + "stdio.h": "c", + "file.h": "c", + "config.h": "c" } } \ No newline at end of file diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5b849a9..ed45a09 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.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 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 /.synced/.../id:*, if any are present. */ + in /.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 /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 /queue/id:*. */ + /* 然后,删除.state子目录本身(此时应该为空) + 并删除所有匹配/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 /crashes/id:* and /hangs/id:*. */ + /* 接下来,处理/crashes/id:*和/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. */