From 31defdcf72c37c64a9b0cc91cad92dda92cb4775 Mon Sep 17 00:00:00 2001 From: yinao <1872767794@qq.com> Date: Wed, 8 Jan 2025 21:33:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86afl-fuzz.c=E7=9A=84?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 6 +- src/afl-fuzz.c | 3750 ++++++++++++++++++++--------------------- 2 files changed, 1860 insertions(+), 1896 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 925fab1..05fcef7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,10 @@ "afl-as.h": "c", "types.h": "c", "fcntl.h": "c", - "android-ashmem.h": "c" + "android-ashmem.h": "c", + "limits": "c", + "typeinfo": "c", + "string.h": "c", + "debug.h": "c" } } \ No newline at end of file diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 04327d7..0f4d4b7 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -459,54 +459,55 @@ static void bind_to_free_cpu(void) { fn = alloc_printf("/proc/%s/status", de->d_name); - if (!(f = fopen(fn, "r"))) { + + if (!(f = fopen(fn, "r"))) { // 如果无法打开文件,释放内存并继续 ck_free(fn); continue; } - while (fgets(tmp, MAX_LINE, f)) { + while (fgets(tmp, MAX_LINE, f)) { // 逐行读取文件内容 u32 hval; /* Processes without VmSize are probably kernel tasks. */ - + // 如果没有 VmSize 的进程,可能是内核任务 if (!strncmp(tmp, "VmSize:\t", 8)) has_vmsize = 1; - if (!strncmp(tmp, "Cpus_allowed_list:\t", 19) && - !strchr(tmp, '-') && !strchr(tmp, ',') && - sscanf(tmp + 19, "%u", &hval) == 1 && hval < sizeof(cpu_used) && - has_vmsize) { + if (!strncmp(tmp, "Cpus_allowed_list:\t", 19) && // 检查是否包含 CPU 绑定信息 + !strchr(tmp, '-') && !strchr(tmp, ',') && // 确保没有范围或列表 + sscanf(tmp + 19, "%u", &hval) == 1 && hval < sizeof(cpu_used) && // 解析 CPU 编号 + has_vmsize) { // 确保进程有 VmSize - cpu_used[hval] = 1; + cpu_used[hval] = 1; // 标记该 CPU 为已使用 break; } } - ck_free(fn); - fclose(f); + ck_free(fn); // 释放文件名内存 + fclose(f); // 关闭文件 } - closedir(d); - if (cpu_to_bind_given) { + closedir(d); // 关闭目录 + if (cpu_to_bind_given) { // 如果指定了要绑定的 CPU - if (cpu_to_bind >= cpu_core_count) + if (cpu_to_bind >= cpu_core_count) // 如果指定的 CPU 编号超出范围 FATAL("The CPU core id to bind should be between 0 and %u", cpu_core_count - 1); - if (cpu_used[cpu_to_bind]) + if (cpu_used[cpu_to_bind]) // 如果指定的 CPU 已被占用 FATAL("The CPU core #%u to bind is not free!", cpu_to_bind); - i = cpu_to_bind; + i = cpu_to_bind; // 使用指定的 CPU } else { - for (i = 0; i < cpu_core_count; i++) if (!cpu_used[i]) break; + for (i = 0; i < cpu_core_count; i++) if (!cpu_used[i]) break; // 找到第一个空闲的 CPU } - if (i == cpu_core_count) { + if (i == cpu_core_count) { // 如果没有找到空闲的 CPU SAYF("\n" cLRD "[-] " cRST "Uh-oh, looks like all %u CPU cores on your system are allocated to\n" @@ -515,19 +516,19 @@ static void bind_to_free_cpu(void) { " absolutely sure, you can set AFL_NO_AFFINITY and try again.\n", cpu_core_count); - FATAL("No more free CPU cores"); + FATAL("No more free CPU cores"); // 报错并退出 } - OKF("Found a free CPU core, binding to #%u.", i); + OKF("Found a free CPU core, binding to #%u.", i); // 找到空闲 CPU,准备绑定 - cpu_aff = i; + cpu_aff = i; // 记录选中的 CPU - CPU_ZERO(&c); - CPU_SET(i, &c); + CPU_ZERO(&c); // 初始化 CPU 集合 + CPU_SET(i, &c); // 将选中的 CPU 加入集合 - if (sched_setaffinity(0, sizeof(c), &c)) - PFATAL("sched_setaffinity failed"); + if (sched_setaffinity(0, sizeof(c), &c)) // 绑定当前进程到选中的 CPU + PFATAL("sched_setaffinity failed"); // 如果绑定失败,报错并退出 } @@ -540,23 +541,23 @@ static void bind_to_free_cpu(void) { static void locate_diffs(u8* ptr1, u8* ptr2, u32 len, s32* first, s32* last) { - s32 f_loc = -1; - s32 l_loc = -1; + s32 f_loc = -1; // 初始化第一个不同点的位置 + s32 l_loc = -1; // 初始化最后一个不同点的位置 u32 pos; - for (pos = 0; pos < len; pos++) { + for (pos = 0; pos < len; pos++) { // 遍历缓冲区 - if (*(ptr1++) != *(ptr2++)) { + if (*(ptr1++) != *(ptr2++)) { // 如果发现不同 - if (f_loc == -1) f_loc = pos; - l_loc = pos; + if (f_loc == -1) f_loc = pos; // 记录第一个不同点 + l_loc = pos; // 更新最后一个不同点 } } - *first = f_loc; - *last = l_loc; + *first = f_loc; // 返回第一个不同点 + *last = l_loc; // 返回最后一个不同点 return; @@ -571,10 +572,10 @@ static void locate_diffs(u8* ptr1, u8* ptr2, u32 len, s32* first, s32* last) { static u8* DI(u64 val) { - static u8 tmp[12][16]; - static u8 cur; + static u8 tmp[12][16]; // 12 个静态缓冲区 + static u8 cur; // 当前使用的缓冲区索引 - cur = (cur + 1) % 12; + cur = (cur + 1) % 12; // 循环使用缓冲区 #define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) do { \ if (val < (_divisor) * (_limit_mult)) { \ @@ -584,40 +585,42 @@ static u8* DI(u64 val) { } while (0) /* 0-9999 */ - CHK_FORMAT(1, 10000, "%llu", u64); + CHK_FORMAT(1, 10000, "%llu", u64); // 如果值小于 10000,直接返回 /* 10.0k - 99.9k */ - CHK_FORMAT(1000, 99.95, "%0.01fk", double); + CHK_FORMAT(1000, 99.95, "%0.01fk", double); // 如果值小于 99.95k,返回带一位小数的千位表示 /* 100k - 999k */ - CHK_FORMAT(1000, 1000, "%lluk", u64); + CHK_FORMAT(1000, 1000, "%lluk", u64); // 如果值小于 1000k,返回千位表示 /* 1.00M - 9.99M */ - CHK_FORMAT(1000 * 1000, 9.995, "%0.02fM", double); + CHK_FORMAT(1000 * 1000, 9.995, "%0.02fM", double); // 如果值小于 9.995M,返回带两位小数的百万位表示 /* 10.0M - 99.9M */ - CHK_FORMAT(1000 * 1000, 99.95, "%0.01fM", double); + CHK_FORMAT(1000 * 1000, 99.95, "%0.01fM", double); // 如果值小于 99.95M,返回带一位小数的百万位表示 /* 100M - 999M */ - CHK_FORMAT(1000 * 1000, 1000, "%lluM", u64); + CHK_FORMAT(1000 * 1000, 1000, "%lluM", u64); // 如果值小于 1000M,返回百万位表示 /* 1.00G - 9.99G */ - CHK_FORMAT(1000LL * 1000 * 1000, 9.995, "%0.02fG", double); + CHK_FORMAT(1000LL * 1000 * 1000, 9.995, "%0.02fG", double); // 如果值小于 9.995G,返回带两位小数的十亿位表示 /* 10.0G - 99.9G */ - CHK_FORMAT(1000LL * 1000 * 1000, 99.95, "%0.01fG", double); + CHK_FORMAT(1000LL * 1000 * 1000, 99.95, "%0.01fG", double); // 如果值小于 99.95G,返回带一位小数的十亿位表示 /* 100G - 999G */ - CHK_FORMAT(1000LL * 1000 * 1000, 1000, "%lluG", u64); + CHK_FORMAT(1000LL * 1000 * 1000, 1000, "%lluG", u64); // 如果值小于 1000G,返回十亿位表示 /* 1.00T - 9.99G */ - CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 9.995, "%0.02fT", double); + CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 9.995, "%0.02fT", double); // 如果值小于 9.995T,返回带两位小数的万亿位表示 /* 10.0T - 99.9T */ - CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 99.95, "%0.01fT", double); + CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 99.95, "%0.01fT", double); // 如果值小于 99.95T,返回带一位小数的万亿位表示 + +#undef CHK_FORMAT /* 100T+ */ - strcpy(tmp[cur], "infty"); + strcpy(tmp[cur], "infty"); // 如果值大于等于 100T,返回 "infty" return tmp[cur]; } @@ -628,19 +631,19 @@ static u8* DI(u64 val) { static u8* DF(double val) { - static u8 tmp[16]; + static u8 tmp[16]; // 单个静态缓冲区 - if (val < 99.995) { - sprintf(tmp, "%0.02f", val); + if (val < 99.995) { // 如果值小于 99.995 + sprintf(tmp, "%0.02f", val); // 返回带两位小数的表示 return tmp; } - if (val < 999.95) { - sprintf(tmp, "%0.01f", val); + if (val < 999.95) { // 如果值小于 999.95 + sprintf(tmp, "%0.01f", val); // 返回带一位小数的表示 return tmp; } - return DI((u64)val); + return DI((u64)val); // 否则调用 DI 函数 } @@ -649,48 +652,48 @@ static u8* DF(double val) { static u8* DMS(u64 val) { - static u8 tmp[12][16]; - static u8 cur; + static u8 tmp[12][16]; // 12 个静态缓冲区 + static u8 cur; // 当前使用的缓冲区索引 - cur = (cur + 1) % 12; + cur = (cur + 1) % 12; // 循环使用缓冲区 /* 0-9999 */ - CHK_FORMAT(1, 10000, "%llu B", u64); + CHK_FORMAT(1, 10000, "%llu B", u64); // 如果值小于 10000,直接返回字节表示 /* 10.0k - 99.9k */ - CHK_FORMAT(1024, 99.95, "%0.01f kB", double); + CHK_FORMAT(1024, 99.95, "%0.01f kB", double); // 如果值小于 99.95k,返回带一位小数的千字节表示 /* 100k - 999k */ - CHK_FORMAT(1024, 1000, "%llu kB", u64); + CHK_FORMAT(1024, 1000, "%llu kB", u64); // 如果值小于 1000k,返回千字节表示 /* 1.00M - 9.99M */ - CHK_FORMAT(1024 * 1024, 9.995, "%0.02f MB", double); + CHK_FORMAT(1024 * 1024, 9.995, "%0.02f MB", double); // 如果值小于 9.995M,返回带两位小数的兆字节表示 /* 10.0M - 99.9M */ - CHK_FORMAT(1024 * 1024, 99.95, "%0.01f MB", double); + CHK_FORMAT(1024 * 1024, 99.95, "%0.01f MB", double); // 如果值小于 99.95M,返回带一位小数的兆字节表示 /* 100M - 999M */ - CHK_FORMAT(1024 * 1024, 1000, "%llu MB", u64); + CHK_FORMAT(1024 * 1024, 1000, "%llu MB", u64); // 如果值小于 1000M,返回兆字节表示 /* 1.00G - 9.99G */ - CHK_FORMAT(1024LL * 1024 * 1024, 9.995, "%0.02f GB", double); + CHK_FORMAT(1024LL * 1024 * 1024, 9.995, "%0.02f GB", double); // 如果值小于 9.995G,返回带两位小数的吉字节表示 /* 10.0G - 99.9G */ - CHK_FORMAT(1024LL * 1024 * 1024, 99.95, "%0.01f GB", double); + CHK_FORMAT(1024LL * 1024 * 1024, 99.95, "%0.01f GB", double); // 如果值小于 99.95G,返回带一位小数的吉字节表示 /* 100G - 999G */ - CHK_FORMAT(1024LL * 1024 * 1024, 1000, "%llu GB", u64); + CHK_FORMAT(1024LL * 1024 * 1024, 1000, "%llu GB", u64); // 如果值小于 1000G,返回吉字节表示 /* 1.00T - 9.99G */ - CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 9.995, "%0.02f TB", double); + CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 9.995, "%0.02f TB", double); // 如果值小于 9.995T,返回带两位小数的太字节表示 /* 10.0T - 99.9T */ - CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 99.95, "%0.01f TB", double); + CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 99.95, "%0.01f TB", double); // 如果值小于 99.95T,返回带一位小数的太字节表示 #undef CHK_FORMAT /* 100T+ */ - strcpy(tmp[cur], "infty"); + strcpy(tmp[cur], "infty"); // 如果值大于等于 100T,返回 "infty" return tmp[cur]; } @@ -700,20 +703,20 @@ static u8* DMS(u64 val) { static u8* DTD(u64 cur_ms, u64 event_ms) { - static u8 tmp[64]; + static u8 tmp[64]; // 静态缓冲区 u64 delta; s32 t_d, t_h, t_m, t_s; - if (!event_ms) return "none seen yet"; + if (!event_ms) return "none seen yet"; // 如果事件时间为 0,返回 "none seen yet" - delta = cur_ms - event_ms; + delta = cur_ms - event_ms; // 计算时间差 - t_d = delta / 1000 / 60 / 60 / 24; - t_h = (delta / 1000 / 60 / 60) % 24; - t_m = (delta / 1000 / 60) % 60; - t_s = (delta / 1000) % 60; + t_d = delta / 1000 / 60 / 60 / 24; // 计算天数 + t_h = (delta / 1000 / 60 / 60) % 24; // 计算小时数 + t_m = (delta / 1000 / 60) % 60; // 计算分钟数 + t_s = (delta / 1000) % 60; // 计算秒数 - sprintf(tmp, "%s days, %u hrs, %u min, %u sec", DI(t_d), t_h, t_m, t_s); + sprintf(tmp, "%s days, %u hrs, %u min, %u sec", DI(t_d), t_h, t_m, t_s); // 格式化时间差 return tmp; } @@ -725,18 +728,18 @@ static u8* DTD(u64 cur_ms, u64 event_ms) { static void mark_as_det_done(struct queue_entry* q) { - u8* fn = strrchr(q->fname, '/'); + u8* fn = strrchr(q->fname, '/'); // 获取文件名中的最后一个斜杠 s32 fd; - fn = alloc_printf("%s/queue/.state/deterministic_done/%s", out_dir, fn + 1); + fn = alloc_printf("%s/queue/.state/deterministic_done/%s", out_dir, fn + 1); // 生成状态文件路径 - fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", fn); - close(fd); + fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建状态文件 + if (fd < 0) PFATAL("Unable to create '%s'", fn); // 如果创建失败,报错并退出 + close(fd); // 关闭文件 - ck_free(fn); + ck_free(fn); // 释放内存 - q->passed_det = 1; + q->passed_det = 1; // 标记该队列项为已完成确定性测试 } @@ -746,23 +749,23 @@ static void mark_as_det_done(struct queue_entry* q) { static void mark_as_variable(struct queue_entry* q) { - u8 *fn = strrchr(q->fname, '/') + 1, *ldest; + u8 *fn = strrchr(q->fname, '/') + 1, *ldest; // 获取文件名 - ldest = alloc_printf("../../%s", fn); - fn = alloc_printf("%s/queue/.state/variable_behavior/%s", out_dir, fn); + ldest = alloc_printf("../../%s", fn); // 生成符号链接目标路径 + fn = alloc_printf("%s/queue/.state/variable_behavior/%s", out_dir, fn); // 生成符号链接路径 - if (symlink(ldest, fn)) { + if (symlink(ldest, fn)) { // 创建符号链接 - s32 fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", fn); - close(fd); + s32 fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); // 如果符号链接创建失败,创建文件 + if (fd < 0) PFATAL("Unable to create '%s'", fn); // 如果创建失败,报错并退出 + close(fd); // 关闭文件 } - ck_free(ldest); - ck_free(fn); + ck_free(ldest); // 释放内存 + ck_free(fn); // 释放内存 - q->var_behavior = 1; + q->var_behavior = 1; // 标记该队列项为具有可变行为 } @@ -775,26 +778,26 @@ static void mark_as_redundant(struct queue_entry* q, u8 state) { u8* fn; s32 fd; - if (state == q->fs_redundant) return; + if (state == q->fs_redundant) return; // 如果状态未改变,直接返回 - q->fs_redundant = state; + q->fs_redundant = state; // 更新冗余状态 - fn = strrchr(q->fname, '/'); - fn = alloc_printf("%s/queue/.state/redundant_edges/%s", out_dir, fn + 1); + fn = strrchr(q->fname, '/'); // 获取文件名中的最后一个斜杠 + fn = alloc_printf("%s/queue/.state/redundant_edges/%s", out_dir, fn + 1); // 生成状态文件路径 - if (state) { + if (state) { // 如果标记为冗余 - fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", fn); - close(fd); + fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建状态文件 + if (fd < 0) PFATAL("Unable to create '%s'", fn); // 如果创建失败,报错并退出 + close(fd); // 关闭文件 - } else { + } else { // 如果取消冗余标记 - if (unlink(fn)) PFATAL("Unable to remove '%s'", fn); + if (unlink(fn)) PFATAL("Unable to remove '%s'", fn); // 删除状态文件 } - ck_free(fn); + ck_free(fn); // 释放内存 } @@ -803,36 +806,37 @@ static void mark_as_redundant(struct queue_entry* q, u8 state) { static void add_to_queue(u8* fname, u32 len, u8 passed_det) { - struct queue_entry* q = ck_alloc(sizeof(struct queue_entry)); + struct queue_entry* q = ck_alloc(sizeof(struct queue_entry)); // 分配队列项内存 - q->fname = fname; - q->len = len; - q->depth = cur_depth + 1; - q->passed_det = passed_det; + q->fname = fname; // 设置文件名 + q->len = len; // 设置文件长度 + q->depth = cur_depth + 1; // 设置路径深度 + q->passed_det = passed_det; // 设置是否通过确定性测试 - if (q->depth > max_depth) max_depth = q->depth; + if (q->depth > max_depth) max_depth = q->depth; // 更新最大路径深度 - if (queue_top) { + if (queue_top) { // 如果队列不为空 - queue_top->next = q; + queue_top->next = q; // 将新项添加到队列尾部 queue_top = q; - } else q_prev100 = queue = queue_top = q; + } else q_prev100 = queue = queue_top = q; // 如果队列为空,初始化队列 - queued_paths++; - pending_not_fuzzed++; + queued_paths++; // 增加队列项计数 + pending_not_fuzzed++; // 增加未模糊测试的项计数 - cycles_wo_finds = 0; + cycles_wo_finds = 0; // 重置未发现新路径的周期计数 /* Set next_100 pointer for every 100th element (index 0, 100, etc) to allow faster iteration. */ + // 为每第 100 个元素设置 next_100 指针,以便快速迭代 if ((queued_paths - 1) % 100 == 0 && queued_paths > 1) { - q_prev100->next_100 = q; + q_prev100->next_100 = q; // 设置前一个 100 项的 next_100 指针 q_prev100 = q; } - last_path_time = get_cur_time(); + last_path_time = get_cur_time(); // 记录最后路径时间 } @@ -846,9 +850,9 @@ EXP_ST void destroy_queue(void) { while (q) { n = q->next; - ck_free(q->fname); - ck_free(q->trace_mini); - ck_free(q); + ck_free(q->fname); // 释放文件名内存 + ck_free(q->trace_mini); // 释放 trace_mini 内存 + ck_free(q); // 释放队列项内存 q = n; } @@ -865,18 +869,17 @@ EXP_ST void write_bitmap(void) { u8* fname; s32 fd; - if (!bitmap_changed) return; + if (!bitmap_changed) return; // 如果位图未改变,直接返回 bitmap_changed = 0; - fname = alloc_printf("%s/fuzz_bitmap", out_dir); - fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); + fname = alloc_printf("%s/fuzz_bitmap", out_dir); // 生成位图文件路径 + fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); // 创建位图文件 - if (fd < 0) PFATAL("Unable to open '%s'", fname); + if (fd < 0) PFATAL("Unable to open '%s'", fname); // 如果创建失败,报错并退出 - ck_write(fd, virgin_bits, MAP_SIZE, fname); - - close(fd); - ck_free(fname); + ck_write(fd, virgin_bits, MAP_SIZE, fname); // 写入位图数据 + close(fd); // 关闭文件 + ck_free(fname); // 释放内存 } @@ -885,13 +888,12 @@ EXP_ST void write_bitmap(void) { EXP_ST void read_bitmap(u8* fname) { - s32 fd = open(fname, O_RDONLY); - - if (fd < 0) PFATAL("Unable to open '%s'", fname); + s32 fd = open(fname, O_RDONLY); // 打开位图文件 - ck_read(fd, virgin_bits, MAP_SIZE, fname); + if (fd < 0) PFATAL("Unable to open '%s'", fname); // 如果打开失败,报错并退出 - close(fd); + ck_read(fd, virgin_bits, MAP_SIZE, fname); // 读取位图数据 + close(fd); // 关闭文件 } @@ -908,38 +910,38 @@ static inline u8 has_new_bits(u8* virgin_map) { #ifdef WORD_SIZE_64 - u64* current = (u64*)trace_bits; - u64* virgin = (u64*)virgin_map; + u64* current = (u64*)trace_bits; // 将 trace_bits 视为 64 位数组 + u64* virgin = (u64*)virgin_map; // 将 virgin_map 视为 64 位数组 - u32 i = (MAP_SIZE >> 3); + u32 i = (MAP_SIZE >> 3); // 计算 64 位数组的长度 #else - u32* current = (u32*)trace_bits; - u32* virgin = (u32*)virgin_map; + u32* current = (u32*)trace_bits; // 将 trace_bits 视为 32 位数组 + u32* virgin = (u32*)virgin_map; // 将 virgin_map 视为 32 位数组 - u32 i = (MAP_SIZE >> 2); + u32 i = (MAP_SIZE >> 2); // 计算 32 位数组的长度 #endif /* ^WORD_SIZE_64 */ u8 ret = 0; - while (i--) { + while (i--) { // 遍历数组 /* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap that have not been already cleared from the virgin map - since this will almost always be the case. */ - + // 优化常见情况:当前位图与原始位图没有新的位 if (unlikely(*current) && unlikely(*current & *virgin)) { if (likely(ret < 2)) { - u8* cur = (u8*)current; - u8* vir = (u8*)virgin; + u8* cur = (u8*)current; // 将当前指针视为字节数组 + u8* vir = (u8*)virgin; // 将原始指针视为字节数组 /* Looks like we have not found any new bytes yet; see if any non-zero bytes in current[] are pristine in virgin[]. */ - + // 检查是否有新的字节被发现 #ifdef WORD_SIZE_64 if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) || @@ -958,16 +960,16 @@ static inline u8 has_new_bits(u8* virgin_map) { } - *virgin &= ~*current; + *virgin &= ~*current; // 更新原始位图 } - current++; + current++; // 移动到下一个 64/32 位 virgin++; } - if (ret && virgin_map == virgin_bits) bitmap_changed = 1; + if (ret && virgin_map == virgin_bits) bitmap_changed = 1; // 如果发现新位且 virgin_map 是 virgin_bits,标记位图已改变 return ret; @@ -979,25 +981,25 @@ static inline u8 has_new_bits(u8* virgin_map) { static u32 count_bits(u8* mem) { - u32* ptr = (u32*)mem; - u32 i = (MAP_SIZE >> 2); + u32* ptr = (u32*)mem; // 将内存视为 32 位数组 + u32 i = (MAP_SIZE >> 2); // 计算 32 位数组的长度 u32 ret = 0; - while (i--) { + while (i--) { // 遍历数组 u32 v = *(ptr++); /* This gets called on the inverse, virgin bitmap; optimize for sparse data. */ - + // 优化稀疏数据的情况 if (v == 0xffffffff) { ret += 32; continue; } - v -= ((v >> 1) & 0x55555555); - v = (v & 0x33333333) + ((v >> 2) & 0x33333333); - ret += (((v + (v >> 4)) & 0xF0F0F0F) * 0x01010101) >> 24; + v -= ((v >> 1) & 0x55555555); // 计算每两位中的 1 的个数 + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // 计算每四位中的 1 的个数 + ret += (((v + (v >> 4)) & 0xF0F0F0F) * 0x01010101) >> 24; // 计算每字节中的 1 的个数 } @@ -1014,19 +1016,19 @@ static u32 count_bits(u8* mem) { static u32 count_bytes(u8* mem) { - u32* ptr = (u32*)mem; - u32 i = (MAP_SIZE >> 2); + u32* ptr = (u32*)mem; // 将内存视为 32 位数组 + u32 i = (MAP_SIZE >> 2); // 计算 32 位数组的长度 u32 ret = 0; - while (i--) { + while (i--) { // 遍历数组 u32 v = *(ptr++); - if (!v) continue; - if (v & FF(0)) ret++; - if (v & FF(1)) ret++; - if (v & FF(2)) ret++; - if (v & FF(3)) ret++; + if (!v) continue; // 如果值为 0,跳过 + if (v & FF(0)) ret++; // 检查第一个字节 + if (v & FF(1)) ret++; // 检查第二个字节 + if (v & FF(2)) ret++; // 检查第三个字节 + if (v & FF(3)) ret++; // 检查第四个字节 } @@ -1040,22 +1042,22 @@ static u32 count_bytes(u8* mem) { static u32 count_non_255_bytes(u8* mem) { - u32* ptr = (u32*)mem; - u32 i = (MAP_SIZE >> 2); + u32* ptr = (u32*)mem; // 将内存视为 32 位数组 + u32 i = (MAP_SIZE >> 2); // 计算 32 位数组的长度 u32 ret = 0; - while (i--) { + while (i--) { // 遍历数组 u32 v = *(ptr++); /* This is called on the virgin bitmap, so optimize for the most likely case. */ - + // 优化常见情况:值为 0xffffffff if (v == 0xffffffff) continue; - if ((v & FF(0)) != FF(0)) ret++; - if ((v & FF(1)) != FF(1)) ret++; - if ((v & FF(2)) != FF(2)) ret++; - if ((v & FF(3)) != FF(3)) ret++; + if ((v & FF(0)) != FF(0)) ret++; // 检查第一个字节 + if ((v & FF(1)) != FF(1)) ret++; // 检查第二个字节 + if ((v & FF(2)) != FF(2)) ret++; // 检查第三个字节 + if ((v & FF(3)) != FF(3)) ret++; // 检查第四个字节 } @@ -1071,8 +1073,8 @@ static u32 count_non_255_bytes(u8* mem) { static const u8 simplify_lookup[256] = { - [0] = 1, - [1 ... 255] = 128 + [0] = 1, // 0 映射为 1 + [1 ... 255] = 128 // 1-255 映射为 128 }; @@ -1080,26 +1082,26 @@ static const u8 simplify_lookup[256] = { static void simplify_trace(u64* mem) { - u32 i = MAP_SIZE >> 3; + u32 i = MAP_SIZE >> 3; // 计算 64 位数组的长度 - while (i--) { + while (i--) { // 遍历数组 /* Optimize for sparse bitmaps. */ - + // 优化稀疏位图的情况 if (unlikely(*mem)) { - u8* mem8 = (u8*)mem; + u8* mem8 = (u8*)mem; // 将当前指针视为字节数组 - mem8[0] = simplify_lookup[mem8[0]]; - mem8[1] = simplify_lookup[mem8[1]]; - mem8[2] = simplify_lookup[mem8[2]]; - mem8[3] = simplify_lookup[mem8[3]]; - mem8[4] = simplify_lookup[mem8[4]]; - mem8[5] = simplify_lookup[mem8[5]]; - mem8[6] = simplify_lookup[mem8[6]]; - mem8[7] = simplify_lookup[mem8[7]]; + mem8[0] = simplify_lookup[mem8[0]]; // 简化第一个字节 + mem8[1] = simplify_lookup[mem8[1]]; // 简化第二个字节 + mem8[2] = simplify_lookup[mem8[2]]; // 简化第三个字节 + mem8[3] = simplify_lookup[mem8[3]]; // 简化第四个字节 + mem8[4] = simplify_lookup[mem8[4]]; // 简化第五个字节 + mem8[5] = simplify_lookup[mem8[5]]; // 简化第六个字节 + mem8[6] = simplify_lookup[mem8[6]]; // 简化第七个字节 + mem8[7] = simplify_lookup[mem8[7]]; // 简化第八个字节 - } else *mem = 0x0101010101010101ULL; + } else *mem = 0x0101010101010101ULL; // 如果值为 0,设置为 0x0101010101010101 mem++; @@ -1111,22 +1113,22 @@ static void simplify_trace(u64* mem) { static void simplify_trace(u32* mem) { - u32 i = MAP_SIZE >> 2; + u32 i = MAP_SIZE >> 2; // 计算 32 位数组的长度 - while (i--) { + while (i--) { // 遍历数组 /* Optimize for sparse bitmaps. */ - + // 优化稀疏位图的情况 if (unlikely(*mem)) { - u8* mem8 = (u8*)mem; + u8* mem8 = (u8*)mem; // 将当前指针视为字节数组 - mem8[0] = simplify_lookup[mem8[0]]; - mem8[1] = simplify_lookup[mem8[1]]; - mem8[2] = simplify_lookup[mem8[2]]; - mem8[3] = simplify_lookup[mem8[3]]; + mem8[0] = simplify_lookup[mem8[0]]; // 简化第一个字节 + mem8[1] = simplify_lookup[mem8[1]]; // 简化第二个字节 + mem8[2] = simplify_lookup[mem8[2]]; // 简化第三个字节 + mem8[3] = simplify_lookup[mem8[3]]; // 简化第四个字节 - } else *mem = 0x01010101; + } else *mem = 0x01010101; // 如果值为 0,设置为 0x01010101 mem++; } @@ -1142,19 +1144,19 @@ static void simplify_trace(u32* mem) { static const u8 count_class_lookup8[256] = { - [0] = 0, - [1] = 1, - [2] = 2, - [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [0] = 0, // 0 映射为 0 + [1] = 1, // 1 映射为 1 + [2] = 2, // 2 映射为 2 + [3] = 4, // 3 映射为 4 + [4 ... 7] = 8, // 4-7 映射为 8 + [8 ... 15] = 16, // 8-15 映射为 16 + [16 ... 31] = 32, // 16-31 映射为 32 + [32 ... 127] = 64, // 32-127 映射为 64 + [128 ... 255] = 128 // 128-255 映射为 128 }; -static u16 count_class_lookup16[65536]; +static u16 count_class_lookup16[65536]; // 16 位计数分类查找表 EXP_ST void init_count_class16(void) { @@ -1164,8 +1166,8 @@ EXP_ST void init_count_class16(void) { for (b1 = 0; b1 < 256; b1++) for (b2 = 0; b2 < 256; b2++) count_class_lookup16[(b1 << 8) + b2] = - (count_class_lookup8[b1] << 8) | - count_class_lookup8[b2]; + (count_class_lookup8[b1] << 8) | // 高字节分类 + count_class_lookup8[b2]; // 低字节分类 } @@ -1174,20 +1176,20 @@ EXP_ST void init_count_class16(void) { static inline void classify_counts(u64* mem) { - u32 i = MAP_SIZE >> 3; + u32 i = MAP_SIZE >> 3; // 计算 64 位数组的长度 - while (i--) { + while (i--) { // 遍历数组 /* Optimize for sparse bitmaps. */ - + // 优化稀疏位图的情况 if (unlikely(*mem)) { - u16* mem16 = (u16*)mem; + u16* mem16 = (u16*)mem; // 将当前指针视为 16 位数组 - mem16[0] = count_class_lookup16[mem16[0]]; - mem16[1] = count_class_lookup16[mem16[1]]; - mem16[2] = count_class_lookup16[mem16[2]]; - mem16[3] = count_class_lookup16[mem16[3]]; + mem16[0] = count_class_lookup16[mem16[0]]; // 分类第一个 16 位 + mem16[1] = count_class_lookup16[mem16[1]]; // 分类第二个 16 位 + mem16[2] = count_class_lookup16[mem16[2]]; // 分类第三个 16 位 + mem16[3] = count_class_lookup16[mem16[3]]; // 分类第四个 16 位 } @@ -1201,18 +1203,18 @@ static inline void classify_counts(u64* mem) { static inline void classify_counts(u32* mem) { - u32 i = MAP_SIZE >> 2; + u32 i = MAP_SIZE >> 2; // 计算 32 位数组的长度 - while (i--) { + while (i--) { // 遍历数组 /* Optimize for sparse bitmaps. */ - + // 优化稀疏位图的情况 if (unlikely(*mem)) { - u16* mem16 = (u16*)mem; + u16* mem16 = (u16*)mem; // 将当前指针视为 16 位数组 - mem16[0] = count_class_lookup16[mem16[0]]; - mem16[1] = count_class_lookup16[mem16[1]]; + mem16[0] = count_class_lookup16[mem16[0]]; // 分类第一个 16 位 + mem16[1] = count_class_lookup16[mem16[1]]; // 分类第二个 16 位 } @@ -1229,7 +1231,7 @@ static inline void classify_counts(u32* mem) { static void remove_shm(void) { - shmctl(shm_id, IPC_RMID, NULL); + shmctl(shm_id, IPC_RMID, NULL); // 删除共享内存段 } @@ -1244,7 +1246,7 @@ static void minimize_bits(u8* dst, u8* src) { while (i < MAP_SIZE) { - if (*(src++)) dst[i >> 3] |= 1 << (i & 7); + if (*(src++)) dst[i >> 3] |= 1 << (i & 7); // 如果源位图中有位设置,设置目标位图中的相应位 i++; } @@ -1265,11 +1267,11 @@ static void minimize_bits(u8* dst, u8* src) { static void update_bitmap_score(struct queue_entry* q) { u32 i; - u64 fav_factor = q->exec_us * q->len; + u64 fav_factor = q->exec_us * q->len; // 计算速度与大小的乘积 /* For every byte set in trace_bits[], see if there is a previous winner, and how it compares to us. */ - + // 遍历 trace_bits,检查是否有更优的路径 for (i = 0; i < MAP_SIZE; i++) if (trace_bits[i]) { @@ -1277,12 +1279,12 @@ static void update_bitmap_score(struct queue_entry* q) { if (top_rated[i]) { /* Faster-executing or smaller test cases are favored. */ - + // 如果当前路径更优,跳过 if (fav_factor > top_rated[i]->exec_us * top_rated[i]->len) continue; /* Looks like we're going to win. Decrease ref count for the previous winner, discard its trace_bits[] if necessary. */ - + // 如果当前路径更优,减少前一个获胜者的引用计数 if (!--top_rated[i]->tc_ref) { ck_free(top_rated[i]->trace_mini); top_rated[i]->trace_mini = 0; @@ -1291,7 +1293,7 @@ static void update_bitmap_score(struct queue_entry* q) { } /* Insert ourselves as the new winner. */ - + // 将当前路径设置为新的获胜者 top_rated[i] = q; q->tc_ref++; @@ -1319,11 +1321,11 @@ static void cull_queue(void) { static u8 temp_v[MAP_SIZE >> 3]; u32 i; - if (dumb_mode || !score_changed) return; + if (dumb_mode || !score_changed) return; // 如果处于 dumb 模式或分数未改变,直接返回 score_changed = 0; - memset(temp_v, 255, MAP_SIZE >> 3); + memset(temp_v, 255, MAP_SIZE >> 3); // 初始化 temp_v queued_favored = 0; pending_favored = 0; @@ -1331,25 +1333,25 @@ static void cull_queue(void) { q = queue; while (q) { - q->favored = 0; + q->favored = 0; // 重置所有队列项的 favored 标志 q = q->next; } /* Let's see if anything in the bitmap isn't captured in temp_v. If yes, and if it has a top_rated[] contender, let's use it. */ - + // 检查是否有未被 temp_v 捕获的位,如果有且存在 top_rated 路径,标记为 favored for (i = 0; i < MAP_SIZE; i++) if (top_rated[i] && (temp_v[i >> 3] & (1 << (i & 7)))) { u32 j = MAP_SIZE >> 3; /* Remove all bits belonging to the current entry from temp_v. */ - + // 从 temp_v 中移除当前路径的所有位 while (j--) if (top_rated[i]->trace_mini[j]) temp_v[j] &= ~top_rated[i]->trace_mini[j]; - top_rated[i]->favored = 1; + top_rated[i]->favored = 1; // 标记为 favored queued_favored++; if (!top_rated[i]->was_fuzzed) pending_favored++; @@ -1359,7 +1361,7 @@ static void cull_queue(void) { q = queue; while (q) { - mark_as_redundant(q, !q->favored); + mark_as_redundant(q, !q->favored); // 标记非 favored 路径为冗余 q = q->next; } @@ -1372,31 +1374,31 @@ EXP_ST void setup_shm(void) { u8* shm_str; - if (!in_bitmap) memset(virgin_bits, 255, MAP_SIZE); + if (!in_bitmap) memset(virgin_bits, 255, MAP_SIZE); // 如果未提供输入位图,初始化 virgin_bits - memset(virgin_tmout, 255, MAP_SIZE); - memset(virgin_crash, 255, MAP_SIZE); + memset(virgin_tmout, 255, MAP_SIZE); // 初始化 virgin_tmout + memset(virgin_crash, 255, MAP_SIZE); // 初始化 virgin_crash - shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); + shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); // 创建共享内存段 - if (shm_id < 0) PFATAL("shmget() failed"); + if (shm_id < 0) PFATAL("shmget() failed"); // 如果创建失败,报错并退出 - atexit(remove_shm); + atexit(remove_shm); // 注册退出时删除共享内存段的处理函数 - shm_str = alloc_printf("%d", shm_id); + shm_str = alloc_printf("%d", shm_id); // 生成共享内存 ID 字符串 /* If somebody is asking us to fuzz instrumented binaries in dumb mode, we don't want them to detect instrumentation, since we won't be sending fork server commands. This should be replaced with better auto-detection later on, perhaps? */ - + // 如果处于 dumb 模式,不设置共享内存环境变量 if (!dumb_mode) setenv(SHM_ENV_VAR, shm_str, 1); - ck_free(shm_str); + ck_free(shm_str); // 释放内存 - trace_bits = shmat(shm_id, NULL, 0); + trace_bits = shmat(shm_id, NULL, 0); // 附加共享内存段 - if (trace_bits == (void *)-1) PFATAL("shmat() failed"); + if (trace_bits == (void *)-1) PFATAL("shmat() failed"); // 如果附加失败,报错并退出 } @@ -1406,24 +1408,24 @@ EXP_ST void setup_shm(void) { static void setup_post(void) { void* dh; - u8* fn = getenv("AFL_POST_LIBRARY"); + u8* fn = getenv("AFL_POST_LIBRARY"); // 获取后处理库路径 u32 tlen = 6; - if (!fn) return; + if (!fn) return; // 如果未设置后处理库,直接返回 - ACTF("Loading postprocessor from '%s'...", fn); + ACTF("Loading postprocessor from '%s'...", fn); // 加载后处理库 - dh = dlopen(fn, RTLD_NOW); - if (!dh) FATAL("%s", dlerror()); + dh = dlopen(fn, RTLD_NOW); // 打开后处理库 + if (!dh) FATAL("%s", dlerror()); // 如果打开失败,报错并退出 - post_handler = dlsym(dh, "afl_postprocess"); - if (!post_handler) FATAL("Symbol 'afl_postprocess' not found."); + post_handler = dlsym(dh, "afl_postprocess"); // 获取后处理函数 + if (!post_handler) FATAL("Symbol 'afl_postprocess' not found."); // 如果未找到后处理函数,报错并退出 /* Do a quick test. It's better to segfault now than later =) */ - + // 快速测试后处理函数 post_handler("hello", &tlen); - OKF("Postprocessor installed successfully."); + OKF("Postprocessor installed successfully."); // 后处理库安装成功 } @@ -1439,21 +1441,21 @@ static void read_testcases(void) { u8* fn; /* Auto-detect non-in-place resumption attempts. */ - + // 自动检测非原地恢复尝试 fn = alloc_printf("%s/queue", in_dir); if (!access(fn, F_OK)) in_dir = fn; else ck_free(fn); - ACTF("Scanning '%s'...", in_dir); + ACTF("Scanning '%s'...", in_dir); // 扫描输入目录 /* We use scandir() + alphasort() rather than readdir() because otherwise, the ordering of test cases would vary somewhat randomly and would be difficult to control. */ - + // 使用 scandir 和 alphasort 来排序测试用例 nl_cnt = scandir(in_dir, &nl, NULL, alphasort); - if (nl_cnt < 0) { + if (nl_cnt < 0) { // 如果扫描失败 - if (errno == ENOENT || errno == ENOTDIR) + if (errno == ENOENT || errno == ENOTDIR) // 如果目录不存在或不是目录 SAYF("\n" cLRD "[-] " cRST "The input directory does not seem to be valid - try again. The fuzzer needs\n" @@ -1461,33 +1463,33 @@ static void read_testcases(void) { " or so. The cases must be stored as regular files directly in the input\n" " directory.\n"); - PFATAL("Unable to open '%s'", in_dir); + PFATAL("Unable to open '%s'", in_dir); // 报错并退出 } - if (shuffle_queue && nl_cnt > 1) { + if (shuffle_queue && nl_cnt > 1) { // 如果需要打乱队列 - ACTF("Shuffling queue..."); + ACTF("Shuffling queue..."); // 打乱队列 shuffle_ptrs((void**)nl, nl_cnt); } - for (i = 0; i < nl_cnt; i++) { + for (i = 0; i < nl_cnt; i++) { // 遍历所有文件 struct stat st; - u8* fn = alloc_printf("%s/%s", in_dir, nl[i]->d_name); - u8* dfn = alloc_printf("%s/.state/deterministic_done/%s", in_dir, nl[i]->d_name); + u8* fn = alloc_printf("%s/%s", in_dir, nl[i]->d_name); // 生成文件路径 + u8* dfn = alloc_printf("%s/.state/deterministic_done/%s", in_dir, nl[i]->d_name); // 生成确定性测试完成状态文件路径 u8 passed_det = 0; free(nl[i]); /* not tracked */ - if (lstat(fn, &st) || access(fn, R_OK)) + if (lstat(fn, &st) || access(fn, R_OK)) // 如果无法访问文件 PFATAL("Unable to access '%s'", fn); /* This also takes care of . and .. */ - + // 忽略非常规文件、空文件和 README.testcases 文件 if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn, "/README.testcases")) { ck_free(fn); @@ -1496,7 +1498,7 @@ static void read_testcases(void) { } - if (st.st_size > MAX_FILE) + if (st.st_size > MAX_FILE) // 如果文件过大 FATAL("Test case '%s' is too big (%s, limit is %s)", fn, DMS(st.st_size), DMS(MAX_FILE)); @@ -1504,17 +1506,17 @@ static void read_testcases(void) { is complete for this entry. We don't want to repeat deterministic fuzzing when resuming aborted scans, because it would be pointless and probably very time-consuming. */ - + // 检查是否已完成确定性测试 if (!access(dfn, F_OK)) passed_det = 1; ck_free(dfn); - add_to_queue(fn, st.st_size, passed_det); + add_to_queue(fn, st.st_size, passed_det); // 将文件添加到队列 } free(nl); /* not tracked */ - if (!queued_paths) { + if (!queued_paths) { // 如果队列为空 SAYF("\n" cLRD "[-] " cRST "Looks like there are no valid test cases in the input directory! The fuzzer\n" @@ -1522,7 +1524,7 @@ static void read_testcases(void) { " 1 kB or so. The cases must be stored as regular files directly in the\n" " input directory.\n"); - FATAL("No usable test cases in '%s'", in_dir); + FATAL("No usable test cases in '%s'", in_dir); // 报错并退出 } @@ -1538,14 +1540,14 @@ static int compare_extras_len(const void* p1, const void* p2) { struct extra_data *e1 = (struct extra_data*)p1, *e2 = (struct extra_data*)p2; - return e1->len - e2->len; + return e1->len - e2->len; // 按长度比较 } static int compare_extras_use_d(const void* p1, const void* p2) { struct extra_data *e1 = (struct extra_data*)p1, *e2 = (struct extra_data*)p2; - return e2->hit_cnt - e1->hit_cnt; + return e2->hit_cnt - e1->hit_cnt; // 按使用次数比较 } @@ -1559,11 +1561,11 @@ static void load_extras_file(u8* fname, u32* min_len, u32* max_len, u8 *lptr; u32 cur_line = 0; - f = fopen(fname, "r"); + f = fopen(fname, "r"); // 打开文件 - if (!f) PFATAL("Unable to open '%s'", fname); + if (!f) PFATAL("Unable to open '%s'", fname); // 如果打开失败,报错并退出 - while ((lptr = fgets(buf, MAX_LINE, f))) { + while ((lptr = fgets(buf, MAX_LINE, f))) { // 逐行读取文件 u8 *rptr, *wptr; u32 klen = 0; @@ -1571,7 +1573,7 @@ static void load_extras_file(u8* fname, u32* min_len, u32* max_len, cur_line++; /* Trim on left and right. */ - + // 去除左右空白字符 while (isspace(*lptr)) lptr++; rptr = lptr + strlen(lptr) - 1; @@ -1580,11 +1582,11 @@ static void load_extras_file(u8* fname, u32* min_len, u32* max_len, *rptr = 0; /* Skip empty lines and comments. */ - + // 跳过空行和注释 if (!*lptr || *lptr == '#') continue; /* All other lines must end with '"', which we can consume. */ - + // 其他行必须以双引号结尾 rptr--; if (rptr < lptr || *rptr != '"') @@ -1593,11 +1595,11 @@ static void load_extras_file(u8* fname, u32* min_len, u32* max_len, *rptr = 0; /* Skip alphanumerics and dashes (label). */ - + // 跳过字母数字和破折号(标签) while (isalnum(*lptr) || *lptr == '_') lptr++; /* If @number follows, parse that. */ - + // 如果包含 @number,解析数字 if (*lptr == '@') { lptr++; @@ -1607,11 +1609,11 @@ static void load_extras_file(u8* fname, u32* min_len, u32* max_len, } /* Skip whitespace and = signs. */ - + // 跳过空白字符和等号 while (isspace(*lptr) || *lptr == '=') lptr++; /* Consume opening '"'. */ - + // 跳过开头的双引号 if (*lptr != '"') FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line); @@ -1621,7 +1623,7 @@ static void load_extras_file(u8* fname, u32* min_len, u32* max_len, /* Okay, let's allocate memory and copy data between "...", handling \xNN escaping, \\, and \". */ - + // 分配内存并复制数据,处理转义字符 extras = ck_realloc_block(extras, (extras_cnt + 1) * sizeof(struct extra_data)); @@ -1696,7 +1698,7 @@ static void load_extras(u8* dir) { u8* x; /* If the name ends with @, extract level and continue. */ - + // 如果目录名以 @ 结尾,提取级别 if ((x = strchr(dir, '@'))) { *x = 0; @@ -1704,33 +1706,34 @@ static void load_extras(u8* dir) { } - ACTF("Loading extra dictionary from '%s' (level %u)...", dir, dict_level); + ACTF("Loading extra dictionary from '%s' (level %u)...", dir, dict_level); // 加载额外字典 - d = opendir(dir); + d = opendir(dir); // 打开目录 - if (!d) { + if (!d) { // 如果打开失败 - if (errno == ENOTDIR) { - load_extras_file(dir, &min_len, &max_len, dict_level); + if (errno == ENOTDIR) { // 如果不是目录 + load_extras_file(dir, &min_len, &max_len, dict_level); // 加载文件 goto check_and_sort; } - PFATAL("Unable to open '%s'", dir); + PFATAL("Unable to open '%s'", dir); // 报错并退出 } - if (x) FATAL("Dictionary levels not supported for directories."); + if (x) FATAL("Dictionary levels not supported for directories."); // 目录不支持字典级别 - while ((de = readdir(d))) { + while ((de = readdir(d))) { // 遍历目录 struct stat st; - u8* fn = alloc_printf("%s/%s", dir, de->d_name); + u8* fn = alloc_printf("%s/%s", dir, de->d_name); // 生成文件路径 s32 fd; - if (lstat(fn, &st) || access(fn, R_OK)) + if (lstat(fn, &st) || access(fn, R_OK)) // 如果无法访问文件 PFATAL("Unable to access '%s'", fn); /* This also takes care of . and .. */ + // 忽略非常规文件和空文件 if (!S_ISREG(st.st_mode) || !st.st_size) { ck_free(fn); @@ -1738,7 +1741,7 @@ static void load_extras(u8* dir) { } - if (st.st_size > MAX_DICT_FILE) + if (st.st_size > MAX_DICT_FILE) // 如果文件过大 FATAL("Extra '%s' is too big (%s, limit is %s)", fn, DMS(st.st_size), DMS(MAX_DICT_FILE)); @@ -1751,11 +1754,11 @@ static void load_extras(u8* dir) { extras[extras_cnt].data = ck_alloc(st.st_size); extras[extras_cnt].len = st.st_size; - fd = open(fn, O_RDONLY); + fd = open(fn, O_RDONLY); // 打开文件 - if (fd < 0) PFATAL("Unable to open '%s'", fn); + if (fd < 0) PFATAL("Unable to open '%s'", fn); // 如果打开失败,报错并退出 - ck_read(fd, extras[extras_cnt].data, st.st_size, fn); + ck_read(fd, extras[extras_cnt].data, st.st_size, fn); // 读取文件内容 close(fd); ck_free(fn); @@ -1768,31 +1771,27 @@ static void load_extras(u8* dir) { check_and_sort: - if (!extras_cnt) FATAL("No usable files in '%s'", dir); + if (!extras_cnt) FATAL("No usable files in '%s'", dir); // 如果没有可用文件,报错并退出 - qsort(extras, extras_cnt, sizeof(struct extra_data), compare_extras_len); + qsort(extras, extras_cnt, sizeof(struct extra_data), compare_extras_len); // 按长度排序 OKF("Loaded %u extra tokens, size range %s to %s.", extras_cnt, - DMS(min_len), DMS(max_len)); + DMS(min_len), DMS(max_len)); // 加载成功 if (max_len > 32) WARNF("Some tokens are relatively large (%s) - consider trimming.", - DMS(max_len)); + DMS(max_len)); // 如果某些标记较大,建议修剪 if (extras_cnt > MAX_DET_EXTRAS) WARNF("More than %u tokens - will use them probabilistically.", - MAX_DET_EXTRAS); + MAX_DET_EXTRAS); // 如果标记过多,将概率性使用 } - - - - /* Helper function for maybe_add_auto() */ static inline u8 memcmp_nocase(u8* m1, u8* m2, u32 len) { - while (len--) if (tolower(*(m1++)) ^ tolower(*(m2++))) return 1; + while (len--) if (tolower(*(m1++)) ^ tolower(*(m2++))) return 1; // 不区分大小写比较内存块 return 0; } @@ -1805,24 +1804,24 @@ static void maybe_add_auto(u8* mem, u32 len) { u32 i; /* Allow users to specify that they don't want auto dictionaries. */ - + // 如果用户指定不使用自动字典,直接返回 if (!MAX_AUTO_EXTRAS || !USE_AUTO_EXTRAS) return; /* Skip runs of identical bytes. */ - + // 跳过连续的相同字节 for (i = 1; i < len; i++) if (mem[0] ^ mem[i]) break; - if (i == len) return; + if (i == len) return; // 如果所有字节都相同,直接返回 /* Reject builtin interesting values. */ - + // 拒绝内置的 interesting 值 if (len == 2) { i = sizeof(interesting_16) >> 1; while (i--) - if (*((u16*)mem) == interesting_16[i] || + if (*((u16*)mem) == interesting_16[i] || // 检查是否为 interesting_16 值 *((u16*)mem) == SWAP16(interesting_16[i])) return; } @@ -1832,7 +1831,7 @@ static void maybe_add_auto(u8* mem, u32 len) { i = sizeof(interesting_32) >> 2; while (i--) - if (*((u32*)mem) == interesting_32[i] || + if (*((u32*)mem) == interesting_32[i] || // 检查是否为 interesting_32 值 *((u32*)mem) == SWAP32(interesting_32[i])) return; } @@ -1840,23 +1839,23 @@ static void maybe_add_auto(u8* mem, u32 len) { /* Reject anything that matches existing extras. Do a case-insensitive match. We optimize by exploiting the fact that extras[] are sorted by size. */ - + // 拒绝与现有 extras 匹配的内容,不区分大小写匹配 for (i = 0; i < extras_cnt; i++) if (extras[i].len >= len) break; for (; i < extras_cnt && extras[i].len == len; i++) - if (!memcmp_nocase(extras[i].data, mem, len)) return; + if (!memcmp_nocase(extras[i].data, mem, len)) return; // 如果匹配,直接返回 /* Last but not least, check a_extras[] for matches. There are no guarantees of a particular sort order. */ - + // 最后检查 a_extras 中是否有匹配项 auto_changed = 1; for (i = 0; i < a_extras_cnt; i++) { - if (a_extras[i].len == len && !memcmp_nocase(a_extras[i].data, mem, len)) { + if (a_extras[i].len == len && !memcmp_nocase(a_extras[i].data, mem, len)) { // 如果匹配 - a_extras[i].hit_cnt++; + a_extras[i].hit_cnt++; // 增加命中计数 goto sort_a_extras; } @@ -1866,24 +1865,24 @@ static void maybe_add_auto(u8* mem, u32 len) { /* At this point, looks like we're dealing with a new entry. So, let's append it if we have room. Otherwise, let's randomly evict some other entry from the bottom half of the list. */ - + // 如果是一个新条目,如果有空间则追加,否则随机淘汰列表底部的条目 if (a_extras_cnt < MAX_AUTO_EXTRAS) { a_extras = ck_realloc_block(a_extras, (a_extras_cnt + 1) * sizeof(struct extra_data)); - a_extras[a_extras_cnt].data = ck_memdup(mem, len); + a_extras[a_extras_cnt].data = ck_memdup(mem, len); // 复制内存 a_extras[a_extras_cnt].len = len; a_extras_cnt++; } else { i = MAX_AUTO_EXTRAS / 2 + - UR((MAX_AUTO_EXTRAS + 1) / 2); + UR((MAX_AUTO_EXTRAS + 1) / 2); // 随机选择一个索引 - ck_free(a_extras[i].data); + ck_free(a_extras[i].data); // 释放旧数据 - a_extras[i].data = ck_memdup(mem, len); + a_extras[i].data = ck_memdup(mem, len); // 复制新数据 a_extras[i].len = len; a_extras[i].hit_cnt = 0; @@ -1892,12 +1891,12 @@ static void maybe_add_auto(u8* mem, u32 len) { sort_a_extras: /* First, sort all auto extras by use count, descending order. */ - + // 首先按使用计数降序排序 qsort(a_extras, a_extras_cnt, sizeof(struct extra_data), compare_extras_use_d); /* Then, sort the top USE_AUTO_EXTRAS entries by size. */ - + // 然后按大小排序前 USE_AUTO_EXTRAS 个条目 qsort(a_extras, MIN(USE_AUTO_EXTRAS, a_extras_cnt), sizeof(struct extra_data), compare_extras_len); @@ -1910,19 +1909,19 @@ static void save_auto(void) { u32 i; - if (!auto_changed) return; + if (!auto_changed) return; // 如果没有自动生成的 extras 改变,直接返回 auto_changed = 0; for (i = 0; i < MIN(USE_AUTO_EXTRAS, a_extras_cnt); i++) { - u8* fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", out_dir, i); + u8* fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", out_dir, i); // 生成文件路径 s32 fd; - fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); // 创建文件 - if (fd < 0) PFATAL("Unable to create '%s'", fn); + if (fd < 0) PFATAL("Unable to create '%s'", fn); // 如果创建失败,报错并退出 - ck_write(fd, a_extras[i].data, a_extras[i].len, fn); + ck_write(fd, a_extras[i].data, a_extras[i].len, fn); // 写入数据 close(fd); ck_free(fn); @@ -1941,14 +1940,14 @@ static void load_auto(void) { for (i = 0; i < USE_AUTO_EXTRAS; i++) { u8 tmp[MAX_AUTO_EXTRA + 1]; - u8* fn = alloc_printf("%s/.state/auto_extras/auto_%06u", in_dir, i); + u8* fn = alloc_printf("%s/.state/auto_extras/auto_%06u", in_dir, i); // 生成文件路径 s32 fd, len; - fd = open(fn, O_RDONLY, 0600); + fd = open(fn, O_RDONLY, 0600); // 打开文件 if (fd < 0) { - if (errno != ENOENT) PFATAL("Unable to open '%s'", fn); + if (errno != ENOENT) PFATAL("Unable to open '%s'", fn); // 如果打开失败且不是文件不存在,报错并退出 ck_free(fn); break; @@ -1956,21 +1955,21 @@ static void load_auto(void) { /* We read one byte more to cheaply detect tokens that are too long (and skip them). */ - + // 多读一个字节以检测过长的标记 len = read(fd, tmp, MAX_AUTO_EXTRA + 1); - if (len < 0) PFATAL("Unable to read from '%s'", fn); + if (len < 0) PFATAL("Unable to read from '%s'", fn); // 如果读取失败,报错并退出 if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA) - maybe_add_auto(tmp, len); + maybe_add_auto(tmp, len); // 如果长度合适,尝试添加自动生成的 extra close(fd); ck_free(fn); } - if (i) OKF("Loaded %u auto-discovered dictionary tokens.", i); - else OKF("No auto-generated dictionary tokens to reuse."); + if (i) OKF("Loaded %u auto-discovered dictionary tokens.", i); // 加载成功 + else OKF("No auto-generated dictionary tokens to reuse."); // 没有自动生成的字典标记 } @@ -1982,12 +1981,12 @@ static void destroy_extras(void) { u32 i; for (i = 0; i < extras_cnt; i++) - ck_free(extras[i].data); + ck_free(extras[i].data); // 释放 extras 数据 ck_free(extras); for (i = 0; i < a_extras_cnt; i++) - ck_free(a_extras[i].data); + ck_free(a_extras[i].data); // 释放 a_extras 数据 ck_free(a_extras); @@ -2009,21 +2008,21 @@ EXP_ST void init_forkserver(char** argv) { int status; s32 rlen; - ACTF("Spinning up the fork server..."); + ACTF("Spinning up the fork server..."); // 启动 fork server - if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed"); + if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed"); // 创建管道 - forksrv_pid = fork(); + forksrv_pid = fork(); // 创建子进程 - if (forksrv_pid < 0) PFATAL("fork() failed"); + if (forksrv_pid < 0) PFATAL("fork() failed"); // 如果 fork 失败,报错并退出 - if (!forksrv_pid) { + if (!forksrv_pid) { // 子进程 struct rlimit r; /* Umpf. On OpenBSD, the default fd limit for root users is set to soft 128. Let's try to fix that... */ - + // 在 OpenBSD 上,root 用户的默认文件描述符限制为 128,尝试修复 if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) { r.rlim_cur = FORKSRV_FD + 2; @@ -2031,7 +2030,7 @@ EXP_ST void init_forkserver(char** argv) { } - if (mem_limit) { + if (mem_limit) { // 如果设置了内存限制 r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; @@ -2044,7 +2043,7 @@ EXP_ST void init_forkserver(char** argv) { /* This takes care of OpenBSD, which doesn't have RLIMIT_AS, but according to reliable sources, RLIMIT_DATA covers anonymous maps - so we should be getting good protection against OOM bugs. */ - + // 在 OpenBSD 上,RLIMIT_DATA 覆盖匿名映射 setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ #endif /* ^RLIMIT_AS */ @@ -2054,32 +2053,32 @@ EXP_ST void init_forkserver(char** argv) { /* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered before the dump is complete. */ - + // 禁用核心转储 r.rlim_max = r.rlim_cur = 0; setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ /* Isolate the process and configure standard descriptors. If out_file is specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */ - + // 隔离进程并配置标准描述符 setsid(); - dup2(dev_null_fd, 1); - dup2(dev_null_fd, 2); + dup2(dev_null_fd, 1); // 重定向标准输出到 /dev/null + dup2(dev_null_fd, 2); // 重定向标准错误到 /dev/null if (out_file) { - dup2(dev_null_fd, 0); + dup2(dev_null_fd, 0); // 如果指定了输出文件,重定向标准输入到 /dev/null } else { - dup2(out_fd, 0); + dup2(out_fd, 0); // 否则重定向标准输入到 out_fd close(out_fd); } /* Set up control and status pipes, close the unneeded original fds. */ - + // 设置控制和状态管道,关闭不需要的文件描述符 if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed"); if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed"); @@ -2095,11 +2094,11 @@ EXP_ST void init_forkserver(char** argv) { /* This should improve performance a bit, since it stops the linker from doing extra work post-fork(). */ - + // 设置 LD_BIND_NOW 以提高性能 if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); /* Set sane defaults for ASAN if nothing else specified. */ - + // 设置 ASAN 的默认选项 setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" "symbolize=0:" @@ -2107,60 +2106,60 @@ EXP_ST void init_forkserver(char** argv) { /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ - + // 设置 MSAN 的默认选项 setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" "abort_on_error=1:" "allocator_may_return_null=1:" "msan_track_origins=0", 0); - execv(target_path, argv); + execv(target_path, argv); // 执行目标程序 /* Use a distinctive bitmap signature to tell the parent about execv() falling through. */ - + // 如果 execv 失败,设置一个特殊的位图签名 *(u32*)trace_bits = EXEC_FAIL_SIG; exit(0); } /* Close the unneeded endpoints. */ - + // 关闭不需要的管道端点 close(ctl_pipe[0]); close(st_pipe[1]); - fsrv_ctl_fd = ctl_pipe[1]; - fsrv_st_fd = st_pipe[0]; + fsrv_ctl_fd = ctl_pipe[1]; // 设置控制管道写端 + fsrv_st_fd = st_pipe[0]; // 设置状态管道读端 /* Wait for the fork server to come up, but don't wait too long. */ - + // 等待 fork server 启动,但不要等待太久 it.it_value.tv_sec = ((exec_tmout * FORK_WAIT_MULT) / 1000); it.it_value.tv_usec = ((exec_tmout * FORK_WAIT_MULT) % 1000) * 1000; - setitimer(ITIMER_REAL, &it, NULL); + setitimer(ITIMER_REAL, &it, NULL); // 设置定时器 - rlen = read(fsrv_st_fd, &status, 4); + rlen = read(fsrv_st_fd, &status, 4); // 读取状态 it.it_value.tv_sec = 0; it.it_value.tv_usec = 0; - setitimer(ITIMER_REAL, &it, NULL); + setitimer(ITIMER_REAL, &it, NULL); // 清除定时器 /* If we have a four-byte "hello" message from the server, we're all set. Otherwise, try to figure out what went wrong. */ - + // 如果收到 4 字节的 "hello" 消息,说明 fork server 启动成功 if (rlen == 4) { OKF("All right - fork server is up."); return; } if (child_timed_out) - FATAL("Timeout while initializing fork server (adjusting -t may help)"); + FATAL("Timeout while initializing fork server (adjusting -t may help)"); // 如果超时,报错并退出 if (waitpid(forksrv_pid, &status, 0) <= 0) - PFATAL("waitpid() failed"); + PFATAL("waitpid() failed"); // 如果 waitpid 失败,报错并退出 - if (WIFSIGNALED(status)) { + if (WIFSIGNALED(status)) { // 如果子进程因信号终止 if (mem_limit && mem_limit < 500 && uses_asan) { @@ -2227,12 +2226,12 @@ EXP_ST void init_forkserver(char** argv) { } - FATAL("Fork server crashed with signal %d", WTERMSIG(status)); + FATAL("Fork server crashed with signal %d", WTERMSIG(status)); // 报错并退出 } if (*(u32*)trace_bits == EXEC_FAIL_SIG) - FATAL("Unable to execute target application ('%s')", argv[0]); + FATAL("Unable to execute target application ('%s')", argv[0]); // 如果 execv 失败,报错并退出 if (mem_limit && mem_limit < 500 && uses_asan) { @@ -2279,11 +2278,10 @@ EXP_ST void init_forkserver(char** argv) { } - FATAL("Fork server handshake failed"); + FATAL("Fork server handshake failed"); // 报错并退出 } - /* Execute target application, monitoring for timeouts. Return status information. The called program will update trace_bits[]. */ @@ -2301,7 +2299,7 @@ static u8 run_target(char** argv, u32 timeout) { /* After this memset, trace_bits[] are effectively volatile, so we must prevent any earlier operations from venturing into that territory. */ - + // 在 memset 之后,trace_bits[] 是易变的,因此需要防止之前的操作进入该区域 memset(trace_bits, 0, MAP_SIZE); MEM_BARRIER(); @@ -2309,18 +2307,18 @@ static u8 run_target(char** argv, u32 timeout) { logic compiled into the target program, so we will just keep calling execve(). There is a bit of code duplication between here and init_forkserver(), but c'est la vie. */ - + // 如果处于 "dumb" 模式,无法依赖目标程序中的 fork server 逻辑,只能不断调用 execve() if (dumb_mode == 1 || no_forkserver) { - child_pid = fork(); + child_pid = fork(); // 创建子进程 - if (child_pid < 0) PFATAL("fork() failed"); + if (child_pid < 0) PFATAL("fork() failed"); // 如果 fork 失败,报错并退出 - if (!child_pid) { + if (!child_pid) { // 子进程 struct rlimit r; - if (mem_limit) { + if (mem_limit) { // 如果设置了内存限制 r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; @@ -2338,36 +2336,36 @@ static u8 run_target(char** argv, u32 timeout) { r.rlim_max = r.rlim_cur = 0; - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ // 禁用核心转储 /* Isolate the process and configure standard descriptors. If out_file is specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */ - + // 隔离进程并配置标准描述符 setsid(); - dup2(dev_null_fd, 1); - dup2(dev_null_fd, 2); + dup2(dev_null_fd, 1); // 重定向标准输出到 /dev/null + dup2(dev_null_fd, 2); // 重定向标准错误到 /dev/null if (out_file) { - dup2(dev_null_fd, 0); + dup2(dev_null_fd, 0); // 如果指定了输出文件,重定向标准输入到 /dev/null } else { - dup2(out_fd, 0); + dup2(out_fd, 0); // 否则重定向标准输入到 out_fd close(out_fd); } /* On Linux, would be faster to use O_CLOEXEC. Maybe TODO. */ - + // 在 Linux 上,使用 O_CLOEXEC 会更快 close(dev_null_fd); close(out_dir_fd); close(dev_urandom_fd); close(fileno(plot_file)); /* Set sane defaults for ASAN if nothing else specified. */ - + // 设置 ASAN 的默认选项 setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" "symbolize=0:" @@ -2377,11 +2375,11 @@ static u8 run_target(char** argv, u32 timeout) { "symbolize=0:" "msan_track_origins=0", 0); - execv(target_path, argv); + execv(target_path, argv); // 执行目标程序 /* Use a distinctive bitmap value to tell the parent about execv() falling through. */ - + // 如果 execv 失败,设置一个特殊的位图值 *(u32*)trace_bits = EXEC_FAIL_SIG; exit(0); @@ -2393,37 +2391,37 @@ static u8 run_target(char** argv, u32 timeout) { /* In non-dumb mode, we have the fork server up and running, so simply tell it to have at it, and then read back PID. */ - + // 在非 dumb 模式下,fork server 已经启动,只需发送命令并读取 PID if ((res = write(fsrv_ctl_fd, &prev_timed_out, 4)) != 4) { - if (stop_soon) return 0; - RPFATAL(res, "Unable to request new process from fork server (OOM?)"); + if (stop_soon) return 0; // 如果停止标志已设置,直接返回 + RPFATAL(res, "Unable to request new process from fork server (OOM?)"); // 如果写入失败,报错并退出 } if ((res = read(fsrv_st_fd, &child_pid, 4)) != 4) { - if (stop_soon) return 0; - RPFATAL(res, "Unable to request new process from fork server (OOM?)"); + if (stop_soon) return 0; // 如果停止标志已设置,直接返回 + RPFATAL(res, "Unable to request new process from fork server (OOM?)"); // 如果读取失败,报错并退出 } - if (child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)"); + if (child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)"); // 如果 PID 无效,报错并退出 } /* Configure timeout, as requested by user, then wait for child to terminate. */ - + // 配置超时,然后等待子进程终止 it.it_value.tv_sec = (timeout / 1000); it.it_value.tv_usec = (timeout % 1000) * 1000; - setitimer(ITIMER_REAL, &it, NULL); + setitimer(ITIMER_REAL, &it, NULL); // 设置定时器 /* The SIGALRM handler simply kills the child_pid and sets child_timed_out. */ - + // SIGALRM 处理程序会杀死子进程并设置 child_timed_out if (dumb_mode == 1 || no_forkserver) { - if (waitpid(child_pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + if (waitpid(child_pid, &status, 0) <= 0) PFATAL("waitpid() failed"); // 如果 waitpid 失败,报错并退出 } else { @@ -2431,14 +2429,14 @@ static u8 run_target(char** argv, u32 timeout) { if ((res = read(fsrv_st_fd, &status, 4)) != 4) { - if (stop_soon) return 0; - RPFATAL(res, "Unable to communicate with fork server (OOM?)"); + if (stop_soon) return 0; // 如果停止标志已设置,直接返回 + RPFATAL(res, "Unable to communicate with fork server (OOM?)"); // 如果读取失败,报错并退出 } } - if (!WIFSTOPPED(status)) child_pid = 0; + if (!WIFSTOPPED(status)) child_pid = 0; // 如果子进程未停止,重置 child_pid getitimer(ITIMER_REAL, &it); exec_ms = (u64) timeout - (it.it_value.tv_sec * 1000 + @@ -2447,56 +2445,57 @@ static u8 run_target(char** argv, u32 timeout) { it.it_value.tv_sec = 0; it.it_value.tv_usec = 0; - setitimer(ITIMER_REAL, &it, NULL); + setitimer(ITIMER_REAL, &it, NULL); // 清除定时器 - total_execs++; + total_execs++; // 增加总执行次数 /* Any subsequent operations on trace_bits must not be moved by the compiler below this point. Past this location, trace_bits[] behave very normally and do not have to be treated as volatile. */ - + // 在此之后,trace_bits[] 的行为正常,不需要视为易变 MEM_BARRIER(); tb4 = *(u32*)trace_bits; #ifdef WORD_SIZE_64 - classify_counts((u64*)trace_bits); + classify_counts((u64*)trace_bits); // 对 64 位 trace_bits 进行分类 #else - classify_counts((u32*)trace_bits); + classify_counts((u32*)trace_bits); // 对 32 位 trace_bits 进行分类 #endif /* ^WORD_SIZE_64 */ prev_timed_out = child_timed_out; /* Report outcome to caller. */ - + // 向调用者报告结果 if (WIFSIGNALED(status) && !stop_soon) { - kill_signal = WTERMSIG(status); + kill_signal = WTERMSIG(status); // 获取终止信号 - if (child_timed_out && kill_signal == SIGKILL) return FAULT_TMOUT; + if (child_timed_out && kill_signal == SIGKILL) return FAULT_TMOUT; // 如果超时且信号为 SIGKILL,返回超时错误 - return FAULT_CRASH; + return FAULT_CRASH; // 返回崩溃错误 } /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and must use a special exit code. */ - + // 针对 MSAN 的特殊处理 if (uses_asan && WEXITSTATUS(status) == MSAN_ERROR) { kill_signal = 0; - return FAULT_CRASH; + return FAULT_CRASH; // 返回崩溃错误 } if ((dumb_mode == 1 || no_forkserver) && tb4 == EXEC_FAIL_SIG) - return FAULT_ERROR; + return FAULT_ERROR; // 如果 execv 失败,返回错误 /* It makes sense to account for the slowest units only if the testcase was run under the user defined timeout. */ + // 只有在用户定义的超时内运行测试用例时,才记录最慢的执行时间 if (!(timeout > exec_tmout) && (slowest_exec_ms < exec_ms)) { slowest_exec_ms = exec_ms; } - return FAULT_NONE; + return FAULT_NONE; // 返回无错误 } @@ -2509,24 +2508,24 @@ static void write_to_testcase(void* mem, u32 len) { s32 fd = out_fd; - if (out_file) { + if (out_file) { // 如果指定了输出文件 - unlink(out_file); /* Ignore errors. */ + unlink(out_file); /* Ignore errors. */ // 删除旧文件 - fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); + fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建新文件 - if (fd < 0) PFATAL("Unable to create '%s'", out_file); + if (fd < 0) PFATAL("Unable to create '%s'", out_file); // 如果创建失败,报错并退出 - } else lseek(fd, 0, SEEK_SET); + } else lseek(fd, 0, SEEK_SET); // 否则重定位文件指针 - ck_write(fd, mem, len, out_file); + ck_write(fd, mem, len, out_file); // 写入数据 if (!out_file) { - if (ftruncate(fd, len)) PFATAL("ftruncate() failed"); - lseek(fd, 0, SEEK_SET); + if (ftruncate(fd, len)) PFATAL("ftruncate() failed"); // 如果截断失败,报错并退出 + lseek(fd, 0, SEEK_SET); // 重定位文件指针 - } else close(fd); + } else close(fd); // 关闭文件 } @@ -2538,26 +2537,26 @@ static void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) { s32 fd = out_fd; u32 tail_len = len - skip_at - skip_len; - if (out_file) { + if (out_file) { // 如果指定了输出文件 - unlink(out_file); /* Ignore errors. */ + unlink(out_file); /* Ignore errors. */ // 删除旧文件 - fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); + fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建新文件 - if (fd < 0) PFATAL("Unable to create '%s'", out_file); + if (fd < 0) PFATAL("Unable to create '%s'", out_file); // 如果创建失败,报错并退出 - } else lseek(fd, 0, SEEK_SET); + } else lseek(fd, 0, SEEK_SET); // 否则重定位文件指针 - if (skip_at) ck_write(fd, mem, skip_at, out_file); + if (skip_at) ck_write(fd, mem, skip_at, out_file); // 写入跳过前的数据 - if (tail_len) ck_write(fd, mem + skip_at + skip_len, tail_len, out_file); + if (tail_len) ck_write(fd, mem + skip_at + skip_len, tail_len, out_file); // 写入跳过后剩余的数据 if (!out_file) { - if (ftruncate(fd, len - skip_len)) PFATAL("ftruncate() failed"); - lseek(fd, 0, SEEK_SET); + if (ftruncate(fd, len - skip_len)) PFATAL("ftruncate() failed"); // 如果截断失败,报错并退出 + lseek(fd, 0, SEEK_SET); // 重定位文件指针 - } else close(fd); + } else close(fd); // 关闭文件 } @@ -2585,7 +2584,7 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, /* Be a bit more generous about timeouts when resuming sessions, or when trying to calibrate already-added finds. This helps avoid trouble due to intermittent latency. */ - + // 在恢复会话或校准已添加的测试用例时,放宽超时限制 if (!from_queue || resuming_fuzz) use_tmout = MAX(exec_tmout + CAL_TMOUT_ADD, exec_tmout * CAL_TMOUT_PERC / 100); @@ -2597,33 +2596,33 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, /* Make sure the forkserver is up before we do anything, and let's not count its spin-up time toward binary calibration. */ - + // 确保 fork server 已启动,并且不将其启动时间计入校准时间 if (dumb_mode != 1 && !no_forkserver && !forksrv_pid) init_forkserver(argv); if (q->exec_cksum) { - memcpy(first_trace, trace_bits, MAP_SIZE); - hnb = has_new_bits(virgin_bits); + memcpy(first_trace, trace_bits, MAP_SIZE); // 复制 trace_bits + hnb = has_new_bits(virgin_bits); // 检查是否有新位 if (hnb > new_bits) new_bits = hnb; } - start_us = get_cur_time_us(); + start_us = get_cur_time_us(); // 获取当前时间 for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { u32 cksum; - if (!first_run && !(stage_cur % stats_update_freq)) show_stats(); + if (!first_run && !(stage_cur % stats_update_freq)) show_stats(); // 显示统计信息 - write_to_testcase(use_mem, q->len); + write_to_testcase(use_mem, q->len); // 写入测试用例 - fault = run_target(argv, use_tmout); + fault = run_target(argv, use_tmout); // 运行目标程序 /* stop_soon is set by the handler for Ctrl+C. When it's pressed, we want to bail out quickly. */ - + // 如果按下 Ctrl+C,快速退出 if (stop_soon || fault != crash_mode) goto abort_calibration; if (!dumb_mode && !stage_cur && !count_bytes(trace_bits)) { @@ -2631,11 +2630,11 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, goto abort_calibration; } - cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算 trace_bits 的校验和 - if (q->exec_cksum != cksum) { + if (q->exec_cksum != cksum) { // 如果校验和不匹配 - hnb = has_new_bits(virgin_bits); + hnb = has_new_bits(virgin_bits); // 检查是否有新位 if (hnb > new_bits) new_bits = hnb; if (q->exec_cksum) { @@ -2644,7 +2643,7 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, for (i = 0; i < MAP_SIZE; i++) { - if (!var_bytes[i] && first_trace[i] != trace_bits[i]) { + if (!var_bytes[i] && first_trace[i] != trace_bits[i]) { // 如果发现可变字节 var_bytes[i] = 1; stage_max = CAL_CYCLES_LONG; @@ -2657,8 +2656,8 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, } else { - q->exec_cksum = cksum; - memcpy(first_trace, trace_bits, MAP_SIZE); + q->exec_cksum = cksum; // 更新校验和 + memcpy(first_trace, trace_bits, MAP_SIZE); // 复制 trace_bits } @@ -2666,14 +2665,14 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, } - stop_us = get_cur_time_us(); + stop_us = get_cur_time_us(); // 获取当前时间 - total_cal_us += stop_us - start_us; - total_cal_cycles += stage_max; + total_cal_us += stop_us - start_us; // 增加总校准时间 + total_cal_cycles += stage_max; // 增加总校准周期 /* OK, let's collect some stats about the performance of this test case. This is used for fuzzing air time calculations in calculate_score(). */ - + // 收集测试用例的性能统计信息,用于计算 fuzzing 时间 q->exec_us = (stop_us - start_us) / stage_max; q->bitmap_size = count_bytes(trace_bits); q->handicap = handicap; @@ -2682,12 +2681,12 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, total_bitmap_size += q->bitmap_size; total_bitmap_entries++; - update_bitmap_score(q); + update_bitmap_score(q); // 更新位图分数 /* If this case didn't result in new output from the instrumentation, tell parent. This is a non-critical problem, but something to warn the user about. */ - + // 如果测试用例没有产生新的输出,警告用户 if (!dumb_mode && first_run && !fault && !new_bits) fault = FAULT_NOBITS; abort_calibration: @@ -2698,13 +2697,13 @@ abort_calibration: } /* Mark variable paths. */ - + // 标记可变路径 if (var_detected) { - var_byte_count = count_bytes(var_bytes); + var_byte_count = count_bytes(var_bytes); // 计算可变字节数 if (!q->var_behavior) { - mark_as_variable(q); + mark_as_variable(q); // 标记为可变行为 queued_variable++; } @@ -2714,7 +2713,7 @@ abort_calibration: stage_cur = old_sc; stage_max = old_sm; - if (!first_run) show_stats(); + if (!first_run) show_stats(); // 显示统计信息 return fault; @@ -2727,12 +2726,12 @@ static void check_map_coverage(void) { u32 i; - if (count_bytes(trace_bits) < 100) return; + if (count_bytes(trace_bits) < 100) return; // 如果覆盖的字节数小于 100,直接返回 for (i = (1 << (MAP_SIZE_POW2 - 1)); i < MAP_SIZE; i++) - if (trace_bits[i]) return; + if (trace_bits[i]) return; // 如果高位有覆盖,直接返回 - WARNF("Recompile binary with newer version of afl to improve coverage!"); + WARNF("Recompile binary with newer version of afl to improve coverage!"); // 警告用户重新编译二进制文件以提高覆盖率 } @@ -2754,22 +2753,22 @@ static void perform_dry_run(char** argv) { u8* fn = strrchr(q->fname, '/') + 1; - ACTF("Attempting dry run with '%s'...", fn); + ACTF("Attempting dry run with '%s'...", fn); // 尝试进行 dry run - fd = open(q->fname, O_RDONLY); - if (fd < 0) PFATAL("Unable to open '%s'", q->fname); + fd = open(q->fname, O_RDONLY); // 打开文件 + if (fd < 0) PFATAL("Unable to open '%s'", q->fname); // 如果打开失败,报错并退出 - use_mem = ck_alloc_nozero(q->len); + use_mem = ck_alloc_nozero(q->len); // 分配内存 if (read(fd, use_mem, q->len) != q->len) - FATAL("Short read from '%s'", q->fname); + FATAL("Short read from '%s'", q->fname); // 如果读取失败,报错并退出 close(fd); - res = calibrate_case(argv, q, use_mem, 0, 1); + res = calibrate_case(argv, q, use_mem, 0, 1); // 校准测试用例 ck_free(use_mem); - if (stop_soon) return; + if (stop_soon) return; // 如果停止标志已设置,直接返回 if (res == crash_mode || res == FAULT_NOBITS) SAYF(cGRA " len = %u, map size = %u, exec speed = %llu us\n" cRST, @@ -2779,9 +2778,9 @@ static void perform_dry_run(char** argv) { case FAULT_NONE: - if (q == queue) check_map_coverage(); + if (q == queue) check_map_coverage(); // 检查覆盖率 - if (crash_mode) FATAL("Test case '%s' does *NOT* crash", fn); + if (crash_mode) FATAL("Test case '%s' does *NOT* crash", fn); // 如果测试用例未崩溃,报错并退出 break; @@ -2792,7 +2791,7 @@ static void perform_dry_run(char** argv) { /* The -t nn+ syntax in the command line sets timeout_given to '2' and instructs afl-fuzz to tolerate but skip queue entries that time out. */ - + // 如果命令行中指定了 -t nn+,跳过超时的测试用例 if (timeout_given > 1) { WARNF("Test case results in a timeout (skipping)"); q->cal_failed = CAL_CHANCES; @@ -2808,7 +2807,7 @@ static void perform_dry_run(char** argv) { " '+' at the end of the value passed to -t ('-t %u+').\n", exec_tmout, exec_tmout); - FATAL("Test case '%s' results in a timeout", fn); + FATAL("Test case '%s' results in a timeout", fn); // 报错并退出 } else { @@ -2820,13 +2819,13 @@ static void perform_dry_run(char** argv) { " If this test case is just a fluke, the other option is to just avoid it\n" " altogether, and find one that is less of a CPU hog.\n", exec_tmout); - FATAL("Test case '%s' results in a timeout", fn); + FATAL("Test case '%s' results in a timeout", fn); // 报错并退出 } case FAULT_CRASH: - if (crash_mode) break; + if (crash_mode) break; // 如果处于崩溃模式,直接跳过 if (skip_crashes) { WARNF("Test case results in a crash (skipping)"); @@ -2895,28 +2894,28 @@ static void perform_dry_run(char** argv) { } - FATAL("Test case '%s' results in a crash", fn); + FATAL("Test case '%s' results in a crash", fn); // 报错并退出 case FAULT_ERROR: - FATAL("Unable to execute target application ('%s')", argv[0]); + FATAL("Unable to execute target application ('%s')", argv[0]); // 报错并退出 case FAULT_NOINST: - FATAL("No instrumentation detected"); + FATAL("No instrumentation detected"); // 报错并退出 case FAULT_NOBITS: useless_at_start++; if (!in_bitmap && !shuffle_queue) - WARNF("No new instrumentation output, test case may be useless."); + WARNF("No new instrumentation output, test case may be useless."); // 警告用户测试用例可能无用 break; } - if (q->var_behavior) WARNF("Instrumentation output varies across runs."); + if (q->var_behavior) WARNF("Instrumentation output varies across runs."); // 警告用户输出在不同运行中变化 q = q->next; @@ -2926,48 +2925,47 @@ static void perform_dry_run(char** argv) { if (cal_failures == queued_paths) FATAL("All test cases time out%s, giving up!", - skip_crashes ? " or crash" : ""); + skip_crashes ? " or crash" : ""); // 如果所有测试用例都超时或崩溃,报错并退出 WARNF("Skipped %u test cases (%0.02f%%) due to timeouts%s.", cal_failures, ((double)cal_failures) * 100 / queued_paths, - skip_crashes ? " or crashes" : ""); + skip_crashes ? " or crashes" : ""); // 警告用户跳过的测试用例 if (cal_failures * 5 > queued_paths) - WARNF(cLRD "High percentage of rejected test cases, check settings!"); + WARNF(cLRD "High percentage of rejected test cases, check settings!"); // 警告用户高比例的测试用例被拒绝 } - OKF("All test cases processed."); + OKF("All test cases processed."); // 所有测试用例处理完成 } - /* Helper function: link() if possible, copy otherwise. */ static void link_or_copy(u8* old_path, u8* new_path) { - s32 i = link(old_path, new_path); + s32 i = link(old_path, new_path); // 尝试创建硬链接 s32 sfd, dfd; u8* tmp; - if (!i) return; + if (!i) return; // 如果硬链接创建成功,直接返回 - sfd = open(old_path, O_RDONLY); - if (sfd < 0) PFATAL("Unable to open '%s'", old_path); + sfd = open(old_path, O_RDONLY); // 打开源文件 + if (sfd < 0) PFATAL("Unable to open '%s'", old_path); // 如果打开失败,报错并退出 - dfd = open(new_path, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (dfd < 0) PFATAL("Unable to create '%s'", new_path); + dfd = open(new_path, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建目标文件 + if (dfd < 0) PFATAL("Unable to create '%s'", new_path); // 如果创建失败,报错并退出 - tmp = ck_alloc(64 * 1024); + tmp = ck_alloc(64 * 1024); // 分配缓冲区 while ((i = read(sfd, tmp, 64 * 1024)) > 0) - ck_write(dfd, tmp, i, new_path); + ck_write(dfd, tmp, i, new_path); // 复制文件内容 - if (i < 0) PFATAL("read() failed"); + if (i < 0) PFATAL("read() failed"); // 如果读取失败,报错并退出 - ck_free(tmp); - close(sfd); - close(dfd); + ck_free(tmp); // 释放缓冲区 + close(sfd); // 关闭源文件 + close(dfd); // 关闭目标文件 } @@ -2982,7 +2980,7 @@ static void pivot_inputs(void) { struct queue_entry* q = queue; u32 id = 0; - ACTF("Creating hard links for all input files..."); + ACTF("Creating hard links for all input files..."); // 为所有输入文件创建硬链接 while (q) { @@ -2994,7 +2992,7 @@ static void pivot_inputs(void) { /* If the original file name conforms to the syntax and the recorded ID matches the one we'd assign, just use the original file name. This is valuable for resuming fuzzing runs. */ - + // 如果原始文件名符合语法且记录的 ID 与我们将分配的 ID 匹配,直接使用原始文件名 #ifndef SIMPLE_FILES # define CASE_PREFIX "id:" #else @@ -3002,7 +3000,7 @@ static void pivot_inputs(void) { #endif /* ^!SIMPLE_FILES */ if (!strncmp(rsl, CASE_PREFIX, 3) && - sscanf(rsl + 3, "%06u", &orig_id) == 1 && orig_id == id) { + sscanf(rsl + 3, "%u", &orig_id) == 1 && orig_id == id) { u8* src_str; u32 src_id; @@ -3012,10 +3010,10 @@ static void pivot_inputs(void) { /* Since we're at it, let's also try to find parent and figure out the appropriate depth for this entry. */ - + // 尝试找到父路径并计算适当的深度 src_str = strchr(rsl + 3, ':'); - if (src_str && sscanf(src_str + 1, "%06u", &src_id) == 1) { + if (src_str && sscanf(src_str + 1, "%u", &src_id) == 1) { struct queue_entry* s = queue; while (src_id-- && s) s = s->next; @@ -3029,7 +3027,7 @@ static void pivot_inputs(void) { /* No dice - invent a new name, capturing the original one as a substring. */ - + // 否则,生成一个新名称,包含原始名称作为子字符串 #ifndef SIMPLE_FILES u8* use_name = strstr(rsl, ",orig:"); @@ -3046,21 +3044,21 @@ static void pivot_inputs(void) { } /* Pivot to the new queue entry. */ - - link_or_copy(q->fname, nfn); - ck_free(q->fname); - q->fname = nfn; + // 切换到新的队列条目 + link_or_copy(q->fname, nfn); // 创建硬链接或复制文件 + ck_free(q->fname); // 释放旧文件名 + q->fname = nfn; // 更新文件名 /* Make sure that the passed_det value carries over, too. */ - - if (q->passed_det) mark_as_det_done(q); + // 确保 passed_det 值也传递 + if (q->passed_det) mark_as_det_done(q); // 标记为已完成确定性测试 q = q->next; id++; } - if (in_place_resume) nuke_resume_dir(); + if (in_place_resume) nuke_resume_dir(); // 如果启用原地恢复,清除恢复目录 } @@ -3074,33 +3072,33 @@ static u8* describe_op(u8 hnb) { static u8 ret[256]; - if (syncing_party) { + if (syncing_party) { // 如果正在同步 - sprintf(ret, "sync:%s,src:%06u", syncing_party, syncing_case); + sprintf(ret, "sync:%s,src:%06u", syncing_party, syncing_case); // 生成同步文件名 } else { - sprintf(ret, "src:%06u", current_entry); + sprintf(ret, "src:%06u", current_entry); // 生成源文件名 if (splicing_with >= 0) - sprintf(ret + strlen(ret), "+%06u", splicing_with); + sprintf(ret + strlen(ret), "+%06u", splicing_with); // 添加拼接信息 - sprintf(ret + strlen(ret), ",op:%s", stage_short); + sprintf(ret + strlen(ret), ",op:%s", stage_short); // 添加操作信息 if (stage_cur_byte >= 0) { - sprintf(ret + strlen(ret), ",pos:%u", stage_cur_byte); + sprintf(ret + strlen(ret), ",pos:%u", stage_cur_byte); // 添加位置信息 if (stage_val_type != STAGE_VAL_NONE) sprintf(ret + strlen(ret), ",val:%s%+d", (stage_val_type == STAGE_VAL_BE) ? "be:" : "", - stage_cur_val); + stage_cur_val); // 添加值信息 - } else sprintf(ret + strlen(ret), ",rep:%u", stage_cur_val); + } else sprintf(ret + strlen(ret), ",rep:%u", stage_cur_val); // 添加重复信息 } - if (hnb == 2) strcat(ret, ",+cov"); + if (hnb == 2) strcat(ret, ",+cov"); // 如果有新覆盖率,添加标记 return ret; @@ -3113,18 +3111,18 @@ static u8* describe_op(u8 hnb) { static void write_crash_readme(void) { - u8* fn = alloc_printf("%s/crashes/README.txt", out_dir); + u8* fn = alloc_printf("%s/crashes/README.txt", out_dir); // 生成 README 文件路径 s32 fd; FILE* f; - fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); + fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建文件 ck_free(fn); /* Do not die on errors here - that would be impolite. */ - + // 如果出错,不退出 if (fd < 0) return; - f = fdopen(fd, "w"); + f = fdopen(fd, "w"); // 打开文件 if (!f) { close(fd); @@ -3149,9 +3147,9 @@ static void write_crash_readme(void) { "Thanks :-)\n", - orig_cmdline, DMS(mem_limit << 20)); /* ignore errors */ + orig_cmdline, DMS(mem_limit << 20)); /* ignore errors */ // 写入信息 - fclose(f); + fclose(f); // 关闭文件 } @@ -3171,7 +3169,7 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) { /* Keep only if there are new bits in the map, add to queue for future fuzzing, etc. */ - + // 如果位图中有新位,保留并添加到队列中 if (!(hnb = has_new_bits(virgin_bits))) { if (crash_mode) total_crashes++; return 0; @@ -3180,34 +3178,34 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) { #ifndef SIMPLE_FILES fn = alloc_printf("%s/queue/id:%06u,%s", out_dir, queued_paths, - describe_op(hnb)); + describe_op(hnb)); // 生成文件名 #else - fn = alloc_printf("%s/queue/id_%06u", out_dir, queued_paths); + fn = alloc_printf("%s/queue/id_%06u", out_dir, queued_paths); // 生成文件名 #endif /* ^!SIMPLE_FILES */ - add_to_queue(fn, len, 0); + add_to_queue(fn, len, 0); // 添加到队列 if (hnb == 2) { queue_top->has_new_cov = 1; queued_with_cov++; } - queue_top->exec_cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + queue_top->exec_cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算校验和 /* Try to calibrate inline; this also calls update_bitmap_score() when successful. */ - + // 尝试内联校准 res = calibrate_case(argv, queue_top, mem, queue_cycle - 1, 0); if (res == FAULT_ERROR) - FATAL("Unable to execute target application"); + FATAL("Unable to execute target application"); // 如果执行失败,报错并退出 - fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", fn); - ck_write(fd, mem, len, fn); + fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建文件 + if (fd < 0) PFATAL("Unable to create '%s'", fn); // 如果创建失败,报错并退出 + ck_write(fd, mem, len, fn); // 写入数据 close(fd); keeping = 1; @@ -3222,20 +3220,20 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) { a handful of samples. We use the presence of new bits in the hang-specific bitmap as a signal of uniqueness. In "dumb" mode, we just keep everything. */ - + // 超时不太有趣,但仍需保留一些样本 total_tmouts++; - if (unique_hangs >= KEEP_UNIQUE_HANG) return keeping; + if (unique_hangs >= KEEP_UNIQUE_HANG) return keeping; // 如果唯一挂起数超过限制,直接返回 if (!dumb_mode) { #ifdef WORD_SIZE_64 - simplify_trace((u64*)trace_bits); + simplify_trace((u64*)trace_bits); // 简化 trace_bits #else - simplify_trace((u32*)trace_bits); + simplify_trace((u32*)trace_bits); // 简化 trace_bits #endif /* ^WORD_SIZE_64 */ - if (!has_new_bits(virgin_tmout)) return keeping; + if (!has_new_bits(virgin_tmout)) return keeping; // 如果没有新位,直接返回 } @@ -3244,17 +3242,17 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) { /* Before saving, we make sure that it's a genuine hang by re-running the target with a more generous timeout (unless the default timeout is already generous). */ - + // 在保存之前,重新运行目标程序以确保是真正的挂起 if (exec_tmout < hang_tmout) { u8 new_fault; - write_to_testcase(mem, len); - new_fault = run_target(argv, hang_tmout); + write_to_testcase(mem, len); // 写入测试用例 + new_fault = run_target(argv, hang_tmout); // 运行目标程序 /* A corner case that one user reported bumping into: increasing the timeout actually uncovers a crash. Make sure we don't discard it if so. */ - + // 如果增加超时时间后发现崩溃,确保不丢弃 if (!stop_soon && new_fault == FAULT_CRASH) goto keep_as_crash; if (stop_soon || new_fault != FAULT_TMOUT) return keeping; @@ -3264,18 +3262,18 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) { #ifndef SIMPLE_FILES fn = alloc_printf("%s/hangs/id:%06llu,%s", out_dir, - unique_hangs, describe_op(0)); + unique_hangs, describe_op(0)); // 生成文件名 #else fn = alloc_printf("%s/hangs/id_%06llu", out_dir, - unique_hangs); + unique_hangs); // 生成文件名 #endif /* ^!SIMPLE_FILES */ unique_hangs++; - last_hang_time = get_cur_time(); + last_hang_time = get_cur_time(); // 记录最后挂起时间 break; @@ -3286,45 +3284,45 @@ keep_as_crash: /* This is handled in a manner roughly similar to timeouts, except for slightly different limits and no need to re-run test cases. */ - + // 处理崩溃,类似于超时处理 total_crashes++; - if (unique_crashes >= KEEP_UNIQUE_CRASH) return keeping; + if (unique_crashes >= KEEP_UNIQUE_CRASH) return keeping; // 如果唯一崩溃数超过限制,直接返回 if (!dumb_mode) { #ifdef WORD_SIZE_64 - simplify_trace((u64*)trace_bits); + simplify_trace((u64*)trace_bits); // 简化 trace_bits #else - simplify_trace((u32*)trace_bits); + simplify_trace((u32*)trace_bits); // 简化 trace_bits #endif /* ^WORD_SIZE_64 */ - if (!has_new_bits(virgin_crash)) return keeping; + if (!has_new_bits(virgin_crash)) return keeping; // 如果没有新位,直接返回 } - if (!unique_crashes) write_crash_readme(); + if (!unique_crashes) write_crash_readme(); // 如果是第一次崩溃,写入 README #ifndef SIMPLE_FILES fn = alloc_printf("%s/crashes/id:%06llu,sig:%02u,%s", out_dir, - unique_crashes, kill_signal, describe_op(0)); + unique_crashes, kill_signal, describe_op(0)); // 生成文件名 #else fn = alloc_printf("%s/crashes/id_%06llu_%02u", out_dir, unique_crashes, - kill_signal); + kill_signal); // 生成文件名 #endif /* ^!SIMPLE_FILES */ unique_crashes++; - last_crash_time = get_cur_time(); - last_crash_execs = total_execs; + last_crash_time = get_cur_time(); // 记录最后崩溃时间 + last_crash_execs = total_execs; // 记录最后崩溃时的执行次数 break; - case FAULT_ERROR: FATAL("Unable to execute target application"); + case FAULT_ERROR: FATAL("Unable to execute target application"); // 报错并退出 default: return keeping; @@ -3332,19 +3330,18 @@ keep_as_crash: /* If we're here, we apparently want to save the crash or hang test case, too. */ - - fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", fn); - ck_write(fd, mem, len, fn); + // 如果需要保存崩溃或挂起测试用例 + fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建文件 + if (fd < 0) PFATAL("Unable to create '%s'", fn); // 如果创建失败,报错并退出 + ck_write(fd, mem, len, fn); // 写入数据 close(fd); - ck_free(fn); + ck_free(fn); // 释放文件名 return keeping; } - /* When resuming, try to find the queue position to start from. This makes sense only when resuming, and when we can find the original fuzzer_stats. */ @@ -3356,25 +3353,25 @@ static u32 find_start_position(void) { s32 fd, i; u32 ret; - if (!resuming_fuzz) return 0; + if (!resuming_fuzz) return 0; // 如果不是恢复模式,直接返回0 - if (in_place_resume) fn = alloc_printf("%s/fuzzer_stats", out_dir); - else fn = alloc_printf("%s/../fuzzer_stats", in_dir); + if (in_place_resume) fn = alloc_printf("%s/fuzzer_stats", out_dir); // 如果是原地恢复,构造fuzzer_stats路径 + else fn = alloc_printf("%s/../fuzzer_stats", in_dir); // 否则,构造输入目录中的fuzzer_stats路径 - fd = open(fn, O_RDONLY); - ck_free(fn); + fd = open(fn, O_RDONLY); // 打开fuzzer_stats文件 + ck_free(fn); // 释放路径字符串 - if (fd < 0) return 0; + if (fd < 0) return 0; // 如果打开失败,返回0 - i = read(fd, tmp, sizeof(tmp) - 1); (void)i; /* Ignore errors */ - close(fd); + i = read(fd, tmp, sizeof(tmp) - 1); (void)i; /* Ignore errors */ // 读取文件内容到tmp缓冲区 + close(fd); // 关闭文件 - off = strstr(tmp, "cur_path : "); - if (!off) return 0; + off = strstr(tmp, "cur_path : "); // 查找cur_path字段 + if (!off) return 0; // 如果找不到,返回0 - ret = atoi(off + 20); - if (ret >= queued_paths) ret = 0; - return ret; + ret = atoi(off + 20); // 将cur_path的值转换为整数 + if (ret >= queued_paths) ret = 0; // 如果ret大于等于队列中的路径数,返回0 + return ret; // 返回找到的起始位置 } @@ -3391,27 +3388,27 @@ static void find_timeout(void) { s32 fd, i; u32 ret; - if (!resuming_fuzz) return; + if (!resuming_fuzz) return; // 如果不是恢复模式,直接返回 - if (in_place_resume) fn = alloc_printf("%s/fuzzer_stats", out_dir); - else fn = alloc_printf("%s/../fuzzer_stats", in_dir); + if (in_place_resume) fn = alloc_printf("%s/fuzzer_stats", out_dir); // 如果是原地恢复,构造fuzzer_stats路径 + else fn = alloc_printf("%s/../fuzzer_stats", in_dir); // 否则,构造输入目录中的fuzzer_stats路径 - fd = open(fn, O_RDONLY); - ck_free(fn); + fd = open(fn, O_RDONLY); // 打开fuzzer_stats文件 + ck_free(fn); // 释放路径字符串 - if (fd < 0) return; + if (fd < 0) return; // 如果打开失败,返回 - i = read(fd, tmp, sizeof(tmp) - 1); (void)i; /* Ignore errors */ - close(fd); + i = read(fd, tmp, sizeof(tmp) - 1); (void)i; /* Ignore errors */ // 读取文件内容到tmp缓冲区 + close(fd); // 关闭文件 - off = strstr(tmp, "exec_timeout : "); - if (!off) return; + off = strstr(tmp, "exec_timeout : "); // 查找exec_timeout字段 + if (!off) return; // 如果找不到,返回 - ret = atoi(off + 20); - if (ret <= 4) return; + ret = atoi(off + 20); // 将exec_timeout的值转换为整数 + if (ret <= 4) return; // 如果ret小于等于4,返回 - exec_tmout = ret; - timeout_given = 3; + exec_tmout = ret; // 设置全局超时变量 + timeout_given = 3; // 标记超时已设置 } @@ -3420,36 +3417,37 @@ static void find_timeout(void) { static void write_stats_file(double bitmap_cvg, double stability, double eps) { - static double last_bcvg, last_stab, last_eps; - static struct rusage usage; + static double last_bcvg, last_stab, last_eps; // 用于保存上一次的覆盖率、稳定性和执行速度 + static struct rusage usage; // 用于获取资源使用情况 - u8* fn = alloc_printf("%s/fuzzer_stats", out_dir); + u8* fn = alloc_printf("%s/fuzzer_stats", out_dir); // 构造fuzzer_stats文件路径 s32 fd; FILE* f; - fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); // 打开文件,准备写入 - if (fd < 0) PFATAL("Unable to create '%s'", fn); + if (fd < 0) PFATAL("Unable to create '%s'", fn); // 如果打开失败,报错 - ck_free(fn); + ck_free(fn); // 释放路径字符串 - f = fdopen(fd, "w"); + f = fdopen(fd, "w"); // 将文件描述符转换为文件指针 - if (!f) PFATAL("fdopen() failed"); + if (!f) PFATAL("fdopen() failed"); // 如果转换失败,报错 /* Keep last values in case we're called from another context where exec/sec stats and such are not readily available. */ - if (!bitmap_cvg && !stability && !eps) { - bitmap_cvg = last_bcvg; - stability = last_stab; - eps = last_eps; - } else { - last_bcvg = bitmap_cvg; - last_stab = stability; - last_eps = eps; + if (!bitmap_cvg && !stability && !eps) { // 如果没有传入新的覆盖率、稳定性和执行速度 + bitmap_cvg = last_bcvg; // 使用上一次的覆盖率 + stability = last_stab; // 使用上一次的稳定性 + eps = last_eps; // 使用上一次的执行速度 + } else { // 否则 + last_bcvg = bitmap_cvg; // 保存当前的覆盖率 + last_stab = stability; // 保存当前的稳定性 + last_eps = eps; // 保存当前的执行速度 } + // 将统计信息写入文件 fprintf(f, "start_time : %llu\n" "last_update : %llu\n" "fuzzer_pid : %u\n" @@ -3498,19 +3496,19 @@ static void write_stats_file(double bitmap_cvg, double stability, double eps) { /* Get rss value from the children We must have killed the forkserver process and called waitpid before calling getrusage */ - if (getrusage(RUSAGE_CHILDREN, &usage)) { - WARNF("getrusage failed"); - } else if (usage.ru_maxrss == 0) { - fprintf(f, "peak_rss_mb : not available while afl is running\n"); + if (getrusage(RUSAGE_CHILDREN, &usage)) { // 获取子进程的资源使用情况 + WARNF("getrusage failed"); // 如果获取失败,发出警告 + } else if (usage.ru_maxrss == 0) { // 如果最大常驻集大小为0 + fprintf(f, "peak_rss_mb : not available while afl is running\n"); // 写入不可用信息 } else { #ifdef __APPLE__ - fprintf(f, "peak_rss_mb : %zu\n", usage.ru_maxrss >> 20); + fprintf(f, "peak_rss_mb : %zu\n", usage.ru_maxrss >> 20); // 在macOS上,最大常驻集大小以字节为单位,转换为MB #else - fprintf(f, "peak_rss_mb : %zu\n", usage.ru_maxrss >> 10); + fprintf(f, "peak_rss_mb : %zu\n", usage.ru_maxrss >> 10); // 在其他系统上,最大常驻集大小以KB为单位,转换为MB #endif /* ^__APPLE__ */ } - fclose(f); + fclose(f); // 关闭文件 } @@ -3519,22 +3517,22 @@ 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; + 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_uh == unique_hangs && prev_md == max_depth) return; // 如果没有变化,直接返回 - 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; + 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; // 更新最大深度 /* Fields in the file: @@ -3546,14 +3544,11 @@ static void maybe_update_plot_file(double bitmap_cvg, double eps) { "%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); /* ignore errors */ // 将数据写入plot文件 - fflush(plot_file); + fflush(plot_file); // 刷新文件缓冲区 } - - - /* A helper function for maybe_delete_out_dir(), deleting all prefixed files in a directory. */ @@ -3562,26 +3557,26 @@ static u8 delete_files(u8* path, u8* prefix) { DIR* d; struct dirent* d_ent; - d = opendir(path); + d = opendir(path); // 打开指定路径的目录 - if (!d) return 0; + if (!d) return 0; // 如果打开失败,返回0 - while ((d_ent = readdir(d))) { + while ((d_ent = readdir(d))) { // 遍历目录中的每个文件 if (d_ent->d_name[0] != '.' && (!prefix || - !strncmp(d_ent->d_name, prefix, strlen(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); + 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); + closedir(d); // 关闭目录 - return !!rmdir(path); + return !!rmdir(path); // 删除目录并返回结果 } @@ -3590,7 +3585,7 @@ static u8 delete_files(u8* path, u8* prefix) { static double get_runnable_processes(void) { - static double res; + static double res; // 保存结果 #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) @@ -3598,7 +3593,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分钟的系统负载平均值 #else @@ -3606,35 +3601,35 @@ 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"); + 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); + !strncmp(tmp, "procs_blocked ", 14)) val += atoi(tmp + 14); // 查找runnable和blocked进程数 } - fclose(f); + fclose(f); // 关闭文件 - if (!res) { + if (!res) { // 如果res未初始化 - res = val; + res = val; // 直接赋值 - } else { + } else { // 否则 res = res * (1.0 - 1.0 / AVG_SMOOTHING) + - ((double)val) * (1.0 / AVG_SMOOTHING); + ((double)val) * (1.0 / AVG_SMOOTHING); // 使用平滑算法更新res } #endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */ - return res; + return res; // 返回结果 } @@ -3645,35 +3640,35 @@ static void nuke_resume_dir(void) { 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"); // 报错并退出 } @@ -3684,46 +3679,46 @@ dir_cleanup_failed: static void maybe_delete_out_dir(void) { FILE* f; - u8 *fn = alloc_printf("%s/fuzzer_stats", out_dir); + u8 *fn = alloc_printf("%s/fuzzer_stats", out_dir); // 构造fuzzer_stats文件路径 /* 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) { + if (flock(out_dir_fd, LOCK_EX | LOCK_NB) && errno == 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"); // 如果锁定失败,提示目录正在被使用 - FATAL("Directory '%s' is in use", out_dir); + FATAL("Directory '%s' is in use", out_dir); // 报错并退出 } #endif /* !__sun */ - f = fopen(fn, "r"); + f = fopen(fn, "r"); // 打开fuzzer_stats文件 - if (f) { + if (f) { // 如果文件存在 u64 start_time, last_update; if (fscanf(f, "start_time : %llu\n" - "last_update : %llu\n", &start_time, &last_update) != 2) - FATAL("Malformed data in '%s'", fn); + "last_update : %llu\n", &start_time, &last_update) != 2) // 读取start_time和last_update + FATAL("Malformed data in '%s'", fn); // 如果读取失败,报错 - fclose(f); + fclose(f); // 关闭文件 /* 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 "The job output directory already exists and contains the results of more\n" @@ -3733,15 +3728,15 @@ static void maybe_delete_out_dir(void) { " If you wish to start a new session, remove or rename the directory manually,\n" " or specify a different output location for this job. To resume the old\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); // 释放路径字符串 /* 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/ @@ -3749,166 +3744,166 @@ static void maybe_delete_out_dir(void) { incomplete due to an earlier abort, so we want to use the old _resume/ 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); // 构造queue路径 - in_dir = alloc_printf("%s/_resume", out_dir); + in_dir = alloc_printf("%s/_resume", out_dir); // 构造_resume路径 - rename(orig_q, in_dir); /* Ignore errors */ + rename(orig_q, in_dir); /* Ignore errors */ // 将queue目录重命名为_resume - OKF("Output directory exists, will attempt session resume."); + OKF("Output directory exists, will attempt session resume."); // 提示用户正在恢复会话 - ck_free(orig_q); + ck_free(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. */ - 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); // 构造.synced路径 + if (delete_files(fn, NULL)) goto dir_cleanup_failed; // 删除.synced目录中的文件 + ck_free(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; - ck_free(fn); + fn = alloc_printf("%s/queue/.state/deterministic_done", out_dir); // 构造路径 + 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; - ck_free(fn); + fn = alloc_printf("%s/queue/.state/auto_extras", out_dir); // 构造路径 + 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; - ck_free(fn); + fn = alloc_printf("%s/queue/.state/redundant_edges", out_dir); // 构造路径 + 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; - ck_free(fn); + fn = alloc_printf("%s/queue/.state/variable_behavior", out_dir); // 构造路径 + 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:*. */ - fn = alloc_printf("%s/queue/.state", out_dir); - if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed; - ck_free(fn); + fn = alloc_printf("%s/queue/.state", out_dir); // 构造路径 + 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; - ck_free(fn); + fn = alloc_printf("%s/queue", out_dir); // 构造queue路径 + if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除queue目录中的文件 + ck_free(fn); // 释放路径字符串 /* All right, let's do /crashes/id:* and /hangs/id:*. */ - if (!in_place_resume) { + if (!in_place_resume) { // 如果不是原地恢复 - fn = alloc_printf("%s/crashes/README.txt", out_dir); - unlink(fn); /* Ignore errors */ - ck_free(fn); + fn = alloc_printf("%s/crashes/README.txt", out_dir); // 构造README.txt路径 + unlink(fn); /* Ignore errors */ // 删除README.txt文件 + ck_free(fn); // 释放路径字符串 } - fn = alloc_printf("%s/crashes", out_dir); + fn = alloc_printf("%s/crashes", out_dir); // 构造crashes路径 /* Make backup of the crashes directory if it's not empty and if we're doing in-place resume. */ - if (in_place_resume && rmdir(fn)) { + if (in_place_resume && rmdir(fn)) { // 如果是原地恢复并且crashes目录不为空 - time_t cur_t = time(0); - struct tm* t = localtime(&cur_t); + time_t cur_t = time(0); // 获取当前时间 + struct tm* t = localtime(&cur_t); // 转换为本地时间 #ifndef SIMPLE_FILES u8* nfn = alloc_printf("%s.%04u-%02u-%02u-%02u:%02u:%02u", fn, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); + t->tm_hour, t->tm_min, t->tm_sec); // 构造备份路径 #else u8* nfn = alloc_printf("%s_%04u%02u%02u%02u%02u%02u", fn, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); + t->tm_hour, t->tm_min, t->tm_sec); // 构造备份路径 #endif /* ^!SIMPLE_FILES */ - rename(fn, nfn); /* Ignore errors. */ - ck_free(nfn); + rename(fn, nfn); /* Ignore errors. */ // 重命名crashes目录 + ck_free(nfn); // 释放路径字符串 } - if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; - ck_free(fn); + if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; // 删除crashes目录中的文件 + ck_free(fn); // 释放路径字符串 - fn = alloc_printf("%s/hangs", out_dir); + fn = alloc_printf("%s/hangs", out_dir); // 构造hangs路径 /* Backup hangs, too. */ - if (in_place_resume && rmdir(fn)) { + if (in_place_resume && rmdir(fn)) { // 如果是原地恢复并且hangs目录不为空 - time_t cur_t = time(0); - struct tm* t = localtime(&cur_t); + time_t cur_t = time(0); // 获取当前时间 + struct tm* t = localtime(&cur_t); // 转换为本地时间 #ifndef SIMPLE_FILES u8* nfn = alloc_printf("%s.%04u-%02u-%02u-%02u:%02u:%02u", fn, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); + t->tm_hour, t->tm_min, t->tm_sec); // 构造备份路径 #else u8* nfn = alloc_printf("%s_%04u%02u%02u%02u%02u%02u", fn, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); + t->tm_hour, t->tm_min, t->tm_sec); // 构造备份路径 #endif /* ^!SIMPLE_FILES */ - rename(fn, nfn); /* Ignore errors. */ - ck_free(nfn); + rename(fn, nfn); /* Ignore errors. */ // 重命名hangs目录 + ck_free(nfn); // 释放路径字符串 } - if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed; - ck_free(fn); + 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; - ck_free(fn); + fn = alloc_printf("%s/.cur_input", out_dir); // 构造.cur_input路径 + if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除.cur_input文件 + ck_free(fn); // 释放路径字符串 - fn = alloc_printf("%s/fuzz_bitmap", out_dir); - if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; - ck_free(fn); + fn = alloc_printf("%s/fuzz_bitmap", out_dir); // 构造fuzz_bitmap路径 + if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除fuzz_bitmap文件 + ck_free(fn); // 释放路径字符串 if (!in_place_resume) { - fn = alloc_printf("%s/fuzzer_stats", out_dir); - if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; - ck_free(fn); + fn = alloc_printf("%s/fuzzer_stats", out_dir); // 构造fuzzer_stats路径 + if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除fuzzer_stats文件 + ck_free(fn); // 释放路径字符串 } - fn = alloc_printf("%s/plot_data", out_dir); - if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; - ck_free(fn); + fn = alloc_printf("%s/plot_data", out_dir); // 构造plot_data路径 + if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; // 删除plot_data文件 + 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 "Whoops, the fuzzer tried to reuse your output directory, but bumped into\n" @@ -3917,149 +3912,166 @@ dir_cleanup_failed: " %s\n\n" " Please examine and manually delete the files, or specify a different\n" - " output location for the tool.\n", fn); + " output location for the tool.\n", fn); // 提示用户手动删除文件 - FATAL("Output directory cleanup failed"); + FATAL("Output directory cleanup failed"); // 报错并退出 } +/* Check terminal size and adjust UI accordingly. */ + +static void check_term_size(void) { + + struct winsize ws; // 用于存储终端窗口大小 -static void check_term_size(void); + if (ioctl(1, TIOCGWINSZ, &ws) == -1) return; // 获取终端窗口大小,如果失败则返回 + + if (ws.ws_row == 0 || ws.ws_col == 0) return; // 如果行数或列数为0,返回 + + /* If the terminal is too small, set a flag and bail out. */ + + if (ws.ws_row < 25 || ws.ws_col < 80) { // 如果终端窗口太小(小于25行或80列) + term_too_small = 1; // 设置标志位 + return; // 返回 + } + + term_too_small = 0; // 否则清除标志位 +} -/* A spiffy retro stats screen! This is called every stats_update_freq +/* Display a retro stats screen. This is called every stats_update_freq execve() calls, plus in several other circumstances. */ 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; + static u64 last_stats_ms, last_plot_ms, last_ms, last_execs; // 用于记录上次统计、绘图、UI更新的时间 + static double avg_exec; // 平均执行速度 + double t_byte_ratio, stab_ratio; // 位图覆盖率和稳定性比率 - u64 cur_ms; - u32 t_bytes, t_bits; + u64 cur_ms; // 当前时间 + u32 t_bytes, t_bits; // 位图中的非255字节数和总位数 - u32 banner_len, banner_pad; - u8 tmp[256]; + u32 banner_len, banner_pad; // 横幅长度和填充 + u8 tmp[256]; // 临时缓冲区 - cur_ms = get_cur_time(); + cur_ms = get_cur_time(); // 获取当前时间 /* If not enough time has passed since last UI update, bail out. */ - if (cur_ms - last_ms < 1000 / UI_TARGET_HZ) return; + if (cur_ms - last_ms < 1000 / UI_TARGET_HZ) return; // 如果距离上次UI更新时间太短,直接返回 /* Check if we're past the 10 minute mark. */ - if (cur_ms - start_time > 10 * 60 * 1000) run_over10m = 1; + if (cur_ms - start_time > 10 * 60 * 1000) run_over10m = 1; // 如果运行时间超过10分钟,设置标志位 /* Calculate smoothed exec speed stats. */ - if (!last_execs) { + if (!last_execs) { // 如果上次执行次数为0 - 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 / - (cur_ms - last_ms); + (cur_ms - last_ms); // 计算当前平均执行速度 /* If there is a dramatic (5x+) jump in speed, reset the indicator more quickly. */ - if (cur_avg * 5 < avg_exec || cur_avg / 5 > avg_exec) - avg_exec = cur_avg; + 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; + last_ms = cur_ms; // 更新上次UI更新时间 + last_execs = total_execs; // 更新上次执行次数 /* Tell the callers when to contact us (as measured in execs). */ - stats_update_freq = avg_exec / (UI_TARGET_HZ * 10); - if (!stats_update_freq) stats_update_freq = 1; + stats_update_freq = avg_exec / (UI_TARGET_HZ * 10); // 计算统计更新频率 + if (!stats_update_freq) stats_update_freq = 1; // 如果频率为0,设置为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; + stab_ratio = 100; // 如果位图为空,稳定性为100% /* Roughly every minute, update fuzzer stats and save auto tokens. */ - if (cur_ms - last_stats_ms > STATS_UPDATE_SEC * 1000) { + if (cur_ms - last_stats_ms > STATS_UPDATE_SEC * 1000) { // 如果距离上次统计更新时间超过1分钟 - last_stats_ms = cur_ms; - write_stats_file(t_byte_ratio, stab_ratio, avg_exec); - save_auto(); - write_bitmap(); + last_stats_ms = cur_ms; // 更新上次统计更新时间 + 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) { + if (cur_ms - last_plot_ms > PLOT_UPDATE_SEC * 1000) { // 如果距离上次绘图更新时间超过PLOT_UPDATE_SEC秒 - last_plot_ms = cur_ms; - maybe_update_plot_file(t_byte_ratio, avg_exec); + last_plot_ms = cur_ms; // 更新上次绘图更新时间 + maybe_update_plot_file(t_byte_ratio, avg_exec); // 更新绘图文件 } /* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */ 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; // 如果没有新发现且设置了AFL_EXIT_WHEN_DONE,准备退出 - if (total_crashes && getenv("AFL_BENCH_UNTIL_CRASH")) stop_soon = 2; + if (total_crashes && getenv("AFL_BENCH_UNTIL_CRASH")) stop_soon = 2; // 如果有崩溃且设置了AFL_BENCH_UNTIL_CRASH,准备退出 /* If we're not on TTY, bail out. */ - if (not_on_tty) return; + if (not_on_tty) return; // 如果不是TTY终端,直接返回 /* Compute some mildly useful bitmap stats. */ - t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits); + t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits); // 计算位图中的总位数 /* Now, for the visuals... */ - if (clear_screen) { + if (clear_screen) { // 如果需要清屏 - SAYF(TERM_CLEAR CURSOR_HIDE); - clear_screen = 0; + SAYF(TERM_CLEAR CURSOR_HIDE); // 清屏并隐藏光标 + clear_screen = 0; // 清除清屏标志 - check_term_size(); + check_term_size(); // 检查终端大小 } - SAYF(TERM_HOME); + SAYF(TERM_HOME); // 移动光标到左上角 - if (term_too_small) { + 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; // 返回 } /* 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); // 计算横幅长度 + banner_pad = (80 - banner_len) / 2; // 计算横幅填充 + memset(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); // 构造横幅内容 - SAYF("\n%s\n\n", tmp); + SAYF("\n%s\n\n", tmp); // 显示横幅 /* "Handy" shortcuts for drawing boxes... */ @@ -4076,81 +4088,81 @@ static void show_stats(void) { /* 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"); + bH bSTOP cCYA " overall results " bSTG bH5 bRT "\n"); // 绘制UI框 - if (dumb_mode) { + if (dumb_mode) { // 如果是dumb模式 - strcpy(tmp, cRST); + strcpy(tmp, cRST); // 使用默认颜色 - } else { + } else { // 否则 - u64 min_wo_finds = (cur_ms - last_path_time) / 1000 / 60; + u64 min_wo_finds = (cur_ms - last_path_time) / 1000 / 60; // 计算距离上次发现新路径的分钟数 /* First queue cycle: don't stop now! */ - if (queue_cycle == 1 || min_wo_finds < 15) strcpy(tmp, cMGN); else + if (queue_cycle == 1 || min_wo_finds < 15) strcpy(tmp, cMGN); else // 如果是第一个队列周期或最近有发现,使用洋红色 /* Subsequent cycles, but we're still making finds. */ - if (cycles_wo_finds < 25 || min_wo_finds < 30) strcpy(tmp, cYEL); else + if (cycles_wo_finds < 25 || min_wo_finds < 30) strcpy(tmp, cYEL); else // 如果最近有发现,使用黄色 /* No finds for a long time and no test cases to try. */ if (cycles_wo_finds > 100 && !pending_not_fuzzed && min_wo_finds > 120) - strcpy(tmp, cLGN); + strcpy(tmp, cLGN); // 如果长时间没有发现且没有待测试用例,使用绿色 /* Default: cautiously OK to stop? */ - else strcpy(tmp, cLBL); + else strcpy(tmp, cLBL); // 否则使用蓝色 } SAYF(bV bSTOP " run time : " cRST "%-34s " bSTG bV bSTOP " cycles done : %s%-5s " bSTG bV "\n", - DTD(cur_ms, start_time), tmp, DI(queue_cycle - 1)); + DTD(cur_ms, start_time), tmp, DI(queue_cycle - 1)); // 显示运行时间和完成的周期数 /* We want to warn people about not seeing new paths after a full cycle, except when resuming fuzzing or running in non-instrumented mode. */ if (!dumb_mode && (last_path_time || resuming_fuzz || queue_cycle == 1 || - in_bitmap || crash_mode)) { + in_bitmap || crash_mode)) { // 如果不是dumb模式且有路径发现时间、恢复模式、第一个周期、在位图中或崩溃模式 SAYF(bV bSTOP " last new path : " cRST "%-34s ", - DTD(cur_ms, last_path_time)); + DTD(cur_ms, last_path_time)); // 显示上次发现新路径的时间 - } else { + } else { // 否则 if (dumb_mode) SAYF(bV bSTOP " last new path : " cPIN "n/a" cRST - " (non-instrumented mode) "); + " (non-instrumented mode) "); // 如果是dumb模式,显示不可用 else SAYF(bV bSTOP " last new path : " cRST "none yet " cLRD - "(odd, check syntax!) "); + "(odd, check syntax!) "); // 否则显示尚未发现新路径 } SAYF(bSTG bV bSTOP " total paths : " cRST "%-5s " bSTG bV "\n", - DI(queued_paths)); + DI(queued_paths)); // 显示总路径数 /* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH limit with a '+' appended to the count. */ sprintf(tmp, "%s%s", DI(unique_crashes), - (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); + (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); // 构造崩溃数显示字符串 SAYF(bV bSTOP " last uniq crash : " cRST "%-34s " bSTG bV bSTOP " uniq crashes : %s%-6s " bSTG bV "\n", DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cRST, - tmp); + tmp); // 显示上次唯一崩溃时间和唯一崩溃数 sprintf(tmp, "%s%s", DI(unique_hangs), - (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); + (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); // 构造挂起数显示字符串 SAYF(bV bSTOP " last uniq hang : " cRST "%-34s " bSTG bV bSTOP " uniq hangs : " cRST "%-6s " bSTG bV "\n", - DTD(cur_ms, last_hang_time), tmp); + DTD(cur_ms, last_hang_time), tmp); // 显示上次唯一挂起时间和唯一挂起数 SAYF(bVR bH bSTOP cCYA " cycle progress " bSTG bH20 bHB bH bSTOP cCYA - " map coverage " bSTG bH bHT bH20 bH2 bH bVL "\n"); + " map coverage " bSTG bH bHT bH20 bH2 bH bVL "\n"); // 绘制UI框 /* This gets funny because we want to print several variable-length variables together, but then cram them into a fixed-width field - so we need to @@ -4158,303 +4170,302 @@ static void show_stats(void) { sprintf(tmp, "%s%s (%0.02f%%)", DI(current_entry), queue_cur->favored ? "" : "*", - ((double)current_entry * 100) / queued_paths); + ((double)current_entry * 100) / queued_paths); // 构造当前处理条目的显示字符串 - SAYF(bV bSTOP " now processing : " cRST "%-17s " bSTG bV bSTOP, tmp); + SAYF(bV bSTOP " now processing : " cRST "%-17s " bSTG bV bSTOP, tmp); // 显示当前处理的条目 sprintf(tmp, "%0.02f%% / %0.02f%%", ((double)queue_cur->bitmap_size) * - 100 / MAP_SIZE, t_byte_ratio); + 100 / MAP_SIZE, t_byte_ratio); // 构造位图密度显示字符串 SAYF(" map density : %s%-21s " bSTG bV "\n", t_byte_ratio > 70 ? cLRD : - ((t_bytes < 200 && !dumb_mode) ? cPIN : cRST), tmp); + ((t_bytes < 200 && !dumb_mode) ? cPIN : cRST), tmp); // 显示位图密度 sprintf(tmp, "%s (%0.02f%%)", DI(cur_skipped_paths), - ((double)cur_skipped_paths * 100) / queued_paths); + ((double)cur_skipped_paths * 100) / queued_paths); // 构造超时路径数显示字符串 - SAYF(bV bSTOP " paths timed out : " cRST "%-17s " bSTG bV, tmp); + SAYF(bV bSTOP " paths timed out : " cRST "%-17s " bSTG bV, tmp); // 显示超时路径数 sprintf(tmp, "%0.02f bits/tuple", - t_bytes ? (((double)t_bits) / t_bytes) : 0); + t_bytes ? (((double)t_bits) / t_bytes) : 0); // 构造覆盖率显示字符串 - SAYF(bSTOP " count coverage : " cRST "%-21s " bSTG bV "\n", tmp); + SAYF(bSTOP " count coverage : " cRST "%-21s " bSTG bV "\n", tmp); // 显示覆盖率 SAYF(bVR bH bSTOP cCYA " stage progress " bSTG bH20 bX bH bSTOP cCYA - " findings in depth " bSTG bH20 bVL "\n"); + " findings in depth " bSTG bH20 bVL "\n"); // 绘制UI框 sprintf(tmp, "%s (%0.02f%%)", DI(queued_favored), - ((double)queued_favored) * 100 / queued_paths); + ((double)queued_favored) * 100 / queued_paths); // 构造受青睐路径数显示字符串 /* Yeah... it's still going on... halp? */ SAYF(bV bSTOP " now trying : " cRST "%-21s " bSTG bV bSTOP - " favored paths : " cRST "%-22s " bSTG bV "\n", stage_name, tmp); + " favored paths : " cRST "%-22s " bSTG bV "\n", stage_name, tmp); // 显示当前尝试的路径和受青睐路径数 - if (!stage_max) { + if (!stage_max) { // 如果当前阶段没有最大值 - sprintf(tmp, "%s/-", DI(stage_cur)); + sprintf(tmp, "%s/-", DI(stage_cur)); // 构造当前阶段进度显示字符串 - } else { + } else { // 否则 sprintf(tmp, "%s/%s (%0.02f%%)", DI(stage_cur), DI(stage_max), - ((double)stage_cur) * 100 / stage_max); + ((double)stage_cur) * 100 / stage_max); // 构造当前阶段进度显示字符串 } - SAYF(bV bSTOP " stage execs : " cRST "%-21s " bSTG bV bSTOP, tmp); + SAYF(bV bSTOP " stage execs : " cRST "%-21s " bSTG bV bSTOP, tmp); // 显示当前阶段执行次数 sprintf(tmp, "%s (%0.02f%%)", DI(queued_with_cov), - ((double)queued_with_cov) * 100 / queued_paths); + ((double)queued_with_cov) * 100 / queued_paths); // 构造新边覆盖率显示字符串 - SAYF(" new edges on : " cRST "%-22s " bSTG bV "\n", tmp); + SAYF(" new edges on : " cRST "%-22s " bSTG bV "\n", tmp); // 显示新边覆盖率 sprintf(tmp, "%s (%s%s unique)", DI(total_crashes), DI(unique_crashes), - (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); + (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); // 构造总崩溃数显示字符串 - if (crash_mode) { + if (crash_mode) { // 如果是崩溃模式 SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP " new crashes : %s%-22s " bSTG bV "\n", DI(total_execs), - unique_crashes ? cLRD : cRST, tmp); + unique_crashes ? cLRD : cRST, tmp); // 显示总执行次数和新崩溃数 - } else { + } else { // 否则 SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP " total crashes : %s%-22s " bSTG bV "\n", DI(total_execs), - unique_crashes ? cLRD : cRST, tmp); + unique_crashes ? cLRD : cRST, tmp); // 显示总执行次数和总崩溃数 } /* Show a warning about slow execution. */ - if (avg_exec < 100) { + if (avg_exec < 100) { // 如果平均执行速度小于100 sprintf(tmp, "%s/sec (%s)", DF(avg_exec), avg_exec < 20 ? - "zzzz..." : "slow!"); + "zzzz..." : "slow!"); // 构造执行速度显示字符串 - SAYF(bV bSTOP " exec speed : " cLRD "%-21s ", tmp); + SAYF(bV bSTOP " exec speed : " cLRD "%-21s ", tmp); // 显示执行速度 - } else { + } else { // 否则 - sprintf(tmp, "%s/sec", DF(avg_exec)); - SAYF(bV bSTOP " exec speed : " cRST "%-21s ", tmp); + sprintf(tmp, "%s/sec", DF(avg_exec)); // 构造执行速度显示字符串 + SAYF(bV bSTOP " exec speed : " cRST "%-21s ", tmp); // 显示执行速度 } sprintf(tmp, "%s (%s%s unique)", DI(total_tmouts), DI(unique_tmouts), - (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); + (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); // 构造总超时数显示字符串 - SAYF (bSTG bV bSTOP " total tmouts : " cRST "%-22s " bSTG bV "\n", tmp); + SAYF (bSTG bV bSTOP " total tmouts : " cRST "%-22s " bSTG bV "\n", tmp); // 显示总超时数 /* Aaaalmost there... hold on! */ SAYF(bVR bH cCYA bSTOP " fuzzing strategy yields " bSTG bH10 bH bHT bH10 - bH5 bHB bH bSTOP cCYA " path geometry " bSTG bH5 bH2 bH bVL "\n"); + bH5 bHB bH bSTOP cCYA " path geometry " bSTG bH5 bH2 bH bVL "\n"); // 绘制UI框 - if (skip_deterministic) { + if (skip_deterministic) { // 如果跳过了确定性阶段 - strcpy(tmp, "n/a, n/a, n/a"); + strcpy(tmp, "n/a, n/a, n/a"); // 显示不可用 - } else { + } else { // 否则 sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_FLIP1]), DI(stage_cycles[STAGE_FLIP1]), DI(stage_finds[STAGE_FLIP2]), DI(stage_cycles[STAGE_FLIP2]), - DI(stage_finds[STAGE_FLIP4]), DI(stage_cycles[STAGE_FLIP4])); + DI(stage_finds[STAGE_FLIP4]), DI(stage_cycles[STAGE_FLIP4])); // 构造位翻转阶段的发现数和周期数显示字符串 } SAYF(bV bSTOP " bit flips : " cRST "%-37s " bSTG bV bSTOP " levels : " - cRST "%-10s " bSTG bV "\n", tmp, DI(max_depth)); + cRST "%-10s " bSTG bV "\n", tmp, DI(max_depth)); // 显示位翻转阶段的发现数和最大深度 if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_FLIP8]), DI(stage_cycles[STAGE_FLIP8]), DI(stage_finds[STAGE_FLIP16]), DI(stage_cycles[STAGE_FLIP16]), - DI(stage_finds[STAGE_FLIP32]), DI(stage_cycles[STAGE_FLIP32])); + DI(stage_finds[STAGE_FLIP32]), DI(stage_cycles[STAGE_FLIP32])); // 构造字节翻转阶段的发现数和周期数显示字符串 SAYF(bV bSTOP " byte flips : " cRST "%-37s " bSTG bV bSTOP " pending : " - cRST "%-10s " bSTG bV "\n", tmp, DI(pending_not_fuzzed)); + cRST "%-10s " bSTG bV "\n", tmp, DI(pending_not_fuzzed)); // 显示字节翻转阶段的发现数和待处理路径数 if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_ARITH8]), DI(stage_cycles[STAGE_ARITH8]), DI(stage_finds[STAGE_ARITH16]), DI(stage_cycles[STAGE_ARITH16]), - DI(stage_finds[STAGE_ARITH32]), DI(stage_cycles[STAGE_ARITH32])); + DI(stage_finds[STAGE_ARITH32]), DI(stage_cycles[STAGE_ARITH32])); // 构造算术阶段的发现数和周期数显示字符串 SAYF(bV bSTOP " arithmetics : " cRST "%-37s " bSTG bV bSTOP " pend fav : " - cRST "%-10s " bSTG bV "\n", tmp, DI(pending_favored)); + cRST "%-10s " bSTG bV "\n", tmp, DI(pending_favored)); // 显示算术阶段的发现数和待处理的受青睐路径数 if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_INTEREST8]), DI(stage_cycles[STAGE_INTEREST8]), DI(stage_finds[STAGE_INTEREST16]), DI(stage_cycles[STAGE_INTEREST16]), - DI(stage_finds[STAGE_INTEREST32]), DI(stage_cycles[STAGE_INTEREST32])); + DI(stage_finds[STAGE_INTEREST32]), DI(stage_cycles[STAGE_INTEREST32])); // 构造感兴趣值阶段的发现数和周期数显示字符串 SAYF(bV bSTOP " known ints : " cRST "%-37s " bSTG bV bSTOP " own finds : " - cRST "%-10s " bSTG bV "\n", tmp, DI(queued_discovered)); + cRST "%-10s " bSTG bV "\n", tmp, DI(queued_discovered)); // 显示感兴趣值阶段的发现数和自主发现的路径数 if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_EXTRAS_UO]), DI(stage_cycles[STAGE_EXTRAS_UO]), DI(stage_finds[STAGE_EXTRAS_UI]), DI(stage_cycles[STAGE_EXTRAS_UI]), - DI(stage_finds[STAGE_EXTRAS_AO]), DI(stage_cycles[STAGE_EXTRAS_AO])); + DI(stage_finds[STAGE_EXTRAS_AO]), DI(stage_cycles[STAGE_EXTRAS_AO])); // 构造字典阶段的发现数和周期数显示字符串 SAYF(bV bSTOP " dictionary : " cRST "%-37s " bSTG bV bSTOP " imported : " cRST "%-10s " bSTG bV "\n", tmp, - sync_id ? DI(queued_imported) : (u8*)"n/a"); + sync_id ? DI(queued_imported) : (u8*)"n/a"); // 显示字典阶段的发现数和导入的路径数 sprintf(tmp, "%s/%s, %s/%s", DI(stage_finds[STAGE_HAVOC]), DI(stage_cycles[STAGE_HAVOC]), - DI(stage_finds[STAGE_SPLICE]), DI(stage_cycles[STAGE_SPLICE])); + DI(stage_finds[STAGE_SPLICE]), DI(stage_cycles[STAGE_SPLICE])); // 构造随机变异阶段的发现数和周期数显示字符串 SAYF(bV bSTOP " havoc : " cRST "%-37s " bSTG bV bSTOP, tmp); if (t_bytes) sprintf(tmp, "%0.02f%%", stab_ratio); - else strcpy(tmp, "n/a"); + else strcpy(tmp, "n/a"); // 构造稳定性比率显示字符串 SAYF(" stability : %s%-10s " bSTG bV "\n", (stab_ratio < 85 && var_byte_count > 40) ? cLRD : ((queued_variable && (!persistent_mode || var_byte_count > 20)) - ? cMGN : cRST), tmp); + ? cMGN : cRST), tmp); // 显示稳定性比率 - if (!bytes_trim_out) { + if (!bytes_trim_out) { // 如果没有修剪字节 - sprintf(tmp, "n/a, "); + sprintf(tmp, "n/a, "); // 显示不可用 - } else { + } else { // 否则 sprintf(tmp, "%0.02f%%/%s, ", ((double)(bytes_trim_in - bytes_trim_out)) * 100 / bytes_trim_in, - DI(trim_execs)); + DI(trim_execs)); // 构造修剪字节比率显示字符串 } - if (!blocks_eff_total) { + if (!blocks_eff_total) { // 如果没有有效块 u8 tmp2[128]; sprintf(tmp2, "n/a"); - strcat(tmp, tmp2); + strcat(tmp, tmp2); // 显示不可用 - } else { + } else { // 否则 u8 tmp2[128]; sprintf(tmp2, "%0.02f%%", ((double)(blocks_eff_total - blocks_eff_select)) * 100 / - blocks_eff_total); + blocks_eff_total); // 构造有效块比率显示字符串 - strcat(tmp, tmp2); + strcat(tmp, tmp2); // 拼接字符串 } SAYF(bV bSTOP " trim : " cRST "%-37s " bSTG bVR bH20 bH2 bH2 bRB "\n" - bLB bH30 bH20 bH2 bH bRB bSTOP cRST RESET_G1, tmp); + bLB bH30 bH20 bH2 bH bRB bSTOP cRST RESET_G1, tmp); // 显示修剪字节比率 /* Provide some CPU utilization stats. */ - if (cpu_core_count) { + if (cpu_core_count) { // 如果有CPU核心数 - double cur_runnable = get_runnable_processes(); - u32 cur_utilization = cur_runnable * 100 / cpu_core_count; + double cur_runnable = get_runnable_processes(); // 获取可运行进程数 + u32 cur_utilization = cur_runnable * 100 / cpu_core_count; // 计算CPU利用率 - u8* cpu_color = cCYA; + u8* cpu_color = cCYA; // 默认使用青色 /* If we could still run one or more processes, use green. */ if (cpu_core_count > 1 && cur_runnable + 1 <= cpu_core_count) - cpu_color = cLGN; + cpu_color = cLGN; // 如果CPU未满载,使用绿色 /* If we're clearly oversubscribed, use red. */ - if (!no_cpu_meter_red && cur_utilization >= 150) cpu_color = cLRD; + if (!no_cpu_meter_red && cur_utilization >= 150) cpu_color = cLRD; // 如果CPU过载,使用红色 #ifdef HAVE_AFFINITY - if (cpu_aff >= 0) { + if (cpu_aff >= 0) { // 如果有CPU亲和性设置 SAYF(SP10 cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, MIN(cpu_aff, 999), cpu_color, - MIN(cur_utilization, 999)); + MIN(cur_utilization, 999)); // 显示CPU利用率和亲和性 - } else { + } else { // 否则 SAYF(SP10 cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, - cpu_color, MIN(cur_utilization, 999)); + cpu_color, MIN(cur_utilization, 999)); // 显示CPU利用率 } #else SAYF(SP10 cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, - cpu_color, MIN(cur_utilization, 999)); + cpu_color, MIN(cur_utilization, 999)); // 显示CPU利用率 #endif /* ^HAVE_AFFINITY */ - } else SAYF("\r"); + } else SAYF("\r"); // 如果没有CPU核心数,只换行 /* Hallelujah! */ - fflush(0); + fflush(0); // 刷新输出缓冲区 } - /* Display quick statistics at the end of processing the input directory, plus a bunch of warnings. Some calibration stuff also ended up here, along with several hardcoded constants. Maybe clean up eventually. */ 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; + 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 (q->exec_us > max_us) max_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 (!min_bits || q->bitmap_size < min_bits) min_bits = q->bitmap_size; - if (q->bitmap_size > max_bits) max_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->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)) 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. */ - 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 (avg_us > 50000) havoc_div = 10; /* 0-19 execs/sec */ // 如果平均执行时间大于50000微秒,调整havoc_div + else if (avg_us > 20000) havoc_div = 5; /* 20-49 execs/sec */ // 如果平均执行时间大于20000微秒,调整havoc_div + else if (avg_us > 10000) havoc_div = 2; /* 50-100 execs/sec */ // 如果平均执行时间大于10000微秒,调整havoc_div - if (!resuming_fuzz) { + 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); + DMS(max_len), doc_path); // 如果测试用例过大,发出警告 else if (max_len > 10 * 1024) 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) - 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) - 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) - WARNF("You have lots of input files; try starting small."); + WARNF("You have lots of input files; try starting small."); // 如果输入文件较多,发出警告 } @@ -4465,9 +4476,9 @@ static void show_init_stats(void) { cGRA " Exec timing : " cRST "%s to %s us (average: %s us)\n", queued_favored, queued_variable, queued_paths, min_bits, max_bits, ((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 1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second. @@ -4476,23 +4487,23 @@ static void show_init_stats(void) { 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; - 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; // 确保超时不超过EXEC_TIMEOUT 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); // 提示用户使用恢复会话中的超时设置 } @@ -4500,9 +4511,9 @@ static void show_init_stats(void) { limit is very expensive, so let's select a more conservative default. */ 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); // 在dumb模式下,设置更保守的挂起超时 - OKF("All set and ready to roll!"); + OKF("All set and ready to roll!"); // 提示用户一切准备就绪 } @@ -4512,9 +4523,9 @@ static void show_init_stats(void) { static u32 next_p2(u32 val) { - u32 ret = 1; - while (val > ret) ret <<= 1; - return ret; + u32 ret = 1; // 初始化为1 + while (val > ret) ret <<= 1; // 找到大于等于val的最小2的幂 + return ret; // 返回结果 } @@ -4525,173 +4536,171 @@ static u32 next_p2(u32 val) { static u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) { - static u8 tmp[64]; - static u8 clean_trace[MAP_SIZE]; + static u8 tmp[64]; // 临时缓冲区 + static u8 clean_trace[MAP_SIZE]; // 用于保存干净的trace - u8 needs_write = 0, fault = 0; - u32 trim_exec = 0; - u32 remove_len; - u32 len_p2; + u8 needs_write = 0, fault = 0; // 是否需要写入和是否发生错误 + u32 trim_exec = 0; // 修剪执行次数 + u32 remove_len; // 删除的长度 + u32 len_p2; // 文件长度的2的幂 /* Although the trimmer will be less useful when variable behavior is detected, it will still work to some extent, so we don't check for this. */ - if (q->len < 5) return 0; + if (q->len < 5) return 0; // 如果文件长度小于5,直接返回 - stage_name = tmp; - bytes_trim_in += q->len; + stage_name = tmp; // 设置阶段名称 + bytes_trim_in += q->len; // 增加修剪的字节数 /* Select initial chunk len, starting with large steps. */ - len_p2 = next_p2(q->len); + len_p2 = next_p2(q->len); // 计算文件长度的2的幂 - remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES); + remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES); // 计算初始删除长度 /* Continue until the number of steps gets too high or the stepover gets too small. */ - while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) { + while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) { // 直到删除长度过小 - u32 remove_pos = remove_len; + u32 remove_pos = remove_len; // 删除的起始位置 - sprintf(tmp, "trim %s/%s", DI(remove_len), DI(remove_len)); + sprintf(tmp, "trim %s/%s", DI(remove_len), DI(remove_len)); // 构造阶段名称 - stage_cur = 0; - stage_max = q->len / remove_len; + stage_cur = 0; // 初始化当前阶段 + stage_max = q->len / remove_len; // 计算阶段最大值 - while (remove_pos < q->len) { + while (remove_pos < q->len) { // 遍历文件 - u32 trim_avail = MIN(remove_len, q->len - remove_pos); + u32 trim_avail = MIN(remove_len, q->len - remove_pos); // 计算可修剪的长度 u32 cksum; - write_with_gap(in_buf, q->len, remove_pos, trim_avail); + write_with_gap(in_buf, q->len, remove_pos, trim_avail); // 写入文件,跳过修剪的部分 - fault = run_target(argv, exec_tmout); - trim_execs++; + fault = run_target(argv, exec_tmout); // 运行目标程序 + trim_execs++; // 增加修剪执行次数 - if (stop_soon || fault == FAULT_ERROR) goto abort_trimming; + if (stop_soon || fault == FAULT_ERROR) goto abort_trimming; // 如果停止或发生错误,跳转到abort_trimming /* Note that we don't keep track of crashes or hangs here; maybe TODO? */ - cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算trace的校验和 /* If the deletion had no impact on the trace, make it permanent. This isn't perfect for variable-path inputs, but we're just making a best-effort pass, so it's not a big deal if we end up with false negatives every now and then. */ - if (cksum == q->exec_cksum) { + if (cksum == q->exec_cksum) { // 如果校验和未改变 - u32 move_tail = q->len - remove_pos - trim_avail; + u32 move_tail = q->len - remove_pos - trim_avail; // 计算尾部需要移动的长度 - q->len -= trim_avail; - len_p2 = next_p2(q->len); + q->len -= trim_avail; // 更新文件长度 + len_p2 = next_p2(q->len); // 重新计算文件长度的2的幂 memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, - move_tail); + move_tail); // 移动尾部数据 /* Let's save a clean trace, which will be needed by update_bitmap_score once we're done with the trimming stuff. */ - if (!needs_write) { + if (!needs_write) { // 如果不需要写入 - needs_write = 1; - memcpy(clean_trace, trace_bits, MAP_SIZE); + needs_write = 1; // 标记需要写入 + memcpy(clean_trace, trace_bits, MAP_SIZE); // 保存干净的trace } - } else remove_pos += remove_len; + } else remove_pos += remove_len; // 否则移动到下一个位置 /* Since this can be slow, update the screen every now and then. */ - if (!(trim_exec++ % stats_update_freq)) show_stats(); - stage_cur++; + if (!(trim_exec++ % stats_update_freq)) show_stats(); // 定期更新UI + stage_cur++; // 增加当前阶段 } - remove_len >>= 1; + remove_len >>= 1; // 减少删除长度 } /* If we have made changes to in_buf, we also need to update the on-disk version of the test case. */ - if (needs_write) { + if (needs_write) { // 如果需要写入 s32 fd; - unlink(q->fname); /* ignore errors */ + unlink(q->fname); /* ignore errors */ // 删除旧文件 - fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); + fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建新文件 - if (fd < 0) PFATAL("Unable to create '%s'", q->fname); + if (fd < 0) PFATAL("Unable to create '%s'", q->fname); // 如果创建失败,报错 - ck_write(fd, in_buf, q->len, q->fname); - close(fd); + ck_write(fd, in_buf, q->len, q->fname); // 写入文件 + close(fd); // 关闭文件 - memcpy(trace_bits, clean_trace, MAP_SIZE); - update_bitmap_score(q); + memcpy(trace_bits, clean_trace, MAP_SIZE); // 恢复干净的trace + update_bitmap_score(q); // 更新位图分数 } abort_trimming: - bytes_trim_out += q->len; - return fault; + bytes_trim_out += q->len; // 增加修剪后的字节数 + return fault; // 返回错误状态 } - - /* Write a modified test case, run program, process results. Handle error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ EXP_ST u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) { - u8 fault; + u8 fault; // 用于存储目标程序的运行结果 - if (post_handler) { + if (post_handler) { // 如果存在后处理函数 - out_buf = post_handler(out_buf, &len); - if (!out_buf || !len) return 0; + out_buf = post_handler(out_buf, &len); // 调用后处理函数处理输出缓冲区 + if (!out_buf || !len) return 0; // 如果处理后缓冲区为空或长度为0,返回0 } - write_to_testcase(out_buf, len); + write_to_testcase(out_buf, len); // 将输出缓冲区写入测试用例文件 - fault = run_target(argv, exec_tmout); + fault = run_target(argv, exec_tmout); // 运行目标程序 - if (stop_soon) return 1; + if (stop_soon) return 1; // 如果收到停止信号,返回1 - if (fault == FAULT_TMOUT) { + if (fault == FAULT_TMOUT) { // 如果目标程序超时 - if (subseq_tmouts++ > TMOUT_LIMIT) { - cur_skipped_paths++; - return 1; + if (subseq_tmouts++ > TMOUT_LIMIT) { // 如果连续超时次数超过限制 + cur_skipped_paths++; // 增加跳过的路径数 + return 1; // 返回1 } - } else subseq_tmouts = 0; + } else subseq_tmouts = 0; // 否则重置连续超时计数器 /* Users can hit us with SIGUSR1 to request the current input to be abandoned. */ - if (skip_requested) { + if (skip_requested) { // 如果收到跳过请求 - skip_requested = 0; - cur_skipped_paths++; - return 1; + skip_requested = 0; // 重置跳过请求标志 + cur_skipped_paths++; // 增加跳过的路径数 + return 1; // 返回1 } /* This handles FAULT_ERROR for us: */ - queued_discovered += save_if_interesting(argv, out_buf, len, fault); + queued_discovered += save_if_interesting(argv, out_buf, len, fault); // 保存有趣的测试用例 if (!(stage_cur % stats_update_freq) || stage_cur + 1 == stage_max) - show_stats(); + show_stats(); // 定期更新UI - return 0; + return 0; // 返回0 } @@ -4701,40 +4710,40 @@ EXP_ST u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) { static u32 choose_block_len(u32 limit) { - u32 min_value, max_value; - u32 rlim = MIN(queue_cycle, 3); + u32 min_value, max_value; // 最小值和最大值 + u32 rlim = MIN(queue_cycle, 3); // 限制范围 - if (!run_over10m) rlim = 1; + if (!run_over10m) rlim = 1; // 如果运行时间未超过10分钟,限制为1 - switch (UR(rlim)) { + switch (UR(rlim)) { // 随机选择 case 0: min_value = 1; - max_value = HAVOC_BLK_SMALL; + max_value = HAVOC_BLK_SMALL; // 小块 break; case 1: min_value = HAVOC_BLK_SMALL; - max_value = HAVOC_BLK_MEDIUM; + max_value = HAVOC_BLK_MEDIUM; // 中块 break; default: - if (UR(10)) { + if (UR(10)) { // 90%的概率 min_value = HAVOC_BLK_MEDIUM; - max_value = HAVOC_BLK_LARGE; + max_value = HAVOC_BLK_LARGE; // 大块 - } else { + } else { // 10%的概率 min_value = HAVOC_BLK_LARGE; - max_value = HAVOC_BLK_XL; + max_value = HAVOC_BLK_XL; // 超大块 } } - if (min_value >= limit) min_value = 1; + if (min_value >= limit) min_value = 1; // 如果最小值大于限制,设置为1 - return min_value + UR(MIN(max_value, limit) - min_value + 1); + return min_value + UR(MIN(max_value, limit) - min_value + 1); // 返回随机块长度 } @@ -4745,45 +4754,45 @@ static u32 choose_block_len(u32 limit) { static u32 calculate_score(struct queue_entry* q) { - u32 avg_exec_us = total_cal_us / total_cal_cycles; - u32 avg_bitmap_size = total_bitmap_size / total_bitmap_entries; - u32 perf_score = 100; + u32 avg_exec_us = total_cal_us / total_cal_cycles; // 计算平均执行时间 + u32 avg_bitmap_size = total_bitmap_size / total_bitmap_entries; // 计算平均位图大小 + u32 perf_score = 100; // 初始性能分数 /* Adjust score based on execution speed of this path, compared to the global average. Multiplier ranges from 0.1x to 3x. Fast inputs are less expensive to fuzz, so we're giving them more air time. */ - if (q->exec_us * 0.1 > avg_exec_us) perf_score = 10; - else if (q->exec_us * 0.25 > avg_exec_us) perf_score = 25; - else if (q->exec_us * 0.5 > avg_exec_us) perf_score = 50; - else if (q->exec_us * 0.75 > avg_exec_us) perf_score = 75; - else if (q->exec_us * 4 < avg_exec_us) perf_score = 300; - else if (q->exec_us * 3 < avg_exec_us) perf_score = 200; - else if (q->exec_us * 2 < avg_exec_us) perf_score = 150; + if (q->exec_us * 0.1 > avg_exec_us) perf_score = 10; // 如果执行时间远高于平均,降低分数 + else if (q->exec_us * 0.25 > avg_exec_us) perf_score = 25; // 如果执行时间高于平均,降低分数 + else if (q->exec_us * 0.5 > avg_exec_us) perf_score = 50; // 如果执行时间略高于平均,降低分数 + else if (q->exec_us * 0.75 > avg_exec_us) perf_score = 75; // 如果执行时间接近平均,降低分数 + else if (q->exec_us * 4 < avg_exec_us) perf_score = 300; // 如果执行时间远低于平均,提高分数 + else if (q->exec_us * 3 < avg_exec_us) perf_score = 200; // 如果执行时间低于平均,提高分数 + else if (q->exec_us * 2 < avg_exec_us) perf_score = 150; // 如果执行时间略低于平均,提高分数 /* Adjust score based on bitmap size. The working theory is that better coverage translates to better targets. Multiplier from 0.25x to 3x. */ - if (q->bitmap_size * 0.3 > avg_bitmap_size) perf_score *= 3; - else if (q->bitmap_size * 0.5 > avg_bitmap_size) perf_score *= 2; - else if (q->bitmap_size * 0.75 > avg_bitmap_size) perf_score *= 1.5; - else if (q->bitmap_size * 3 < avg_bitmap_size) perf_score *= 0.25; - else if (q->bitmap_size * 2 < avg_bitmap_size) perf_score *= 0.5; - else if (q->bitmap_size * 1.5 < avg_bitmap_size) perf_score *= 0.75; + if (q->bitmap_size * 0.3 > avg_bitmap_size) perf_score *= 3; // 如果位图大小远高于平均,提高分数 + else if (q->bitmap_size * 0.5 > avg_bitmap_size) perf_score *= 2; // 如果位图大小高于平均,提高分数 + else if (q->bitmap_size * 0.75 > avg_bitmap_size) perf_score *= 1.5; // 如果位图大小略高于平均,提高分数 + else if (q->bitmap_size * 3 < avg_bitmap_size) perf_score *= 0.25; // 如果位图大小远低于平均,降低分数 + else if (q->bitmap_size * 2 < avg_bitmap_size) perf_score *= 0.5; // 如果位图大小低于平均,降低分数 + else if (q->bitmap_size * 1.5 < avg_bitmap_size) perf_score *= 0.75; // 如果位图大小略低于平均,降低分数 /* Adjust score based on handicap. Handicap is proportional to how late in the game we learned about this path. Latecomers are allowed to run for a bit longer until they catch up with the rest. */ - if (q->handicap >= 4) { + if (q->handicap >= 4) { // 如果handicap大于等于4 - perf_score *= 4; - q->handicap -= 4; + perf_score *= 4; // 提高分数 + q->handicap -= 4; // 减少handicap - } else if (q->handicap) { + } else if (q->handicap) { // 如果handicap大于0 - perf_score *= 2; - q->handicap--; + perf_score *= 2; // 提高分数 + q->handicap--; // 减少handicap } @@ -4791,21 +4800,21 @@ static u32 calculate_score(struct queue_entry* q) { deeper test cases is more likely to reveal stuff that can't be discovered with traditional fuzzers. */ - switch (q->depth) { + switch (q->depth) { // 根据输入深度调整分数 - case 0 ... 3: break; - case 4 ... 7: perf_score *= 2; break; - case 8 ... 13: perf_score *= 3; break; - case 14 ... 25: perf_score *= 4; break; - default: perf_score *= 5; + case 0 ... 3: break; // 深度0-3,不调整 + case 4 ... 7: perf_score *= 2; break; // 深度4-7,分数翻倍 + case 8 ... 13: perf_score *= 3; break; // 深度8-13,分数三倍 + case 14 ... 25: perf_score *= 4; break; // 深度14-25,分数四倍 + default: perf_score *= 5; // 深度大于25,分数五倍 } /* Make sure that we don't go over limit. */ - if (perf_score > HAVOC_MAX_MULT * 100) perf_score = HAVOC_MAX_MULT * 100; + if (perf_score > HAVOC_MAX_MULT * 100) perf_score = HAVOC_MAX_MULT * 100; // 确保分数不超过上限 - return perf_score; + return perf_score; // 返回性能分数 } @@ -4819,105 +4828,104 @@ static u32 calculate_score(struct queue_entry* q) { static u8 could_be_bitflip(u32 xor_val) { - u32 sh = 0; + u32 sh = 0; // 移位计数器 - if (!xor_val) return 1; + if (!xor_val) return 1; // 如果xor_val为0,返回1 /* Shift left until first bit set. */ - while (!(xor_val & 1)) { sh++; xor_val >>= 1; } + while (!(xor_val & 1)) { sh++; xor_val >>= 1; } // 找到第一个置位 /* 1-, 2-, and 4-bit patterns are OK anywhere. */ - if (xor_val == 1 || xor_val == 3 || xor_val == 15) return 1; + if (xor_val == 1 || xor_val == 3 || xor_val == 15) return 1; // 如果是1、3或15,返回1 /* 8-, 16-, and 32-bit patterns are OK only if shift factor is divisible by 8, since that's the stepover for these ops. */ - if (sh & 7) return 0; + if (sh & 7) return 0; // 如果移位不是8的倍数,返回0 if (xor_val == 0xff || xor_val == 0xffff || xor_val == 0xffffffff) - return 1; + return 1; // 如果是0xff、0xffff或0xffffffff,返回1 - return 0; + return 0; // 否则返回0 } - /* Helper function to see if a particular value is reachable through arithmetic operations. Used for similar purposes. */ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { - u32 i, ov = 0, nv = 0, diffs = 0; + u32 i, ov = 0, nv = 0, diffs = 0; // 用于存储旧值、新值和差异数 - if (old_val == new_val) return 1; + if (old_val == new_val) return 1; // 如果旧值和新值相同,返回1 /* See if one-byte adjustments to any byte could produce this result. */ - for (i = 0; i < blen; i++) { + for (i = 0; i < blen; i++) { // 遍历每个字节 - u8 a = old_val >> (8 * i), - b = new_val >> (8 * i); + u8 a = old_val >> (8 * i), // 获取旧值的字节 + b = new_val >> (8 * i); // 获取新值的字节 - if (a != b) { diffs++; ov = a; nv = b; } + if (a != b) { diffs++; ov = a; nv = b; } // 如果字节不同,增加差异数并记录旧值和新值 } /* If only one byte differs and the values are within range, return 1. */ - if (diffs == 1) { + if (diffs == 1) { // 如果只有一个字节不同 - if ((u8)(ov - nv) <= ARITH_MAX || - (u8)(nv - ov) <= ARITH_MAX) return 1; + if ((u8)(ov - nv) <= ARITH_MAX || // 如果差值在ARITH_MAX范围内 + (u8)(nv - ov) <= ARITH_MAX) return 1; // 或者反向差值在ARITH_MAX范围内,返回1 } - if (blen == 1) return 0; + if (blen == 1) return 0; // 如果块长度为1,返回0 /* See if two-byte adjustments to any byte would produce this result. */ - diffs = 0; + diffs = 0; // 重置差异数 - for (i = 0; i < blen / 2; i++) { + for (i = 0; i < blen / 2; i++) { // 遍历每个双字节 - u16 a = old_val >> (16 * i), - b = new_val >> (16 * i); + u16 a = old_val >> (16 * i), // 获取旧值的双字节 + b = new_val >> (16 * i); // 获取新值的双字节 - if (a != b) { diffs++; ov = a; nv = b; } + if (a != b) { diffs++; ov = a; nv = b; } // 如果双字节不同,增加差异数并记录旧值和新值 } /* If only one word differs and the values are within range, return 1. */ - if (diffs == 1) { + if (diffs == 1) { // 如果只有一个双字节不同 - if ((u16)(ov - nv) <= ARITH_MAX || - (u16)(nv - ov) <= ARITH_MAX) return 1; + if ((u16)(ov - nv) <= ARITH_MAX || // 如果差值在ARITH_MAX范围内 + (u16)(nv - ov) <= ARITH_MAX) return 1; // 或者反向差值在ARITH_MAX范围内,返回1 - ov = SWAP16(ov); nv = SWAP16(nv); + ov = SWAP16(ov); nv = SWAP16(nv); // 交换字节顺序 - if ((u16)(ov - nv) <= ARITH_MAX || - (u16)(nv - ov) <= ARITH_MAX) return 1; + if ((u16)(ov - nv) <= ARITH_MAX || // 如果交换后的差值在ARITH_MAX范围内 + (u16)(nv - ov) <= ARITH_MAX) return 1; // 或者反向差值在ARITH_MAX范围内,返回1 } /* Finally, let's do the same thing for dwords. */ - if (blen == 4) { + if (blen == 4) { // 如果块长度为4 - if ((u32)(old_val - new_val) <= ARITH_MAX || - (u32)(new_val - old_val) <= ARITH_MAX) return 1; + if ((u32)(old_val - new_val) <= ARITH_MAX || // 如果差值在ARITH_MAX范围内 + (u32)(new_val - old_val) <= ARITH_MAX) return 1; // 或者反向差值在ARITH_MAX范围内,返回1 - new_val = SWAP32(new_val); - old_val = SWAP32(old_val); + new_val = SWAP32(new_val); // 交换字节顺序 + old_val = SWAP32(old_val); // 交换字节顺序 - if ((u32)(old_val - new_val) <= ARITH_MAX || - (u32)(new_val - old_val) <= ARITH_MAX) return 1; + if ((u32)(old_val - new_val) <= ARITH_MAX || // 如果交换后的差值在ARITH_MAX范围内 + (u32)(new_val - old_val) <= ARITH_MAX) return 1; // 或者反向差值在ARITH_MAX范围内,返回1 } - return 0; + return 0; // 否则返回0 } @@ -4930,21 +4938,21 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { - u32 i, j; + u32 i, j; // 用于循环计数 - if (old_val == new_val) return 1; + if (old_val == new_val) return 1; // 如果旧值和新值相同,返回1 /* See if one-byte insertions from interesting_8 over old_val could produce new_val. */ - for (i = 0; i < blen; i++) { + for (i = 0; i < blen; i++) { // 遍历每个字节 - for (j = 0; j < sizeof(interesting_8); j++) { + for (j = 0; j < sizeof(interesting_8); j++) { // 遍历interesting_8数组 - u32 tval = (old_val & ~(0xff << (i * 8))) | - (((u8)interesting_8[j]) << (i * 8)); + u32 tval = (old_val & ~(0xff << (i * 8))) | // 清除旧值的字节 + (((u8)interesting_8[j]) << (i * 8)); // 插入interesting_8的值 - if (new_val == tval) return 1; + if (new_val == tval) return 1; // 如果新值等于tval,返回1 } @@ -4953,27 +4961,27 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { /* Bail out unless we're also asked to examine two-byte LE insertions as a preparation for BE attempts. */ - if (blen == 2 && !check_le) return 0; + if (blen == 2 && !check_le) return 0; // 如果块长度为2且不需要检查LE插入,返回0 /* See if two-byte insertions over old_val could give us new_val. */ - for (i = 0; i < blen - 1; i++) { + for (i = 0; i < blen - 1; i++) { // 遍历每个双字节 - for (j = 0; j < sizeof(interesting_16) / 2; j++) { + for (j = 0; j < sizeof(interesting_16) / 2; j++) { // 遍历interesting_16数组 - u32 tval = (old_val & ~(0xffff << (i * 8))) | - (((u16)interesting_16[j]) << (i * 8)); + u32 tval = (old_val & ~(0xffff << (i * 8))) | // 清除旧值的双字节 + (((u16)interesting_16[j]) << (i * 8)); // 插入interesting_16的值 - if (new_val == tval) return 1; + if (new_val == tval) return 1; // 如果新值等于tval,返回1 /* Continue here only if blen > 2. */ - if (blen > 2) { + if (blen > 2) { // 如果块长度大于2 - tval = (old_val & ~(0xffff << (i * 8))) | - (SWAP16(interesting_16[j]) << (i * 8)); + tval = (old_val & ~(0xffff << (i * 8))) | // 清除旧值的双字节 + (SWAP16(interesting_16[j]) << (i * 8)); // 插入交换后的interesting_16的值 - if (new_val == tval) return 1; + if (new_val == tval) return 1; // 如果新值等于tval,返回1 } @@ -4981,24 +4989,22 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { } - if (blen == 4 && check_le) { + if (blen == 4 && check_le) { // 如果块长度为4且需要检查LE插入 /* See if four-byte insertions could produce the same result (LE only). */ - for (j = 0; j < sizeof(interesting_32) / 4; j++) - if (new_val == (u32)interesting_32[j]) return 1; + for (j = 0; j < sizeof(interesting_32) / 4; j++) // 遍历interesting_32数组 + if (new_val == (u32)interesting_32[j]) return 1; // 如果新值等于interesting_32的值,返回1 } - return 0; + return 0; // 否则返回0 } - -/* Take the current entry from the queue, fuzz it for a while. This - function is a tad too long... returns 0 if fuzzed successfully, 1 if - skipped or bailed out. */ +/* 从队列中取出当前条目,进行模糊测试。这个函数有点长... + 如果模糊测试成功返回0,如果跳过或放弃返回1。 */ static u8 fuzz_one(char** argv) { @@ -5014,8 +5020,7 @@ static u8 fuzz_one(char** argv) { #ifdef IGNORE_FINDS - /* In IGNORE_FINDS mode, skip any entries that weren't in the - initial data set. */ + /* 在 IGNORE_FINDS 模式下,跳过任何不在初始数据集中的条目。 */ if (queue_cur->depth > 1) return 1; @@ -5023,18 +5028,17 @@ static u8 fuzz_one(char** argv) { if (pending_favored) { - /* If we have any favored, non-fuzzed new arrivals in the queue, - possibly skip to them at the expense of already-fuzzed or non-favored - cases. */ + /* 如果我们有任何优先的、未模糊测试的新条目在队列中, + 可能会跳过已经模糊测试过的或非优先的条目。 */ if ((queue_cur->was_fuzzed || !queue_cur->favored) && UR(100) < SKIP_TO_NEW_PROB) return 1; } else if (!dumb_mode && !queue_cur->favored && queued_paths > 10) { - /* Otherwise, still possibly skip non-favored cases, albeit less often. - The odds of skipping stuff are higher for already-fuzzed inputs and - lower for never-fuzzed entries. */ + /* 否则,仍然可能跳过非优先的条目,尽管频率较低。 + 对于已经模糊测试过的输入,跳过它们的概率较高, + 而对于从未模糊测试过的条目,跳过它们的概率较低。 */ if (queue_cycle > 1 && !queue_cur->was_fuzzed) { @@ -5056,7 +5060,7 @@ static u8 fuzz_one(char** argv) { fflush(stdout); } - /* Map the test case into memory. */ + /* 将测试用例映射到内存中。 */ fd = open(queue_cur->fname, O_RDONLY); @@ -5070,9 +5074,8 @@ static u8 fuzz_one(char** argv) { close(fd); - /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every - single byte anyway, so it wouldn't give us any performance or memory usage - benefits. */ + /* 我们可以将 out_buf 映射为 MAP_PRIVATE,但由于我们会覆盖每个字节, + 因此这样做不会带来任何性能或内存使用上的好处。 */ out_buf = ck_alloc_nozero(len); @@ -5090,9 +5093,9 @@ static u8 fuzz_one(char** argv) { if (queue_cur->cal_failed < CAL_CHANCES) { - /* Reset exec_cksum to tell calibrate_case to re-execute the testcase - avoiding the usage of an invalid trace_bits. - For more info: https://github.com/AFLplusplus/AFLplusplus/pull/425 */ + /* 重置 exec_cksum,告诉 calibrate_case 重新执行测试用例, + 避免使用无效的 trace_bits。 + 更多信息:https://github.com/AFLplusplus/AFLplusplus/pull/425 */ queue_cur->exec_cksum = 0; @@ -5126,7 +5129,7 @@ static u8 fuzz_one(char** argv) { goto abandon_entry; } - /* Don't retry trimming, even if it failed. */ + /* 即使修剪失败,也不要重试。 */ queue_cur->trim_done = 1; @@ -5142,15 +5145,13 @@ static u8 fuzz_one(char** argv) { orig_perf = perf_score = calculate_score(queue_cur); - /* Skip right away if -d is given, if we have done deterministic fuzzing on - this entry ourselves (was_fuzzed), or if it has gone through deterministic - testing in earlier, resumed runs (passed_det). */ + /* 如果给定了 -d 参数,或者我们已经对这个条目进行了确定性模糊测试(was_fuzzed), + 或者在早期恢复的运行中已经通过了确定性测试(passed_det),则立即跳过。 */ if (skip_deterministic || queue_cur->was_fuzzed || queue_cur->passed_det) goto havoc_stage; - /* Skip deterministic fuzzing if exec path checksum puts this out of scope - for this master instance. */ + /* 如果执行路径校验和将此条目排除在主实例的范围之外,则跳过确定性模糊测试。 */ if (master_max && (queue_cur->exec_cksum % master_max) != master_id - 1) goto havoc_stage; @@ -5167,7 +5168,7 @@ static u8 fuzz_one(char** argv) { _arf[(_bf) >> 3] ^= (128 >> ((_bf) & 7)); \ } while (0) - /* Single walking bit. */ + /* 单比特翻转。 */ stage_short = "flip1"; stage_max = len << 3; @@ -5189,32 +5190,24 @@ static u8 fuzz_one(char** argv) { FLIP_BIT(out_buf, stage_cur); - /* While flipping the least significant bit in every byte, pull of an extra - trick to detect possible syntax tokens. In essence, the idea is that if - you have a binary blob like this: + /* 在翻转每个字节的最低有效位时,我们还会执行一个额外的技巧来检测可能的语法标记。 + 本质上,如果你有一个像这样的二进制 blob: xxxxxxxxIHDRxxxxxxxx - ...and changing the leading and trailing bytes causes variable or no - changes in program flow, but touching any character in the "IHDR" string - always produces the same, distinctive path, it's highly likely that - "IHDR" is an atomically-checked magic value of special significance to - the fuzzed format. + ...并且改变前导和尾随字节会导致程序流程的变量或没有变化, + 但触摸 "IHDR" 字符串中的任何字符总是产生相同的、独特的路径, + 那么很可能 "IHDR" 是一个原子检查的魔法值,对被模糊测试的格式具有特殊意义。 - We do this here, rather than as a separate stage, because it's a nice - way to keep the operation approximately "free" (i.e., no extra execs). - - Empirically, performing the check when flipping the least significant bit - is advantageous, compared to doing it at the time of more disruptive - changes, where the program flow may be affected in more violent ways. + 我们在这里执行此操作,而不是作为一个单独的阶段,因为这是一种保持操作近似“免费”的好方法 + (即,没有额外的执行)。 - The caveat is that we won't generate dictionaries in the -d mode or -S - mode - but that's probably a fair trade-off. + 经验表明,在翻转最低有效位时执行检查是有利的,与在更破坏性的更改时执行检查相比, + 后者可能会以更剧烈的方式影响程序流程。 - This won't work particularly well with paths that exhibit variable - behavior, but fails gracefully, so we'll carry out the checks anyway. + 需要注意的是,我们不会在 -d 模式或 -S 模式下生成字典——但这可能是一个公平的权衡。 - */ + 这对于表现出可变行为的路径效果不佳,但会优雅地失败,因此我们仍然会执行检查。 */ if (!dumb_mode && (stage_cur & 7) == 7) { @@ -5222,8 +5215,7 @@ static u8 fuzz_one(char** argv) { if (stage_cur == stage_max - 1 && cksum == prev_cksum) { - /* If at end of file and we are still collecting a string, grab the - final character and force output. */ + /* 如果在文件末尾并且我们仍在收集字符串,则抓取最后一个字符并强制输出。 */ if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3]; a_len++; @@ -5233,8 +5225,8 @@ static u8 fuzz_one(char** argv) { } else if (cksum != prev_cksum) { - /* Otherwise, if the checksum has changed, see if we have something - worthwhile queued up, and collect that if the answer is yes. */ + /* 否则,如果校验和已更改,请查看我们是否有值得排队的内容, + 如果有,则收集它。 */ if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) maybe_add_auto(a_collect, a_len); @@ -5244,8 +5236,7 @@ static u8 fuzz_one(char** argv) { } - /* Continue collecting string, but only if the bit flip actually made - any difference - we don't want no-op tokens. */ + /* 继续收集字符串,但仅当位翻转实际上产生了任何差异时——我们不想要无操作的标记。 */ if (cksum != queue_cur->exec_cksum) { @@ -5263,7 +5254,7 @@ static u8 fuzz_one(char** argv) { stage_finds[STAGE_FLIP1] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_FLIP1] += stage_max; - /* Two walking bits. */ + /* 双比特翻转。 */ stage_name = "bitflip 2/1"; stage_short = "flip2"; @@ -5290,7 +5281,7 @@ static u8 fuzz_one(char** argv) { stage_finds[STAGE_FLIP2] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_FLIP2] += stage_max; - /* Four walking bits. */ + /* 四比特翻转。 */ stage_name = "bitflip 4/1"; stage_short = "flip4"; @@ -5321,11 +5312,11 @@ static u8 fuzz_one(char** argv) { stage_finds[STAGE_FLIP4] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_FLIP4] += stage_max; - /* Effector map setup. These macros calculate: + /* 效应器映射设置。这些宏计算: - EFF_APOS - position of a particular file offset in the map. - EFF_ALEN - length of a map with a particular number of bytes. - EFF_SPAN_ALEN - map span for a sequence of bytes. + EFF_APOS - 文件中特定偏移量在映射中的位置。 + EFF_ALEN - 具有特定字节数的映射的长度。 + EFF_SPAN_ALEN - 字节序列的映射跨度。 */ @@ -5334,8 +5325,7 @@ static u8 fuzz_one(char** argv) { #define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l)) #define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1) - /* Initialize effector map for the next step (see comments below). Always - flag first and last byte as doing something. */ + /* 为下一步初始化效应器映射(见下文注释)。始终标记第一个和最后一个字节为有效。 */ eff_map = ck_alloc(EFF_ALEN(len)); eff_map[0] = 1; @@ -5345,7 +5335,7 @@ static u8 fuzz_one(char** argv) { eff_cnt++; } - /* Walking byte. */ + /* 字节翻转。 */ stage_name = "bitflip 8/8"; stage_short = "flip8"; @@ -5361,17 +5351,15 @@ static u8 fuzz_one(char** argv) { if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - /* We also use this stage to pull off a simple trick: we identify - bytes that seem to have no effect on the current execution path - even when fully flipped - and we skip them during more expensive - deterministic stages, such as arithmetics or known ints. */ + /* 我们还使用此阶段来执行一个简单的技巧:我们识别那些即使完全翻转 + 似乎对当前执行路径也没有影响的字节——并在更昂贵的确定性阶段(如算术或已知整数) + 中跳过它们。 */ if (!eff_map[EFF_APOS(stage_cur)]) { u32 cksum; - /* If in dumb mode or if the file is very short, just flag everything - without wasting time on checksums. */ + /* 如果在 dumb 模式下或文件非常短,则无需浪费时间计算校验和,直接标记所有字节。 */ if (!dumb_mode && len >= EFF_MIN_LEN) cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); @@ -5389,9 +5377,8 @@ static u8 fuzz_one(char** argv) { } - /* If the effector map is more than EFF_MAX_PERC dense, just flag the - whole thing as worth fuzzing, since we wouldn't be saving much time - anyway. */ + /* 如果效应器映射的密度超过 EFF_MAX_PERC,则将其全部标记为值得模糊测试, + 因为无论如何我们不会节省太多时间。 */ if (eff_cnt != EFF_ALEN(len) && eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { @@ -5413,7 +5400,7 @@ static u8 fuzz_one(char** argv) { stage_finds[STAGE_FLIP8] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_FLIP8] += stage_max; - /* Two walking bytes. */ + /* 双字节翻转。 */ if (len < 2) goto skip_bitflip; @@ -5426,7 +5413,7 @@ static u8 fuzz_one(char** argv) { for (i = 0; i < len - 1; i++) { - /* Let's consult the effector map... */ + /* 让我们参考效应器映射... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { stage_max--; @@ -5452,7 +5439,7 @@ static u8 fuzz_one(char** argv) { if (len < 4) goto skip_bitflip; - /* Four walking bytes. */ + /* 四字节翻转。 */ stage_name = "bitflip 32/8"; stage_short = "flip32"; @@ -5463,7 +5450,7 @@ static u8 fuzz_one(char** argv) { for (i = 0; i < len - 3; i++) { - /* Let's consult the effector map... */ + /* 让我们参考效应器映射... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) { stage_max--; @@ -5494,7 +5481,7 @@ skip_bitflip: * ARITHMETIC INC/DEC * **********************/ - /* 8-bit arithmetics. */ + /* 8 位算术运算。 */ stage_name = "arith 8/8"; stage_short = "arith8"; @@ -5509,7 +5496,7 @@ skip_bitflip: u8 orig = out_buf[i]; - /* Let's consult the effector map... */ + /* 让我们参考效应器映射... */ if (!eff_map[EFF_APOS(i)]) { stage_max -= 2 * ARITH_MAX; @@ -5522,8 +5509,7 @@ skip_bitflip: u8 r = orig ^ (orig + j); - /* Do arithmetic operations only if the result couldn't be a product - of a bitflip. */ + /* 仅当结果不可能是位翻转的产物时才执行算术运算。 */ if (!could_be_bitflip(r)) { @@ -5558,7 +5544,7 @@ skip_bitflip: stage_finds[STAGE_ARITH8] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_ARITH8] += stage_max; - /* 16-bit arithmetics, both endians. */ + /* 16 位算术运算,两种字节序。 */ if (len < 2) goto skip_arith; @@ -5573,7 +5559,7 @@ skip_bitflip: u16 orig = *(u16*)(out_buf + i); - /* Let's consult the effector map... */ + /* 让我们参考效应器映射... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { stage_max -= 4 * ARITH_MAX; @@ -5589,10 +5575,8 @@ skip_bitflip: r3 = orig ^ SWAP16(SWAP16(orig) + j), r4 = orig ^ SWAP16(SWAP16(orig) - j); - /* Try little endian addition and subtraction first. Do it only - if the operation would affect more than one byte (hence the - & 0xff overflow checks) and if it couldn't be a product of - a bitflip. */ + /* 首先尝试小端加法和减法。仅当操作会影响多个字节时才执行 + (因此进行 & 0xff 溢出检查),并且如果它不可能是位翻转的产物。 */ stage_val_type = STAGE_VAL_LE; @@ -5616,7 +5600,7 @@ skip_bitflip: } else stage_max--; - /* Big endian comes next. Same deal. */ + /* 接下来是大端。同样的处理。 */ stage_val_type = STAGE_VAL_BE; @@ -5652,7 +5636,7 @@ skip_bitflip: stage_finds[STAGE_ARITH16] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_ARITH16] += stage_max; - /* 32-bit arithmetics, both endians. */ + /* 32 位算术运算,两种字节序。 */ if (len < 4) goto skip_arith; @@ -5667,7 +5651,7 @@ skip_bitflip: u32 orig = *(u32*)(out_buf + i); - /* Let's consult the effector map... */ + /* 让我们参考效应器映射... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) { @@ -5684,8 +5668,7 @@ skip_bitflip: r3 = orig ^ SWAP32(SWAP32(orig) + j), r4 = orig ^ SWAP32(SWAP32(orig) - j); - /* Little endian first. Same deal as with 16-bit: we only want to - try if the operation would have effect on more than two bytes. */ + /* 首先是小端。与 16 位相同:我们只希望在操作会影响超过两个字节时尝试。 */ stage_val_type = STAGE_VAL_LE; @@ -5709,7 +5692,7 @@ skip_bitflip: } else stage_max--; - /* Big endian next. */ + /* 接下来是大端。 */ stage_val_type = STAGE_VAL_BE; @@ -5759,13 +5742,13 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - /* Setting 8-bit integers. */ + /* 设置 8 位整数。 */ for (i = 0; i < len; i++) { u8 orig = out_buf[i]; - /* Let's consult the effector map... */ + /* 让我们参考效应器映射... */ if (!eff_map[EFF_APOS(i)]) { stage_max -= sizeof(interesting_8); @@ -5776,7 +5759,7 @@ skip_arith: for (j = 0; j < sizeof(interesting_8); j++) { - /* Skip if the value could be a product of bitflips or arithmetics. */ + /* 如果该值可能是位翻转或算术运算的产物,则跳过。 */ if (could_be_bitflip(orig ^ (u8)interesting_8[j]) || could_be_arith(orig, (u8)interesting_8[j], 1)) { @@ -5801,7 +5784,7 @@ skip_arith: stage_finds[STAGE_INTEREST8] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_INTEREST8] += stage_max; - /* Setting 16-bit integers, both endians. */ + /* 设置 16 位整数,两种字节序。 */ if (no_arith || len < 2) goto skip_interest; @@ -5816,7 +5799,7 @@ skip_arith: u16 orig = *(u16*)(out_buf + i); - /* Let's consult the effector map... */ + /* 让我们参考效应器映射... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { stage_max -= sizeof(interesting_16); @@ -5829,8 +5812,7 @@ skip_arith: stage_cur_val = interesting_16[j]; - /* Skip if this could be a product of a bitflip, arithmetics, - or single-byte interesting value insertion. */ + /* 如果这可能是位翻转、算术运算或单字节有趣值插入的产物,则跳过。 */ if (!could_be_bitflip(orig ^ (u16)interesting_16[j]) && !could_be_arith(orig, (u16)interesting_16[j], 2) && @@ -5871,7 +5853,7 @@ skip_arith: if (len < 4) goto skip_interest; - /* Setting 32-bit integers, both endians. */ + /* 设置 32 位整数,两种字节序。 */ stage_name = "interest 32/8"; stage_short = "int32"; @@ -5884,7 +5866,7 @@ skip_arith: u32 orig = *(u32*)(out_buf + i); - /* Let's consult the effector map... */ + /* 让我们参考效应器映射... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) { @@ -5898,8 +5880,7 @@ skip_arith: stage_cur_val = interesting_32[j]; - /* Skip if this could be a product of a bitflip, arithmetics, - or word interesting value insertion. */ + /* 如果这可能是位翻转、算术运算或字有趣值插入的产物,则跳过。 */ if (!could_be_bitflip(orig ^ (u32)interesting_32[j]) && !could_be_arith(orig, interesting_32[j], 4) && @@ -5946,7 +5927,7 @@ skip_interest: if (!extras_cnt) goto skip_user_extras; - /* Overwrite with user-supplied extras. */ + /* 使用用户提供的额外内容覆盖。 */ stage_name = "user extras (over)"; stage_short = "ext_UO"; @@ -5963,17 +5944,14 @@ skip_interest: stage_cur_byte = i; - /* Extras are sorted by size, from smallest to largest. This means - that we don't have to worry about restoring the buffer in - between writes at a particular offset determined by the outer - loop. */ + /* 额外内容按大小排序,从最小到最大。这意味着我们不必担心在 + 外部循环确定的特定偏移量之间恢复缓冲区。 */ for (j = 0; j < extras_cnt; j++) { - /* Skip extras probabilistically if extras_cnt > MAX_DET_EXTRAS. Also - skip them if there's no room to insert the payload, if the token - is redundant, or if its entire span has no bytes set in the effector - map. */ + /* 如果 extras_cnt > MAX_DET_EXTRAS,则以概率跳过额外内容。如果 + 没有空间插入有效载荷,如果标记是冗余的,或者如果其整个跨度在效应器映射中 + 没有设置任何字节,则也跳过它们。 */ if ((extras_cnt > MAX_DET_EXTRAS && UR(extras_cnt) >= MAX_DET_EXTRAS) || extras[j].len > len - i || @@ -5994,7 +5972,7 @@ skip_interest: } - /* Restore all the clobbered memory. */ + /* 恢复所有被破坏的内存。 */ memcpy(out_buf + i, in_buf + i, last_len); } @@ -6004,7 +5982,7 @@ skip_interest: stage_finds[STAGE_EXTRAS_UO] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_EXTRAS_UO] += stage_max; - /* Insertion of user-supplied extras. */ + /* 插入用户提供的额外内容。 */ stage_name = "user extras (insert)"; stage_short = "ext_UI"; @@ -6026,10 +6004,10 @@ skip_interest: continue; } - /* Insert token */ + /* 插入标记 */ memcpy(ex_tmp + i, extras[j].data, extras[j].len); - /* Copy tail */ + /* 复制尾部 */ memcpy(ex_tmp + i + extras[j].len, out_buf + i, len - i); if (common_fuzz_stuff(argv, ex_tmp, len + extras[j].len)) { @@ -6041,7 +6019,7 @@ skip_interest: } - /* Copy head */ + /* 复制头部 */ ex_tmp[i] = out_buf[i]; } @@ -6074,7 +6052,7 @@ skip_user_extras: for (j = 0; j < MIN(a_extras_cnt, USE_AUTO_EXTRAS); j++) { - /* See the comment in the earlier code; extras are sorted by size. */ + /* 参见前面的代码中的注释;额外内容按大小排序。 */ if (a_extras[j].len > len - i || !memcmp(a_extras[j].data, out_buf + i, a_extras[j].len) || @@ -6094,7 +6072,7 @@ skip_user_extras: } - /* Restore all the clobbered memory. */ + /* 恢复所有被破坏的内存。 */ memcpy(out_buf + i, in_buf + i, last_len); } @@ -6106,9 +6084,8 @@ skip_user_extras: skip_extras: - /* If we made this to here without jumping to havoc_stage or abandon_entry, - we're properly done with deterministic steps and can mark it as such - in the .state/ directory. */ + /* 如果我们没有跳转到 havoc_stage 或 abandon_entry, + 我们已经正确地完成了确定性步骤,可以在 .state/ 目录中将其标记为已完成。 */ if (!queue_cur->passed_det) mark_as_det_done(queue_cur); @@ -6120,8 +6097,8 @@ havoc_stage: stage_cur_byte = -1; - /* The havoc stage mutation code is also invoked when splicing files; if the - splice_cycle variable is set, generate different descriptions and such. */ + /* havoc 阶段的变异代码也在拼接文件时调用;如果 splice_cycle 变量被设置, + 则生成不同的描述等。 */ if (!splice_cycle) { @@ -6151,8 +6128,8 @@ havoc_stage: havoc_queued = queued_paths; - /* We essentially just do several thousand runs (depending on perf_score) - where we take the input file and make random stacked tweaks. */ + /* 我们基本上只是进行数千次运行(取决于 perf_score), + 在这些运行中,我们对输入文件进行随机堆叠的调整。 */ for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { @@ -6166,21 +6143,21 @@ havoc_stage: case 0: - /* Flip a single bit somewhere. Spooky! */ + /* 翻转某个地方的单个位。 */ FLIP_BIT(out_buf, UR(temp_len << 3)); break; case 1: - /* Set byte to interesting value. */ + /* 将字节设置为有趣的值。 */ out_buf[UR(temp_len)] = interesting_8[UR(sizeof(interesting_8))]; break; case 2: - /* Set word to interesting value, randomly choosing endian. */ + /* 将字设置为有趣的值,随机选择字节序。 */ if (temp_len < 2) break; @@ -6200,7 +6177,7 @@ havoc_stage: case 3: - /* Set dword to interesting value, randomly choosing endian. */ + /* 将双字设置为有趣的值,随机选择字节序。 */ if (temp_len < 4) break; @@ -6220,21 +6197,21 @@ havoc_stage: case 4: - /* Randomly subtract from byte. */ + /* 随机从字节中减去。 */ out_buf[UR(temp_len)] -= 1 + UR(ARITH_MAX); break; case 5: - /* Randomly add to byte. */ + /* 随机向字节中添加。 */ out_buf[UR(temp_len)] += 1 + UR(ARITH_MAX); break; case 6: - /* Randomly subtract from word, random endian. */ + /* 随机从字中减去,随机字节序。 */ if (temp_len < 2) break; @@ -6258,7 +6235,7 @@ havoc_stage: case 7: - /* Randomly add to word, random endian. */ + /* 随机向字中添加,随机字节序。 */ if (temp_len < 2) break; @@ -6282,7 +6259,7 @@ havoc_stage: case 8: - /* Randomly subtract from dword, random endian. */ + /* 随机从双字中减去,随机字节序。 */ if (temp_len < 4) break; @@ -6306,7 +6283,7 @@ havoc_stage: case 9: - /* Randomly add to dword, random endian. */ + /* 随机向双字中添加,随机字节序。 */ if (temp_len < 4) break; @@ -6330,24 +6307,22 @@ havoc_stage: case 10: - /* Just set a random byte to a random value. Because, - why not. We use XOR with 1-255 to eliminate the - possibility of a no-op. */ + /* 只是将一个随机字节设置为一个随机值。因为, + 为什么不呢。我们使用 XOR 1-255 来消除无操作的可能性。 */ out_buf[UR(temp_len)] ^= 1 + UR(255); break; case 11 ... 12: { - /* Delete bytes. We're making this a bit more likely - than insertion (the next option) in hopes of keeping - files reasonably small. */ + /* 删除字节。我们使这种情况比插入(下一个选项)更有可能, + 以希望保持文件大小合理。 */ u32 del_from, del_len; if (temp_len < 2) break; - /* Don't delete too much. */ + /* 不要删除太多。 */ del_len = choose_block_len(temp_len - 1); @@ -6366,7 +6341,7 @@ havoc_stage: if (temp_len + HAVOC_BLK_XL < MAX_FILE) { - /* Clone bytes (75%) or insert a block of constant bytes (25%). */ + /* 克隆字节(75%)或插入一个常量字节块(25%)。 */ u8 actually_clone = UR(4); u32 clone_from, clone_to, clone_len; @@ -6388,11 +6363,11 @@ havoc_stage: new_buf = ck_alloc_nozero(temp_len + clone_len); - /* Head */ + /* 头部 */ memcpy(new_buf, out_buf, clone_to); - /* Inserted part */ + /* 插入部分 */ if (actually_clone) memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); @@ -6400,7 +6375,7 @@ havoc_stage: memset(new_buf + clone_to, UR(2) ? UR(256) : out_buf[UR(temp_len)], clone_len); - /* Tail */ + /* 尾部 */ memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); @@ -6414,8 +6389,7 @@ havoc_stage: case 14: { - /* Overwrite bytes with a randomly selected chunk (75%) or fixed - bytes (25%). */ + /* 用随机选择的块(75%)或固定字节(25%)覆盖字节。 */ u32 copy_from, copy_to, copy_len; @@ -6438,17 +6412,15 @@ havoc_stage: } - /* Values 15 and 16 can be selected only if there are any extras - present in the dictionaries. */ + /* 值 15 和 16 只有在字典中存在任何额外内容时才能被选择。 */ case 15: { - /* Overwrite bytes with an extra. */ + /* 用额外内容覆盖字节。 */ if (!extras_cnt || (a_extras_cnt && UR(2))) { - /* No user-specified extras or odds in our favor. Let's use an - auto-detected one. */ + /* 没有用户指定的额外内容或概率对我们有利。让我们使用自动检测到的额外内容。 */ u32 use_extra = UR(a_extras_cnt); u32 extra_len = a_extras[use_extra].len; @@ -6461,7 +6433,7 @@ havoc_stage: } else { - /* No auto extras or odds in our favor. Use the dictionary. */ + /* 没有自动额外内容或概率对我们有利。使用字典。 */ u32 use_extra = UR(extras_cnt); u32 extra_len = extras[use_extra].len; @@ -6483,8 +6455,7 @@ havoc_stage: u32 use_extra, extra_len, insert_at = UR(temp_len + 1); u8* new_buf; - /* Insert an extra. Do the same dice-rolling stuff as for the - previous case. */ + /* 插入一个额外内容。执行与上一种情况相同的掷骰子操作。 */ if (!extras_cnt || (a_extras_cnt && UR(2))) { @@ -6495,10 +6466,10 @@ havoc_stage: new_buf = ck_alloc_nozero(temp_len + extra_len); - /* Head */ + /* 头部 */ memcpy(new_buf, out_buf, insert_at); - /* Inserted part */ + /* 插入部分 */ memcpy(new_buf + insert_at, a_extras[use_extra].data, extra_len); } else { @@ -6510,15 +6481,15 @@ havoc_stage: new_buf = ck_alloc_nozero(temp_len + extra_len); - /* Head */ + /* 头部 */ memcpy(new_buf, out_buf, insert_at); - /* Inserted part */ + /* 插入部分 */ memcpy(new_buf + insert_at, extras[use_extra].data, extra_len); } - /* Tail */ + /* 尾部 */ memcpy(new_buf + insert_at + extra_len, out_buf + insert_at, temp_len - insert_at); @@ -6537,15 +6508,13 @@ havoc_stage: if (common_fuzz_stuff(argv, out_buf, temp_len)) goto abandon_entry; - /* out_buf might have been mangled a bit, so let's restore it to its - original size and shape. */ + /* out_buf 可能已经被稍微破坏,因此让我们将其恢复到原始大小和形状。 */ if (temp_len < len) out_buf = ck_realloc(out_buf, len); temp_len = len; memcpy(out_buf, in_buf, len); - /* If we're finding new stuff, let's run for a bit longer, limits - permitting. */ + /* 如果我们发现了新的东西,让我们运行更长时间,限制允许。 */ if (queued_paths != havoc_queued) { @@ -6576,10 +6545,9 @@ havoc_stage: * SPLICING * ************/ - /* This is a last-resort strategy triggered by a full round with no findings. - It takes the current input file, randomly selects another input, and - splices them together at some offset, then relies on the havoc - code to mutate that blob. */ + /* 这是一个在完整轮次中没有发现任何东西时触发的最后手段策略。 + 它获取当前输入文件,随机选择另一个输入,并将它们在某个偏移处拼接在一起, + 然后依赖 havoc 代码来变异该 blob。 */ retry_splicing: @@ -6591,8 +6559,7 @@ retry_splicing: u8* new_buf; s32 f_diff, l_diff; - /* First of all, if we've modified in_buf for havoc, let's clean that - up... */ + /* 首先,如果我们为 havoc 修改了 in_buf,让我们清理一下... */ if (in_buf != orig_in) { ck_free(in_buf); @@ -6600,7 +6567,7 @@ retry_splicing: len = queue_cur->len; } - /* Pick a random queue entry and seek to it. Don't splice with yourself. */ + /* 选择一个随机队列条目并跳转到它。不要与自己拼接。 */ do { tid = UR(queued_paths); } while (tid == current_entry); @@ -6610,7 +6577,7 @@ retry_splicing: while (tid >= 100) { target = target->next_100; tid -= 100; } while (tid--) target = target->next; - /* Make sure that the target has a reasonable length. */ + /* 确保目标具有合理的长度。 */ while (target && (target->len < 2 || target == queue_cur)) { target = target->next; @@ -6619,7 +6586,7 @@ retry_splicing: if (!target) goto retry_splicing; - /* Read the testcase into a new buffer. */ + /* 将测试用例读入新缓冲区。 */ fd = open(target->fname, O_RDONLY); @@ -6631,9 +6598,8 @@ retry_splicing: close(fd); - /* Find a suitable splicing location, somewhere between the first and - the last differing byte. Bail out if the difference is just a single - byte or so. */ + /* 找到一个合适的拼接位置,位于第一个和最后一个不同字节之间的某个地方。 + 如果差异只是一个字节左右,则放弃。 */ locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); @@ -6642,11 +6608,11 @@ retry_splicing: goto retry_splicing; } - /* Split somewhere between the first and last differing byte. */ + /* 在第一个和最后一个不同字节之间的某个地方拆分。 */ split_at = f_diff + UR(l_diff - f_diff); - /* Do the thing. */ + /* 执行操作。 */ len = target->len; memcpy(new_buf, in_buf, split_at); @@ -6668,8 +6634,7 @@ abandon_entry: splicing_with = -1; - /* Update pending_not_fuzzed count if we made it through the calibration - cycle and have not seen this entry before. */ + /* 如果我们通过了校准周期并且之前没有见过此条目,则更新 pending_not_fuzzed 计数。 */ if (!stop_soon && !queue_cur->cal_failed && !queue_cur->was_fuzzed) { queue_cur->was_fuzzed = 1; @@ -6688,142 +6653,140 @@ abandon_entry: #undef FLIP_BIT } - - /* Grab interesting test cases from other fuzzers. */ static void sync_fuzzers(char** argv) { - DIR* sd; - struct dirent* sd_ent; - u32 sync_cnt = 0; + DIR* sd; // 目录指针,用于遍历同步目录 + struct dirent* sd_ent; // 目录条目结构体,用于存储目录中的文件信息 + u32 sync_cnt = 0; // 同步计数器,记录同步的次数 - sd = opendir(sync_dir); - if (!sd) PFATAL("Unable to open '%s'", sync_dir); + sd = opendir(sync_dir); // 打开同步目录 + if (!sd) PFATAL("Unable to open '%s'", sync_dir); // 如果打开失败,输出错误信息并终止程序 - stage_max = stage_cur = 0; - cur_depth = 0; + stage_max = stage_cur = 0; // 初始化当前阶段和最大阶段的计数器 + cur_depth = 0; // 初始化当前深度 /* Look at the entries created for every other fuzzer in the sync directory. */ - while ((sd_ent = readdir(sd))) { + while ((sd_ent = readdir(sd))) { // 遍历同步目录中的每一个条目 - static u8 stage_tmp[128]; + static u8 stage_tmp[128]; // 临时缓冲区,用于存储阶段名称 - DIR* qd; - struct dirent* qd_ent; - u8 *qd_path, *qd_synced_path; - u32 min_accept = 0, next_min_accept; + DIR* qd; // 目录指针,用于遍历队列目录 + struct dirent* qd_ent; // 目录条目结构体,用于存储队列目录中的文件信息 + u8 *qd_path, *qd_synced_path; // 队列目录路径和同步路径 + u32 min_accept = 0, next_min_accept; // 最小接受值和下一个最小接受值 - s32 id_fd; + s32 id_fd; // 文件描述符,用于读取和写入同步ID /* Skip dot files and our own output directory. */ - if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue; + if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue; // 跳过隐藏文件和当前fuzzer的输出目录 /* Skip anything that doesn't have a queue/ subdirectory. */ - qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name); + qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name); // 构造队列目录路径 - if (!(qd = opendir(qd_path))) { - ck_free(qd_path); + if (!(qd = opendir(qd_path))) { // 打开队列目录 + ck_free(qd_path); // 如果打开失败,释放路径内存并继续 continue; } /* Retrieve the ID of the last seen test case. */ - qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name); + qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name); // 构造同步路径 - id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600); + id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600); // 打开或创建同步文件 - if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path); + if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path); // 如果打开失败,输出错误信息并终止程序 - if (read(id_fd, &min_accept, sizeof(u32)) > 0) - lseek(id_fd, 0, SEEK_SET); + if (read(id_fd, &min_accept, sizeof(u32)) > 0) // 读取最小接受值 + lseek(id_fd, 0, SEEK_SET); // 将文件指针重置到文件开头 - next_min_accept = min_accept; + next_min_accept = min_accept; // 初始化下一个最小接受值 /* Show stats */ - sprintf(stage_tmp, "sync %u", ++sync_cnt); - stage_name = stage_tmp; - stage_cur = 0; - stage_max = 0; + sprintf(stage_tmp, "sync %u", ++sync_cnt); // 构造阶段名称 + stage_name = stage_tmp; // 设置当前阶段名称 + stage_cur = 0; // 初始化当前阶段计数器 + stage_max = 0; // 初始化最大阶段计数器 /* For every file queued by this fuzzer, parse ID and see if we have looked at it before; exec a test case if not. */ - while ((qd_ent = readdir(qd))) { + while ((qd_ent = readdir(qd))) { // 遍历队列目录中的每一个条目 - u8* path; - s32 fd; - struct stat st; + u8* path; // 文件路径 + s32 fd; // 文件描述符 + struct stat st; // 文件状态结构体 - if (qd_ent->d_name[0] == '.' || - sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || - syncing_case < min_accept) continue; + if (qd_ent->d_name[0] == '.' || // 跳过隐藏文件 + sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || // 解析文件名中的测试用例ID + syncing_case < min_accept) continue; // 如果测试用例ID小于最小接受值,跳过 /* OK, sounds like a new one. Let's give it a try. */ - if (syncing_case >= next_min_accept) - next_min_accept = syncing_case + 1; + if (syncing_case >= next_min_accept) // 如果测试用例ID大于等于下一个最小接受值 + next_min_accept = syncing_case + 1; // 更新下一个最小接受值 - path = alloc_printf("%s/%s", qd_path, qd_ent->d_name); + path = alloc_printf("%s/%s", qd_path, qd_ent->d_name); // 构造文件路径 /* Allow this to fail in case the other fuzzer is resuming or so... */ - fd = open(path, O_RDONLY); + fd = open(path, O_RDONLY); // 打开文件 - if (fd < 0) { - ck_free(path); + if (fd < 0) { // 如果打开失败 + ck_free(path); // 释放路径内存并继续 continue; } - if (fstat(fd, &st)) PFATAL("fstat() failed"); + if (fstat(fd, &st)) PFATAL("fstat() failed"); // 获取文件状态,如果失败则输出错误信息并终止程序 /* Ignore zero-sized or oversized files. */ - if (st.st_size && st.st_size <= MAX_FILE) { + if (st.st_size && st.st_size <= MAX_FILE) { // 如果文件大小在合理范围内 - u8 fault; - u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + u8 fault; // 错误类型 + u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); // 将文件映射到内存 - if (mem == MAP_FAILED) PFATAL("Unable to mmap '%s'", path); + if (mem == MAP_FAILED) PFATAL("Unable to mmap '%s'", path); // 如果映射失败,输出错误信息并终止程序 /* See what happens. We rely on save_if_interesting() to catch major errors and save the test case. */ - write_to_testcase(mem, st.st_size); + write_to_testcase(mem, st.st_size); // 将内存中的数据写入测试用例 - fault = run_target(argv, exec_tmout); + fault = run_target(argv, exec_tmout); // 运行目标程序 - if (stop_soon) return; + if (stop_soon) return; // 如果收到停止信号,返回 - syncing_party = sd_ent->d_name; - queued_imported += save_if_interesting(argv, mem, st.st_size, fault); - syncing_party = 0; + syncing_party = sd_ent->d_name; // 设置同步方名称 + queued_imported += save_if_interesting(argv, mem, st.st_size, fault); // 保存有趣的测试用例 + syncing_party = 0; // 重置同步方名称 - munmap(mem, st.st_size); + munmap(mem, st.st_size); // 解除内存映射 - if (!(stage_cur++ % stats_update_freq)) show_stats(); + if (!(stage_cur++ % stats_update_freq)) show_stats(); // 定期显示统计信息 } - ck_free(path); - close(fd); + ck_free(path); // 释放路径内存 + close(fd); // 关闭文件 } - ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); + ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); // 将下一个最小接受值写入同步文件 - close(id_fd); - closedir(qd); - ck_free(qd_path); - ck_free(qd_synced_path); + close(id_fd); // 关闭文件描述符 + closedir(qd); // 关闭队列目录 + ck_free(qd_path); // 释放队列目录路径内存 + ck_free(qd_synced_path); // 释放同步路径内存 } - closedir(sd); + closedir(sd); // 关闭同步目录 } @@ -6832,10 +6795,10 @@ static void sync_fuzzers(char** argv) { static void handle_stop_sig(int sig) { - stop_soon = 1; + stop_soon = 1; // 设置停止标志 - if (child_pid > 0) kill(child_pid, SIGKILL); - if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); + if (child_pid > 0) kill(child_pid, SIGKILL); // 杀死子进程 + if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); // 杀死forkserver进程 } @@ -6844,7 +6807,7 @@ static void handle_stop_sig(int sig) { static void handle_skipreq(int sig) { - skip_requested = 1; + skip_requested = 1; // 设置跳过请求标志 } @@ -6852,15 +6815,15 @@ static void handle_skipreq(int sig) { static void handle_timeout(int sig) { - if (child_pid > 0) { + if (child_pid > 0) { // 如果有子进程 - child_timed_out = 1; - kill(child_pid, SIGKILL); + child_timed_out = 1; // 设置子进程超时标志 + kill(child_pid, SIGKILL); // 杀死子进程 - } else if (child_pid == -1 && forksrv_pid > 0) { + } else if (child_pid == -1 && forksrv_pid > 0) { // 如果子进程ID为-1且有forkserver进程 - child_timed_out = 1; - kill(forksrv_pid, SIGKILL); + child_timed_out = 1; // 设置子进程超时标志 + kill(forksrv_pid, SIGKILL); // 杀死forkserver进程 } @@ -6873,76 +6836,76 @@ static void handle_timeout(int sig) { EXP_ST void check_binary(u8* fname) { - u8* env_path = 0; - struct stat st; + u8* env_path = 0; // 环境变量PATH + struct stat st; // 文件状态结构体 - s32 fd; - u8* f_data; - u32 f_len = 0; + s32 fd; // 文件描述符 + u8* f_data; // 文件数据 + u32 f_len = 0; // 文件长度 - ACTF("Validating target binary..."); + ACTF("Validating target binary..."); // 输出验证目标二进制文件的信息 - if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { + if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { // 如果文件名包含路径或没有PATH环境变量 - target_path = ck_strdup(fname); - if (stat(target_path, &st) || !S_ISREG(st.st_mode) || - !(st.st_mode & 0111) || (f_len = st.st_size) < 4) - FATAL("Program '%s' not found or not executable", fname); + target_path = ck_strdup(fname); // 复制文件名到目标路径 + if (stat(target_path, &st) || !S_ISREG(st.st_mode) || // 获取文件状态,检查是否为普通文件 + !(st.st_mode & 0111) || (f_len = st.st_size) < 4) // 检查文件是否可执行且大小大于4字节 + FATAL("Program '%s' not found or not executable", fname); // 如果检查失败,输出错误信息并终止程序 } else { - while (env_path) { + while (env_path) { // 遍历PATH环境变量 - u8 *cur_elem, *delim = strchr(env_path, ':'); + u8 *cur_elem, *delim = strchr(env_path, ':'); // 当前路径元素和分隔符 - if (delim) { + if (delim) { // 如果有分隔符 - cur_elem = ck_alloc(delim - env_path + 1); + cur_elem = ck_alloc(delim - env_path + 1); // 分配内存并复制路径元素 memcpy(cur_elem, env_path, delim - env_path); delim++; - } else cur_elem = ck_strdup(env_path); + } else cur_elem = ck_strdup(env_path); // 如果没有分隔符,复制整个路径 - env_path = delim; + env_path = delim; // 更新环境变量路径 - if (cur_elem[0]) - target_path = alloc_printf("%s/%s", cur_elem, fname); + if (cur_elem[0]) // 如果当前路径元素不为空 + target_path = alloc_printf("%s/%s", cur_elem, fname); // 构造目标路径 else - target_path = ck_strdup(fname); + target_path = ck_strdup(fname); // 否则直接复制文件名 - ck_free(cur_elem); + ck_free(cur_elem); // 释放当前路径元素内存 - if (!stat(target_path, &st) && S_ISREG(st.st_mode) && - (st.st_mode & 0111) && (f_len = st.st_size) >= 4) break; + if (!stat(target_path, &st) && S_ISREG(st.st_mode) && // 获取文件状态,检查是否为普通文件 + (st.st_mode & 0111) && (f_len = st.st_size) >= 4) break; // 检查文件是否可执行且大小大于4字节 - ck_free(target_path); - target_path = 0; + ck_free(target_path); // 释放目标路径内存 + target_path = 0; // 重置目标路径 } - if (!target_path) FATAL("Program '%s' not found or not executable", fname); + if (!target_path) FATAL("Program '%s' not found or not executable", fname); // 如果没有找到目标路径,输出错误信息并终止程序 } - if (getenv("AFL_SKIP_BIN_CHECK")) return; + if (getenv("AFL_SKIP_BIN_CHECK")) return; // 如果设置了跳过二进制检查的环境变量,返回 /* Check for blatant user errors. */ - if ((!strncmp(target_path, "/tmp/", 5) && !strchr(target_path + 5, '/')) || + if ((!strncmp(target_path, "/tmp/", 5) && !strchr(target_path + 5, '/')) || // 检查目标路径是否在/tmp或/var/tmp目录下 (!strncmp(target_path, "/var/tmp/", 9) && !strchr(target_path + 9, '/'))) - FATAL("Please don't keep binaries in /tmp or /var/tmp"); + FATAL("Please don't keep binaries in /tmp or /var/tmp"); // 如果目标路径在/tmp或/var/tmp目录下,输出错误信息并终止程序 - fd = open(target_path, O_RDONLY); + fd = open(target_path, O_RDONLY); // 打开目标文件 - if (fd < 0) PFATAL("Unable to open '%s'", target_path); + if (fd < 0) PFATAL("Unable to open '%s'", target_path); // 如果打开失败,输出错误信息并终止程序 - f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); + f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); // 将文件映射到内存 - if (f_data == MAP_FAILED) PFATAL("Unable to mmap file '%s'", target_path); + if (f_data == MAP_FAILED) PFATAL("Unable to mmap file '%s'", target_path); // 如果映射失败,输出错误信息并终止程序 - close(fd); + close(fd); // 关闭文件描述符 - if (f_data[0] == '#' && f_data[1] == '!') { + if (f_data[0] == '#' && f_data[1] == '!') { // 检查文件是否为shell脚本 SAYF("\n" cLRD "[-] " cRST "Oops, the target binary looks like a shell script. Some build systems will\n" @@ -6954,24 +6917,24 @@ EXP_ST void check_binary(u8* fname) { " fuzzing process by a factor of 20x or more; it's best to write the wrapper\n" " in a compiled language instead.\n"); - FATAL("Program '%s' is a shell script", target_path); + FATAL("Program '%s' is a shell script", target_path); // 如果文件是shell脚本,输出错误信息并终止程序 } #ifndef __APPLE__ - if (f_data[0] != 0x7f || memcmp(f_data + 1, "ELF", 3)) - FATAL("Program '%s' is not an ELF binary", target_path); + if (f_data[0] != 0x7f || memcmp(f_data + 1, "ELF", 3)) // 检查文件是否为ELF格式 + FATAL("Program '%s' is not an ELF binary", target_path); // 如果文件不是ELF格式,输出错误信息并终止程序 #else - if (f_data[0] != 0xCF || f_data[1] != 0xFA || f_data[2] != 0xED) - FATAL("Program '%s' is not a 64-bit Mach-O binary", target_path); + if (f_data[0] != 0xCF || f_data[1] != 0xFA || f_data[2] != 0xED) // 检查文件是否为64位Mach-O格式 + FATAL("Program '%s' is not a 64-bit Mach-O binary", target_path); // 如果文件不是64位Mach-O格式,输出错误信息并终止程序 #endif /* ^!__APPLE__ */ - if (!qemu_mode && !dumb_mode && - !memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { + if (!qemu_mode && !dumb_mode && // 如果不是QEMU模式且不是dumb模式 + !memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { // 检查文件中是否包含AFL的共享内存环境变量 SAYF("\n" cLRD "[-] " cRST "Looks like the target binary is not instrumented! The fuzzer depends on\n" @@ -6986,52 +6949,52 @@ EXP_ST void check_binary(u8* fname) { " For that, you can use the -n option - but expect much worse results.)\n", doc_path); - FATAL("No instrumentation detected"); + FATAL("No instrumentation detected"); // 如果没有检测到插桩,输出错误信息并终止程序 } - if (qemu_mode && - memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { + if (qemu_mode && // 如果是QEMU模式 + memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { // 检查文件中是否包含AFL的共享内存环境变量 SAYF("\n" cLRD "[-] " cRST "This program appears to be instrumented with afl-gcc, but is being run in\n" " QEMU mode (-Q). This is probably not what you want - this setup will be\n" " slow and offer no practical benefits.\n"); - FATAL("Instrumentation found in -Q mode"); + FATAL("Instrumentation found in -Q mode"); // 如果在QEMU模式下检测到插桩,输出错误信息并终止程序 } - if (memmem(f_data, f_len, "libasan.so", 10) || - memmem(f_data, f_len, "__msan_init", 11)) uses_asan = 1; + if (memmem(f_data, f_len, "libasan.so", 10) || // 检查文件中是否包含libasan.so + memmem(f_data, f_len, "__msan_init", 11)) uses_asan = 1; // 检查文件中是否包含__msan_init,设置uses_asan标志 /* Detect persistent & deferred init signatures in the binary. */ - if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { + if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { // 检查文件中是否包含持久模式签名 - OKF(cPIN "Persistent mode binary detected."); - setenv(PERSIST_ENV_VAR, "1", 1); - persistent_mode = 1; + OKF(cPIN "Persistent mode binary detected."); // 输出持久模式检测到的信息 + setenv(PERSIST_ENV_VAR, "1", 1); // 设置持久模式环境变量 + persistent_mode = 1; // 设置持久模式标志 - } else if (getenv("AFL_PERSISTENT")) { + } else if (getenv("AFL_PERSISTENT")) { // 如果设置了持久模式环境变量 - WARNF("AFL_PERSISTENT is no longer supported and may misbehave!"); + WARNF("AFL_PERSISTENT is no longer supported and may misbehave!"); // 输出警告信息 } - if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { + if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { // 检查文件中是否包含延迟forkserver签名 - OKF(cPIN "Deferred forkserver binary detected."); - setenv(DEFER_ENV_VAR, "1", 1); - deferred_mode = 1; + OKF(cPIN "Deferred forkserver binary detected."); // 输出延迟forkserver检测到的信息 + setenv(DEFER_ENV_VAR, "1", 1); // 设置延迟forkserver环境变量 + deferred_mode = 1; // 设置延迟forkserver标志 - } else if (getenv("AFL_DEFER_FORKSRV")) { + } else if (getenv("AFL_DEFER_FORKSRV")) { // 如果设置了延迟forkserver环境变量 - WARNF("AFL_DEFER_FORKSRV is no longer supported and may misbehave!"); + WARNF("AFL_DEFER_FORKSRV is no longer supported and may misbehave!"); // 输出警告信息 } - if (munmap(f_data, f_len)) PFATAL("unmap() failed"); + if (munmap(f_data, f_len)) PFATAL("unmap() failed"); // 解除内存映射,如果失败则输出错误信息并终止程序 } @@ -7040,26 +7003,26 @@ EXP_ST void check_binary(u8* fname) { static void fix_up_banner(u8* name) { - if (!use_banner) { + if (!use_banner) { // 如果没有设置banner - if (sync_id) { + if (sync_id) { // 如果有同步ID - use_banner = sync_id; + use_banner = sync_id; // 使用同步ID作为banner } else { - u8* trim = strrchr(name, '/'); - if (!trim) use_banner = name; else use_banner = trim + 1; + u8* trim = strrchr(name, '/'); // 查找文件名中的最后一个斜杠 + if (!trim) use_banner = name; else use_banner = trim + 1; // 如果没有斜杠,使用文件名;否则使用斜杠后的部分 } } - if (strlen(use_banner) > 40) { + if (strlen(use_banner) > 40) { // 如果banner长度超过40 - u8* tmp = ck_alloc(44); - sprintf(tmp, "%.40s...", use_banner); - use_banner = tmp; + u8* tmp = ck_alloc(44); // 分配内存 + sprintf(tmp, "%.40s...", use_banner); // 截取前40个字符并添加省略号 + use_banner = tmp; // 更新banner } @@ -7070,19 +7033,19 @@ static void fix_up_banner(u8* name) { static void check_if_tty(void) { - struct winsize ws; + struct winsize ws; // 终端窗口大小结构体 - if (getenv("AFL_NO_UI")) { - OKF("Disabling the UI because AFL_NO_UI is set."); - not_on_tty = 1; + if (getenv("AFL_NO_UI")) { // 如果设置了禁用UI的环境变量 + OKF("Disabling the UI because AFL_NO_UI is set."); // 输出禁用UI的信息 + not_on_tty = 1; // 设置不在终端上的标志 return; } - if (ioctl(1, TIOCGWINSZ, &ws)) { + if (ioctl(1, TIOCGWINSZ, &ws)) { // 获取终端窗口大小 - if (errno == ENOTTY) { - OKF("Looks like we're not running on a tty, so I'll be a bit less verbose."); - not_on_tty = 1; + if (errno == ENOTTY) { // 如果不在终端上 + OKF("Looks like we're not running on a tty, so I'll be a bit less verbose."); // 输出不在终端上的信息 + not_on_tty = 1; // 设置不在终端上的标志 } return; @@ -7095,56 +7058,54 @@ static void check_if_tty(void) { static void check_term_size(void) { - struct winsize ws; + struct winsize ws; // 终端窗口大小结构体 - term_too_small = 0; + term_too_small = 0; // 初始化终端太小标志 - if (ioctl(1, TIOCGWINSZ, &ws)) return; + if (ioctl(1, TIOCGWINSZ, &ws)) return; // 获取终端窗口大小,如果失败则返回 - if (ws.ws_row == 0 && ws.ws_col == 0) return; - if (ws.ws_row < 25 || ws.ws_col < 80) term_too_small = 1; + if (ws.ws_row == 0 && ws.ws_col == 0) return; // 如果窗口大小为0,返回 + if (ws.ws_row < 25 || ws.ws_col < 80) term_too_small = 1; // 如果窗口大小小于25行或80列,设置终端太小标志 } - - /* Display usage hints. */ static void usage(u8* argv0) { - SAYF("\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" + SAYF("\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" // 显示程序用法 "Required parameters:\n\n" - " -i dir - input directory with test cases\n" - " -o dir - output directory for fuzzer findings\n\n" + " -i dir - input directory with test cases\n" // 输入目录,包含测试用例 + " -o dir - output directory for fuzzer findings\n\n" // 输出目录,用于存储fuzzer的发现 "Execution control settings:\n\n" - " -f file - location read by the fuzzed program (stdin)\n" - " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" - " -m megs - memory limit for child process (%u MB)\n" - " -Q - use binary-only instrumentation (QEMU mode)\n\n" - + " -f file - location read by the fuzzed program (stdin)\n" // fuzzed程序读取的文件位置(stdin) + " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" // 每次运行的超时时间(自动调整,50-最大毫秒) + " -m megs - memory limit for child process (%u MB)\n" // 子进程的内存限制(MB) + " -Q - use binary-only instrumentation (QEMU mode)\n\n" // 使用二进制插桩(QEMU模式) + "Fuzzing behavior settings:\n\n" - " -d - quick & dirty mode (skips deterministic steps)\n" - " -n - fuzz without instrumentation (dumb mode)\n" - " -x dir - optional fuzzer dictionary (see README)\n\n" + " -d - quick & dirty mode (skips deterministic steps)\n" // 快速模式(跳过确定性步骤) + " -n - fuzz without instrumentation (dumb mode)\n" // 无插桩的fuzz模式(dumb模式) + " -x dir - optional fuzzer dictionary (see README)\n\n" // 可选的fuzzer字典(参见README) "Other stuff:\n\n" - " -T text - text banner to show on the screen\n" - " -M / -S id - distributed mode (see parallel_fuzzing.txt)\n" - " -C - crash exploration mode (the peruvian rabbit thing)\n" - " -V - show version number and exit\n\n" - " -b cpu_id - bind the fuzzing process to the specified CPU core\n\n" + " -T text - text banner to show on the screen\n" // 在屏幕上显示的文本横幅 + " -M / -S id - distributed mode (see parallel_fuzzing.txt)\n" // 分布式模式(参见parallel_fuzzing.txt) + " -C - crash exploration mode (the peruvian rabbit thing)\n" // 崩溃探索模式 + " -V - show version number and exit\n\n" // 显示版本号并退出 + " -b cpu_id - bind the fuzzing process to the specified CPU core\n\n" // 将fuzzing进程绑定到指定的CPU核心 - "For additional tips, please consult %s/README.\n\n", + "For additional tips, please consult %s/README.\n\n", // 更多提示,请参阅README - argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); + argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); // 参数:程序名、执行超时、内存限制、文档路径 - exit(1); + exit(1); // 退出程序 } @@ -7153,31 +7114,31 @@ static void usage(u8* argv0) { EXP_ST void setup_dirs_fds(void) { - u8* tmp; - s32 fd; + u8* tmp; // 临时字符串指针 + s32 fd; // 文件描述符 - ACTF("Setting up output directories..."); + ACTF("Setting up output directories..."); // 输出设置输出目录的信息 - if (sync_id && mkdir(sync_dir, 0700) && errno != EEXIST) - PFATAL("Unable to create '%s'", sync_dir); + if (sync_id && mkdir(sync_dir, 0700) && errno != EEXIST) // 如果同步ID存在且创建同步目录失败 + PFATAL("Unable to create '%s'", sync_dir); // 输出错误信息并终止程序 - if (mkdir(out_dir, 0700)) { + if (mkdir(out_dir, 0700)) { // 创建输出目录 - if (errno != EEXIST) PFATAL("Unable to create '%s'", out_dir); + if (errno != EEXIST) PFATAL("Unable to create '%s'", out_dir); // 如果创建失败且不是因为目录已存在,输出错误信息并终止程序 - maybe_delete_out_dir(); + maybe_delete_out_dir(); // 可能删除输出目录 } else { - if (in_place_resume) - FATAL("Resume attempted but old output directory not found"); + if (in_place_resume) // 如果是原地恢复模式 + FATAL("Resume attempted but old output directory not found"); // 输出错误信息并终止程序 - out_dir_fd = open(out_dir, O_RDONLY); + out_dir_fd = open(out_dir, O_RDONLY); // 打开输出目录 #ifndef __sun - if (out_dir_fd < 0 || flock(out_dir_fd, LOCK_EX | LOCK_NB)) - PFATAL("Unable to flock() output directory."); + if (out_dir_fd < 0 || flock(out_dir_fd, LOCK_EX | LOCK_NB)) // 如果打开失败或无法锁定目录 + PFATAL("Unable to flock() output directory."); // 输出错误信息并终止程序 #endif /* !__sun */ @@ -7185,89 +7146,89 @@ EXP_ST void setup_dirs_fds(void) { /* Queue directory for any starting & discovered paths. */ - tmp = alloc_printf("%s/queue", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/queue", out_dir); // 构造队列目录路径 + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建队列目录,如果失败则输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 /* Top-level directory for queue metadata used for session resume and related tasks. */ - tmp = alloc_printf("%s/queue/.state/", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/queue/.state/", out_dir); // 构造队列元数据目录路径 + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建队列元数据目录,如果失败则输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 /* Directory for flagging queue entries that went through deterministic fuzzing in the past. */ - tmp = alloc_printf("%s/queue/.state/deterministic_done/", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/queue/.state/deterministic_done/", out_dir); // 构造确定性fuzzing完成标志目录路径 + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建目录,如果失败则输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 /* Directory with the auto-selected dictionary entries. */ - tmp = alloc_printf("%s/queue/.state/auto_extras/", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/queue/.state/auto_extras/", out_dir); // 构造自动选择字典条目目录路径 + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建目录,如果失败则输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 /* The set of paths currently deemed redundant. */ - tmp = alloc_printf("%s/queue/.state/redundant_edges/", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/queue/.state/redundant_edges/", out_dir); // 构造冗余路径目录路径 + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建目录,如果失败则输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 /* The set of paths showing variable behavior. */ - tmp = alloc_printf("%s/queue/.state/variable_behavior/", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/queue/.state/variable_behavior/", out_dir); // 构造可变行为路径目录路径 + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建目录,如果失败则输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 /* Sync directory for keeping track of cooperating fuzzers. */ - if (sync_id) { + if (sync_id) { // 如果有同步ID - tmp = alloc_printf("%s/.synced/", out_dir); + tmp = alloc_printf("%s/.synced/", out_dir); // 构造同步目录路径 - if (mkdir(tmp, 0700) && (!in_place_resume || errno != EEXIST)) - PFATAL("Unable to create '%s'", tmp); + if (mkdir(tmp, 0700) && (!in_place_resume || errno != EEXIST)) // 创建同步目录,如果失败且不是原地恢复模式或目录已存在 + PFATAL("Unable to create '%s'", tmp); // 输出错误信息并终止程序 - ck_free(tmp); + ck_free(tmp); // 释放临时字符串内存 } /* All recorded crashes. */ - tmp = alloc_printf("%s/crashes", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/crashes", out_dir); // 构造崩溃记录目录路径 + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建目录,如果失败则输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 /* All recorded hangs. */ - tmp = alloc_printf("%s/hangs", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/hangs", out_dir); // 构造挂起记录目录路径 + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建目录,如果失败则输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 /* Generally useful file descriptors. */ - dev_null_fd = open("/dev/null", O_RDWR); - if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); + dev_null_fd = open("/dev/null", O_RDWR); // 打开/dev/null + if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); // 如果打开失败,输出错误信息并终止程序 - dev_urandom_fd = open("/dev/urandom", O_RDONLY); - if (dev_urandom_fd < 0) PFATAL("Unable to open /dev/urandom"); + dev_urandom_fd = open("/dev/urandom", O_RDONLY); // 打开/dev/urandom + if (dev_urandom_fd < 0) PFATAL("Unable to open /dev/urandom"); // 如果打开失败,输出错误信息并终止程序 /* Gnuplot output file. */ - tmp = alloc_printf("%s/plot_data", out_dir); - fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + tmp = alloc_printf("%s/plot_data", out_dir); // 构造Gnuplot输出文件路径 + fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); // 打开Gnuplot输出文件 + if (fd < 0) PFATAL("Unable to create '%s'", tmp); // 如果打开失败,输出错误信息并终止程序 + ck_free(tmp); // 释放临时字符串内存 - plot_file = fdopen(fd, "w"); - if (!plot_file) PFATAL("fdopen() failed"); + plot_file = fdopen(fd, "w"); // 将文件描述符转换为文件指针 + if (!plot_file) PFATAL("fdopen() failed"); // 如果转换失败,输出错误信息并终止程序 fprintf(plot_file, "# unix_time, cycles_done, cur_path, paths_total, " "pending_total, pending_favs, map_size, unique_crashes, " - "unique_hangs, max_depth, execs_per_sec\n"); - /* ignore errors */ + "unique_hangs, max_depth, execs_per_sec\n"); // 写入Gnuplot文件头 + /* ignore errors */ // 忽略错误 } @@ -7276,15 +7237,15 @@ EXP_ST void setup_dirs_fds(void) { EXP_ST void setup_stdio_file(void) { - u8* fn = alloc_printf("%s/.cur_input", out_dir); + u8* fn = alloc_printf("%s/.cur_input", out_dir); // 构造当前输入文件路径 - unlink(fn); /* Ignore errors */ + unlink(fn); /* Ignore errors */ // 删除文件,忽略错误 - out_fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600); + out_fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600); // 打开当前输入文件 - if (out_fd < 0) PFATAL("Unable to create '%s'", fn); + if (out_fd < 0) PFATAL("Unable to create '%s'", fn); // 如果打开失败,输出错误信息并终止程序 - ck_free(fn); + ck_free(fn); // 释放临时字符串内存 } @@ -7301,7 +7262,7 @@ static void check_crash_handling(void) { until I get a box to test the code. So, for now, we check for crash reporting the awful way. */ - if (system("launchctl list 2>/dev/null | grep -q '\\.ReportCrash$'")) return; + if (system("launchctl list 2>/dev/null | grep -q '\\.ReportCrash$'")) return; // 检查MacOS X上的崩溃报告守护进程 SAYF("\n" cLRD "[-] " cRST "Whoops, your system is configured to forward crash notifications to an\n" @@ -7315,22 +7276,22 @@ static void check_crash_handling(void) { " launchctl unload -w ${SL}/LaunchAgents/${PL}.plist\n" " sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist\n"); - if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) - FATAL("Crash reporter detected"); + if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) // 如果没有设置忽略崩溃的环境变量 + FATAL("Crash reporter detected"); // 输出错误信息并终止程序 #else /* This is Linux specific, but I don't think there's anything equivalent on *BSD, so we can just let it slide for now. */ - s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY); + s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY); // 打开core_pattern文件 u8 fchar; - if (fd < 0) return; + if (fd < 0) return; // 如果打开失败,返回 - ACTF("Checking core_pattern..."); + ACTF("Checking core_pattern..."); // 输出检查core_pattern的信息 - if (read(fd, &fchar, 1) == 1 && fchar == '|') { + if (read(fd, &fchar, 1) == 1 && fchar == '|') { // 如果core_pattern以管道符开头 SAYF("\n" cLRD "[-] " cRST "Hmm, your system is configured to send core dump notifications to an\n" @@ -7343,12 +7304,12 @@ static void check_crash_handling(void) { " echo core >/proc/sys/kernel/core_pattern\n"); - if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) - FATAL("Pipe at the beginning of 'core_pattern'"); + if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) // 如果没有设置忽略崩溃的环境变量 + FATAL("Pipe at the beginning of 'core_pattern'"); // 输出错误信息并终止程序 } - close(fd); + close(fd); // 关闭文件描述符 #endif /* ^__APPLE__ */ @@ -7359,38 +7320,38 @@ static void check_crash_handling(void) { static void check_cpu_governor(void) { - FILE* f; - u8 tmp[128]; - u64 min = 0, max = 0; + FILE* f; // 文件指针 + u8 tmp[128]; // 临时缓冲区 + u64 min = 0, max = 0; // 最小和最大CPU频率 - if (getenv("AFL_SKIP_CPUFREQ")) return; + if (getenv("AFL_SKIP_CPUFREQ")) return; // 如果设置了跳过CPU频率检查的环境变量,返回 - f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "r"); - if (!f) return; + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "r"); // 打开CPU频率调节器文件 + if (!f) return; // 如果打开失败,返回 - ACTF("Checking CPU scaling governor..."); + ACTF("Checking CPU scaling governor..."); // 输出检查CPU频率调节器的信息 - if (!fgets(tmp, 128, f)) PFATAL("fgets() failed"); + if (!fgets(tmp, 128, f)) PFATAL("fgets() failed"); // 读取CPU频率调节器信息,如果失败则输出错误信息并终止程序 - fclose(f); + fclose(f); // 关闭文件 - if (!strncmp(tmp, "perf", 4)) return; + if (!strncmp(tmp, "perf", 4)) return; // 如果CPU频率调节器是performance模式,返回 - f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", "r"); + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", "r"); // 打开最小CPU频率文件 if (f) { - if (fscanf(f, "%llu", &min) != 1) min = 0; - fclose(f); + if (fscanf(f, "%llu", &min) != 1) min = 0; // 读取最小CPU频率 + fclose(f); // 关闭文件 } - f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "r"); + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "r"); // 打开最大CPU频率文件 if (f) { - if (fscanf(f, "%llu", &max) != 1) max = 0; - fclose(f); + if (fscanf(f, "%llu", &max) != 1) max = 0; // 读取最大CPU频率 + fclose(f); // 关闭文件 } - if (min == max) return; + if (min == max) return; // 如果最小和最大CPU频率相同,返回 SAYF("\n" cLRD "[-] " cRST "Whoops, your system uses on-demand CPU frequency scaling, adjusted\n" @@ -7404,9 +7365,9 @@ static void check_cpu_governor(void) { " You can later go back to the original state by replacing 'performance' with\n" " 'ondemand'. If you don't want to change the settings, set AFL_SKIP_CPUFREQ\n" " to make afl-fuzz skip this check - but expect some performance drop.\n", - min / 1024, max / 1024); + min / 1024, max / 1024); // 输出CPU频率调节器的警告信息 - FATAL("Suboptimal CPU scaling governor"); + FATAL("Suboptimal CPU scaling governor"); // 输出错误信息并终止程序 } @@ -7415,24 +7376,24 @@ static void check_cpu_governor(void) { static void get_core_count(void) { - u32 cur_runnable = 0; + u32 cur_runnable = 0; // 当前可运行的任务数 #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) - size_t s = sizeof(cpu_core_count); + size_t s = sizeof(cpu_core_count); // CPU核心数的大小 /* On *BSD systems, we can just use a sysctl to get the number of CPUs. */ #ifdef __APPLE__ - if (sysctlbyname("hw.logicalcpu", &cpu_core_count, &s, NULL, 0) < 0) - return; + if (sysctlbyname("hw.logicalcpu", &cpu_core_count, &s, NULL, 0) < 0) // 获取逻辑CPU核心数 + return; // 如果获取失败,返回 #else - int s_name[2] = { CTL_HW, HW_NCPU }; + int s_name[2] = { CTL_HW, HW_NCPU }; // sysctl的名称 - if (sysctl(s_name, 2, &cpu_core_count, &s, NULL, 0) < 0) return; + if (sysctl(s_name, 2, &cpu_core_count, &s, NULL, 0) < 0) return; // 获取CPU核心数,如果失败则返回 #endif /* ^__APPLE__ */ @@ -7440,49 +7401,49 @@ static void get_core_count(void) { #ifdef HAVE_AFFINITY - cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN); + cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN); // 获取在线CPU核心数 #else - FILE* f = fopen("/proc/stat", "r"); - u8 tmp[1024]; + FILE* f = fopen("/proc/stat", "r"); // 打开/proc/stat文件 + u8 tmp[1024]; // 临时缓冲区 - if (!f) return; + if (!f) return; // 如果打开失败,返回 - while (fgets(tmp, sizeof(tmp), f)) - if (!strncmp(tmp, "cpu", 3) && isdigit(tmp[3])) cpu_core_count++; + while (fgets(tmp, sizeof(tmp), f)) // 读取/proc/stat文件 + if (!strncmp(tmp, "cpu", 3) && isdigit(tmp[3])) cpu_core_count++; // 统计CPU核心数 - fclose(f); + fclose(f); // 关闭文件 #endif /* ^HAVE_AFFINITY */ #endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */ - if (cpu_core_count > 0) { + if (cpu_core_count > 0) { // 如果CPU核心数大于0 - cur_runnable = (u32)get_runnable_processes(); + cur_runnable = (u32)get_runnable_processes(); // 获取当前可运行的任务数 #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) /* Add ourselves, since the 1-minute average doesn't include that yet. */ - cur_runnable++; + cur_runnable++; // 增加当前任务数 #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ OKF("You have %u CPU core%s and %u runnable tasks (utilization: %0.0f%%).", cpu_core_count, cpu_core_count > 1 ? "s" : "", - cur_runnable, cur_runnable * 100.0 / cpu_core_count); + cur_runnable, cur_runnable * 100.0 / cpu_core_count); // 输出CPU核心数和任务利用率 - if (cpu_core_count > 1) { + if (cpu_core_count > 1) { // 如果CPU核心数大于1 - if (cur_runnable > cpu_core_count * 1.5) { + if (cur_runnable > cpu_core_count * 1.5) { // 如果任务数超过CPU核心数的1.5倍 - WARNF("System under apparent load, performance may be spotty."); + WARNF("System under apparent load, performance may be spotty."); // 输出系统负载警告 - } else if (cur_runnable + 1 <= cpu_core_count) { + } else if (cur_runnable + 1 <= cpu_core_count) { // 如果任务数小于等于CPU核心数 - OKF("Try parallel jobs - see %s/parallel_fuzzing.txt.", doc_path); + OKF("Try parallel jobs - see %s/parallel_fuzzing.txt.", doc_path); // 建议尝试并行任务 } @@ -7490,8 +7451,8 @@ static void get_core_count(void) { } else { - cpu_core_count = 0; - WARNF("Unable to figure out the number of CPU cores."); + cpu_core_count = 0; // 重置CPU核心数 + WARNF("Unable to figure out the number of CPU cores."); // 输出无法获取CPU核心数的警告 } @@ -7502,39 +7463,39 @@ static void get_core_count(void) { static void fix_up_sync(void) { - u8* x = sync_id; + u8* x = sync_id; // 同步ID - if (dumb_mode) - FATAL("-S / -M and -n are mutually exclusive"); + if (dumb_mode) // 如果是dumb模式 + FATAL("-S / -M and -n are mutually exclusive"); // 输出错误信息并终止程序 - if (skip_deterministic) { + if (skip_deterministic) { // 如果跳过了确定性步骤 - if (force_deterministic) - FATAL("use -S instead of -M -d"); + if (force_deterministic) // 如果强制确定性模式 + FATAL("use -S instead of -M -d"); // 输出错误信息并终止程序 else - FATAL("-S already implies -d"); + FATAL("-S already implies -d"); // 输出错误信息并终止程序 } - while (*x) { + while (*x) { // 遍历同步ID - if (!isalnum(*x) && *x != '_' && *x != '-') - FATAL("Non-alphanumeric fuzzer ID specified via -S or -M"); + if (!isalnum(*x) && *x != '_' && *x != '-') // 如果同步ID包含非字母数字字符 + FATAL("Non-alphanumeric fuzzer ID specified via -S or -M"); // 输出错误信息并终止程序 - x++; + x++; // 移动到下一个字符 } - if (strlen(sync_id) > 32) FATAL("Fuzzer ID too long"); + if (strlen(sync_id) > 32) FATAL("Fuzzer ID too long"); // 如果同步ID过长,输出错误信息并终止程序 - x = alloc_printf("%s/%s", out_dir, sync_id); + x = alloc_printf("%s/%s", out_dir, sync_id); // 构造输出目录路径 - sync_dir = out_dir; - out_dir = x; + sync_dir = out_dir; // 设置同步目录 + out_dir = x; // 更新输出目录 - if (!force_deterministic) { - skip_deterministic = 1; - use_splicing = 1; + if (!force_deterministic) { // 如果没有强制确定性模式 + skip_deterministic = 1; // 设置跳过确定性步骤 + use_splicing = 1; // 启用拼接 } } @@ -7543,35 +7504,35 @@ static void fix_up_sync(void) { /* Handle screen resize (SIGWINCH). */ static void handle_resize(int sig) { - clear_screen = 1; + clear_screen = 1; // 设置清屏标志 } /* Check ASAN options. */ static void check_asan_opts(void) { - u8* x = getenv("ASAN_OPTIONS"); + u8* x = getenv("ASAN_OPTIONS"); // 获取ASAN_OPTIONS环境变量 - if (x) { + if (x) { // 如果ASAN_OPTIONS存在 - if (!strstr(x, "abort_on_error=1")) - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + if (!strstr(x, "abort_on_error=1")) // 如果没有设置abort_on_error=1 + FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); // 输出错误信息并终止程序 - if (!strstr(x, "symbolize=0")) - FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + if (!strstr(x, "symbolize=0")) // 如果没有设置symbolize=0 + FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); // 输出错误信息并终止程序 } - x = getenv("MSAN_OPTIONS"); + x = getenv("MSAN_OPTIONS"); // 获取MSAN_OPTIONS环境变量 - if (x) { + if (x) { // 如果MSAN_OPTIONS存在 - if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) // 如果没有设置exit_code FATAL("Custom MSAN_OPTIONS set without exit_code=" - STRINGIFY(MSAN_ERROR) " - please fix!"); + STRINGIFY(MSAN_ERROR) " - please fix!"); // 输出错误信息并终止程序 - if (!strstr(x, "symbolize=0")) - FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + if (!strstr(x, "symbolize=0")) // 如果没有设置symbolize=0 + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); // 输出错误信息并终止程序 } @@ -7582,45 +7543,45 @@ static void check_asan_opts(void) { EXP_ST void detect_file_args(char** argv) { - u32 i = 0; - u8* cwd = getcwd(NULL, 0); + u32 i = 0; // 参数索引 + u8* cwd = getcwd(NULL, 0); // 获取当前工作目录 - if (!cwd) PFATAL("getcwd() failed"); + if (!cwd) PFATAL("getcwd() failed"); // 如果获取失败,输出错误信息并终止程序 - while (argv[i]) { + while (argv[i]) { // 遍历参数 - u8* aa_loc = strstr(argv[i], "@@"); + u8* aa_loc = strstr(argv[i], "@@"); // 查找参数中的@@ - if (aa_loc) { + if (aa_loc) { // 如果找到@@ - u8 *aa_subst, *n_arg; + u8 *aa_subst, *n_arg; // 替换字符串和新参数 /* If we don't have a file name chosen yet, use a safe default. */ - if (!out_file) - out_file = alloc_printf("%s/.cur_input", out_dir); + if (!out_file) // 如果没有输出文件 + out_file = alloc_printf("%s/.cur_input", out_dir); // 使用默认输出文件路径 /* Be sure that we're always using fully-qualified paths. */ - if (out_file[0] == '/') aa_subst = out_file; - else aa_subst = alloc_printf("%s/%s", cwd, out_file); + if (out_file[0] == '/') aa_subst = out_file; // 如果输出文件路径是绝对路径,直接使用 + else aa_subst = alloc_printf("%s/%s", cwd, out_file); // 否则构造绝对路径 /* Construct a replacement argv value. */ - *aa_loc = 0; - n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); - argv[i] = n_arg; - *aa_loc = '@'; + *aa_loc = 0; // 将@@替换为字符串结束符 + n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); // 构造新参数 + argv[i] = n_arg; // 更新参数 + *aa_loc = '@'; // 恢复@@ - if (out_file[0] != '/') ck_free(aa_subst); + if (out_file[0] != '/') ck_free(aa_subst); // 如果输出文件路径不是绝对路径,释放替换字符串内存 } - i++; + i++; // 移动到下一个参数 } - free(cwd); /* not tracked */ + free(cwd); /* not tracked */ // 释放当前工作目录内存 } @@ -7631,41 +7592,41 @@ EXP_ST void detect_file_args(char** argv) { EXP_ST void setup_signal_handlers(void) { - struct sigaction sa; + struct sigaction sa; // 信号处理结构体 - sa.sa_handler = NULL; - sa.sa_flags = SA_RESTART; - sa.sa_sigaction = NULL; + sa.sa_handler = NULL; // 信号处理函数 + sa.sa_flags = SA_RESTART; // 信号处理标志 + sa.sa_sigaction = NULL; // 信号处理函数 - sigemptyset(&sa.sa_mask); + sigemptyset(&sa.sa_mask); // 初始化信号掩码 /* Various ways of saying "stop". */ - sa.sa_handler = handle_stop_sig; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = handle_stop_sig; // 设置停止信号处理函数 + sigaction(SIGHUP, &sa, NULL); // 注册SIGHUP信号处理 + sigaction(SIGINT, &sa, NULL); // 注册SIGINT信号处理 + sigaction(SIGTERM, &sa, NULL); // 注册SIGTERM信号处理 /* Exec timeout notifications. */ - sa.sa_handler = handle_timeout; - sigaction(SIGALRM, &sa, NULL); + sa.sa_handler = handle_timeout; // 设置超时信号处理函数 + sigaction(SIGALRM, &sa, NULL); // 注册SIGALRM信号处理 /* Window resize */ - sa.sa_handler = handle_resize; - sigaction(SIGWINCH, &sa, NULL); + sa.sa_handler = handle_resize; // 设置窗口大小调整信号处理函数 + sigaction(SIGWINCH, &sa, NULL); // 注册SIGWINCH信号处理 /* SIGUSR1: skip entry */ - sa.sa_handler = handle_skipreq; - sigaction(SIGUSR1, &sa, NULL); + sa.sa_handler = handle_skipreq; // 设置跳过请求信号处理函数 + sigaction(SIGUSR1, &sa, NULL); // 注册SIGUSR1信号处理 /* Things we don't care about. */ - sa.sa_handler = SIG_IGN; - sigaction(SIGTSTP, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); + sa.sa_handler = SIG_IGN; // 忽略信号 + sigaction(SIGTSTP, &sa, NULL); // 注册SIGTSTP信号处理 + sigaction(SIGPIPE, &sa, NULL); // 注册SIGPIPE信号处理 } @@ -7674,57 +7635,57 @@ EXP_ST void setup_signal_handlers(void) { static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { - char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); - u8 *tmp, *cp, *rsl, *own_copy; + char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); // 分配新参数数组内存 + u8 *tmp, *cp, *rsl, *own_copy; // 临时字符串指针 /* Workaround for a QEMU stability glitch. */ - setenv("QEMU_LOG", "nochain", 1); + setenv("QEMU_LOG", "nochain", 1); // 设置QEMU日志环境变量 - memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); + memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); // 复制参数 - new_argv[2] = target_path; - new_argv[1] = "--"; + new_argv[2] = target_path; // 设置目标路径 + new_argv[1] = "--"; // 设置参数分隔符 /* Now we need to actually find the QEMU binary to put in argv[0]. */ - tmp = getenv("AFL_PATH"); + tmp = getenv("AFL_PATH"); // 获取AFL路径环境变量 - if (tmp) { + if (tmp) { // 如果AFL路径存在 - cp = alloc_printf("%s/afl-qemu-trace", tmp); + cp = alloc_printf("%s/afl-qemu-trace", tmp); // 构造QEMU二进制路径 - if (access(cp, X_OK)) - FATAL("Unable to find '%s'", tmp); + if (access(cp, X_OK)) // 检查QEMU二进制是否可执行 + FATAL("Unable to find '%s'", tmp); // 如果不可执行,输出错误信息并终止程序 - target_path = new_argv[0] = cp; - return new_argv; + target_path = new_argv[0] = cp; // 设置目标路径和新参数 + return new_argv; // 返回新参数 } - own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); + own_copy = ck_strdup(own_loc); // 复制自身路径 + rsl = strrchr(own_copy, '/'); // 查找最后一个斜杠 - if (rsl) { + if (rsl) { // 如果找到斜杠 - *rsl = 0; + *rsl = 0; // 将斜杠替换为字符串结束符 - cp = alloc_printf("%s/afl-qemu-trace", own_copy); - ck_free(own_copy); + cp = alloc_printf("%s/afl-qemu-trace", own_copy); // 构造QEMU二进制路径 + ck_free(own_copy); // 释放自身路径内存 - if (!access(cp, X_OK)) { + if (!access(cp, X_OK)) { // 检查QEMU二进制是否可执行 - target_path = new_argv[0] = cp; - return new_argv; + target_path = new_argv[0] = cp; // 设置目标路径和新参数 + return new_argv; // 返回新参数 } - } else ck_free(own_copy); + } else ck_free(own_copy); // 释放自身路径内存 - if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { + if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { // 检查默认路径下的QEMU二进制是否可执行 - target_path = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace"); - return new_argv; + target_path = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace"); // 设置目标路径和新参数 + return new_argv; // 返回新参数 } @@ -7738,7 +7699,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { " instrumented at compile time with afl-gcc. It is also possible to use it as a\n" " traditional \"dumb\" fuzzer by specifying '-n' in the command line.\n"); - FATAL("Failed to locate 'afl-qemu-trace'."); + FATAL("Failed to locate 'afl-qemu-trace'."); // 输出错误信息并终止程序 } @@ -7747,26 +7708,26 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { static void save_cmdline(u32 argc, char** argv) { - u32 len = 1, i; - u8* buf; + u32 len = 1, i; // 命令行长度和索引 + u8* buf; // 缓冲区 - for (i = 0; i < argc; i++) - len += strlen(argv[i]) + 1; + for (i = 0; i < argc; i++) // 遍历参数 + len += strlen(argv[i]) + 1; // 计算命令行长度 - buf = orig_cmdline = ck_alloc(len); + buf = orig_cmdline = ck_alloc(len); // 分配命令行内存 - for (i = 0; i < argc; i++) { + for (i = 0; i < argc; i++) { // 遍历参数 - u32 l = strlen(argv[i]); + u32 l = strlen(argv[i]); // 参数长度 - memcpy(buf, argv[i], l); - buf += l; + memcpy(buf, argv[i], l); // 复制参数 + buf += l; // 移动缓冲区指针 - if (i != argc - 1) *(buf++) = ' '; + if (i != argc - 1) *(buf++) = ' '; // 添加空格 } - *buf = 0; + *buf = 0; // 添加字符串结束符 } @@ -7778,18 +7739,18 @@ static void save_cmdline(u32 argc, char** argv) { // 主函数,程序的入口点 int main(int argc, char** argv) { - s32 opt; - u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to; - u8 *extras_dir = 0; - u8 mem_limit_given = 0; - u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); - char** use_argv; + s32 opt; // 命令行选项 + u64 prev_queued = 0; // 上一次队列中的路径数 + u32 sync_interval_cnt = 0, seek_to; // 同步间隔计数和查找位置 + u8 *extras_dir = 0; // 额外目录 + u8 mem_limit_given = 0; // 内存限制是否已设置 + u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); // 是否只运行一次 + char** use_argv; // 使用的参数 - struct timeval tv; - struct timezone tz; + struct timeval tv; // 时间结构体 + struct timezone tz; // 时区结构体 - SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by \n"); + SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by \n"); // 输出程序版本信息 } /*************************************************************** @@ -7797,55 +7758,54 @@ int main(int argc, char** argv) { * 描述: 该段代码负责解析命令行参数,并对输入输出目录、文件、同步ID以及超时等选项进行处理和验证。 ***************************************************************/ - doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; + doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; // 设置文档路径 - gettimeofday(&tv, &tz); - srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); + gettimeofday(&tv, &tz); // 获取当前时间 + srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); // 初始化随机数种子 // argv 处理 - while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0) + while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0) // 解析命令行选项 - switch (opt) { + switch (opt) { // 根据选项进行处理 - case 'i': /* input dir */ + case 'i': /* input dir */ // 输入目录 // 初始 corpus 目录 - if (in_dir) FATAL("Multiple -i options not supported"); - in_dir = optarg; + if (in_dir) FATAL("Multiple -i options not supported"); // 如果输入目录已设置,输出错误信息并终止程序 + in_dir = optarg; // 设置输入目录 // 若使用 "-i -",则表示 in-place resume - if (!strcmp(in_dir, "-")) in_place_resume = 1; + if (!strcmp(in_dir, "-")) in_place_resume = 1; // 如果输入目录为"-",设置原地恢复模式 break; /* 处理输出目录的命令行选项 */ - case 'o': /* output dir */ + case 'o': /* output dir */ // 输出目录 - if (out_dir) FATAL("Multiple -o options not supported"); - out_dir = optarg; + if (out_dir) FATAL("Multiple -o options not supported"); // 如果输出目录已设置,输出错误信息并终止程序 + out_dir = optarg; // 设置输出目录 break; - case 'M': { /* master sync ID */ + case 'M': { /* master sync ID */ // 主同步ID // 处理选项 'M',用于设定主同步 ID - u8* c; + u8* c; // 临时字符指针 - if (sync_id) FATAL("Multiple -S or -M options not supported"); - sync_id = ck_strdup(optarg); + if (sync_id) FATAL("Multiple -S or -M options not supported"); // 如果同步ID已设置,输出错误信息并终止程序 + sync_id = ck_strdup(optarg); // 设置同步ID - if ((c = strchr(sync_id, ':'))) { + if ((c = strchr(sync_id, ':'))) { // 如果同步ID中包含冒号 // 解析同步ID中的主ID和最大ID - *c = 0; + *c = 0; // 将冒号替换为字符串结束符 - if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || - !master_id || !master_max || master_id > master_max || - master_max > 1000000) FATAL("Bogus master ID passed to -M"); + if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || // 解析主ID和最大ID + !master_id || !master_max || master_id > master_max || // 检查主ID和最大ID的合法性 + master_max > 1000000) FATAL("Bogus master ID passed to -M"); // 如果主ID或最大ID不合法,输出错误信息并终止程序 } - force_deterministic = 1; - // 强制使用确定性执行 + force_deterministic = 1; // 强制使用确定性执行 } @@ -7858,378 +7818,378 @@ int main(int argc, char** argv) { ************************************************************************************/ case 'S': - if (sync_id) FATAL("Multiple -S or -M options not supported"); - sync_id = ck_strdup(optarg); + if (sync_id) FATAL("Multiple -S or -M options not supported"); // 如果同步ID已设置,输出错误信息并终止程序 + sync_id = ck_strdup(optarg); // 设置同步ID break; - case 'f': /* target file */ + case 'f': /* target file */ // 目标文件 - if (out_file) FATAL("Multiple -f options not supported"); - out_file = optarg; + if (out_file) FATAL("Multiple -f options not supported"); // 如果目标文件已设置,输出错误信息并终止程序 + out_file = optarg; // 设置目标文件 break; - case 'x': /* dictionary */ + case 'x': /* dictionary */ // 字典 - if (extras_dir) FATAL("Multiple -x options not supported"); - extras_dir = optarg; + if (extras_dir) FATAL("Multiple -x options not supported"); // 如果字典目录已设置,输出错误信息并终止程序 + extras_dir = optarg; // 设置字典目录 break; /************************************** * 处理命令行选项中与超时相关的参数 * 支持-t选项,用于设置执行超时时间 **************************************/ - case 't': { /* timeout */ + case 't': { /* timeout */ // 超时 - u8 suffix = 0; + u8 suffix = 0; // 后缀 - if (timeout_given) FATAL("Multiple -t options not supported"); + if (timeout_given) FATAL("Multiple -t options not supported"); // 如果超时已设置,输出错误信息并终止程序 - if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -t"); + if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 || // 解析超时时间和后缀 + optarg[0] == '-') FATAL("Bad syntax used for -t"); // 如果解析失败,输出错误信息并终止程序 - if (exec_tmout < 5) FATAL("Dangerously low value of -t"); + if (exec_tmout < 5) FATAL("Dangerously low value of -t"); // 如果超时时间小于5毫秒,输出错误信息并终止程序 - if (suffix == '+') timeout_given = 2; else timeout_given = 1; + if (suffix == '+') timeout_given = 2; else timeout_given = 1; // 根据后缀设置超时标志 break; } - case 'm': { /* mem limit */ + case 'm': { /* mem limit */ // 内存限制 // 处理内存限制选项的相关逻辑 - u8 suffix = 'M'; + u8 suffix = 'M'; // 默认后缀为M - if (mem_limit_given) FATAL("Multiple -m options not supported"); - mem_limit_given = 1; + if (mem_limit_given) FATAL("Multiple -m options not supported"); // 如果内存限制已设置,输出错误信息并终止程序 + mem_limit_given = 1; // 设置内存限制标志 - if (!strcmp(optarg, "none")) { + if (!strcmp(optarg, "none")) { // 如果选项参数为"none" // 如果选项参数为"none",则将内存限制设置为0 - mem_limit = 0; - break; + mem_limit = 0; // 设置内存限制为0 + break; // 跳出switch } // 解析命令行参数,获取内存限制和单位后缀 - if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -m"); + if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || // 解析内存限制和单位后缀 + optarg[0] == '-') FATAL("Bad syntax used for -m"); // 如果解析失败,输出错误信息并终止程序 // 根据单位后缀调整内存限制的值 - switch (suffix) { + switch (suffix) { // 根据后缀调整内存限制 - case 'T': mem_limit *= 1024 * 1024; break; - case 'G': mem_limit *= 1024; break; - case 'k': mem_limit /= 1024; break; - case 'M': break; + case 'T': mem_limit *= 1024 * 1024; break; // 如果后缀为T,转换为MB + case 'G': mem_limit *= 1024; break; // 如果后缀为G,转换为MB + case 'k': mem_limit /= 1024; break; // 如果后缀为k,转换为MB + case 'M': break; // 如果后缀为M,直接使用 - default: FATAL("Unsupported suffix or bad syntax for -m"); + default: FATAL("Unsupported suffix or bad syntax for -m"); // 如果后缀不支持,输出错误信息并终止程序 } // 检查内存限制值是否低于安全阈值 - if (mem_limit < 5) FATAL("Dangerously low value of -m"); + if (mem_limit < 5) FATAL("Dangerously low value of -m"); // 如果内存限制小于5MB,输出错误信息并终止程序 // 在32位系统上检查内存限制的范围 - if (sizeof(rlim_t) == 4 && mem_limit > 2000) - FATAL("Value of -m out of range on 32-bit systems"); + if (sizeof(rlim_t) == 4 && mem_limit > 2000) // 如果是32位系统且内存限制大于2000MB + FATAL("Value of -m out of range on 32-bit systems"); // 输出错误信息并终止程序 } break; - case 'b': { /* bind CPU core */ + case 'b': { /* bind CPU core */ // 绑定CPU核心 // 处理 CPU 绑定选项,确保只能使用一次 - if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); - cpu_to_bind_given = 1; + if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); // 如果CPU绑定已设置,输出错误信息并终止程序 + cpu_to_bind_given = 1; // 设置CPU绑定标志 // 解析用户输入的 CPU 核心编号,检查输入合法性 - if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -b"); + if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || // 解析CPU核心编号 + optarg[0] == '-') FATAL("Bad syntax used for -b"); // 如果解析失败,输出错误信息并终止程序 break; } /* 处理命令行参数,选择跳过确定性测试的选项 */ - case 'd': /* skip deterministic */ + case 'd': /* skip deterministic */ // 跳过确定性测试 - if (skip_deterministic) FATAL("Multiple -d options not supported"); - skip_deterministic = 1; - use_splicing = 1; + if (skip_deterministic) FATAL("Multiple -d options not supported"); // 如果跳过确定性测试已设置,输出错误信息并终止程序 + skip_deterministic = 1; // 设置跳过确定性测试标志 + use_splicing = 1; // 启用拼接 break; - case 'B': /* load bitmap */ + case 'B': /* load bitmap */ // 加载位图 /* 该代码段处理加载位图的选项。 位图功能是一个未文档化的选项,适用于在正常模糊测试过程中找到有趣的测试用例后,进行变异而不重新发现先前运行中已找到的测试用例。 使用此模式时,需要将 -B 指向之前运行相同二进制文件生成的 fuzz_bitmap。 该功能仅在少数几次中使用过,因此不做为官方设置。 */ - if (in_bitmap) FATAL("Multiple -B options not supported"); + if (in_bitmap) FATAL("Multiple -B options not supported"); // 如果位图已加载,输出错误信息并终止程序 - in_bitmap = optarg; - read_bitmap(in_bitmap); + in_bitmap = optarg; // 设置位图路径 + read_bitmap(in_bitmap); // 读取位图 break; /* 处理命令行参数选项 'C',用于设置崩溃模式 */ - case 'C': /* crash mode */ + case 'C': /* crash mode */ // 崩溃模式 - if (crash_mode) FATAL("Multiple -C options not supported"); - crash_mode = FAULT_CRASH; + if (crash_mode) FATAL("Multiple -C options not supported"); // 如果崩溃模式已设置,输出错误信息并终止程序 + crash_mode = FAULT_CRASH; // 设置崩溃模式 break; - case 'n': /* dumb mode */ + case 'n': /* dumb mode */ // dumb模式 // 处理-dumb模式的选项,当程序收到'-n'时触发 - if (dumb_mode) FATAL("Multiple -n options not supported"); - if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1; + if (dumb_mode) FATAL("Multiple -n options not supported"); // 如果dumb模式已设置,输出错误信息并终止程序 + if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1; // 根据环境变量设置dumb模式 break; /* 处理命令行选项 */ - case 'T': /* banner */ + case 'T': /* banner */ // 横幅 /* 检查是否支持多个 -T 选项 */ - if (use_banner) FATAL("Multiple -T options not supported"); - use_banner = optarg; + if (use_banner) FATAL("Multiple -T options not supported"); // 如果横幅已设置,输出错误信息并终止程序 + use_banner = optarg; // 设置横幅 break; // 处理QEMU模式的代码段 // 如果启用QEMU模式,检查是否已指定多个-Q选项 // 如果未指定内存限制,则使用默认的QEMU内存限制 // 在处理完成后退出switch语句 - case 'Q': /* QEMU mode */ + case 'Q': /* QEMU mode */ // QEMU模式 - if (qemu_mode) FATAL("Multiple -Q options not supported"); - qemu_mode = 1; + if (qemu_mode) FATAL("Multiple -Q options not supported"); // 如果QEMU模式已设置,输出错误信息并终止程序 + qemu_mode = 1; // 设置QEMU模式 - if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; + if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; // 如果未指定内存限制,使用默认的QEMU内存限制 break; /* 处理选项 'V' 的情况,显示版本号 */ - case 'V': /* Show version number */ + case 'V': /* Show version number */ // 显示版本号 /* Version number has been printed already, just quit. */ - exit(0); + exit(0); // 退出程序 default: - usage(argv[0]); + usage(argv[0]); // 显示用法信息 } // 检查命令行参数以及输入输出目录是否有效,若无效则显示使用方法 - if (optind == argc || !in_dir || !out_dir) usage(argv[0]); + if (optind == argc || !in_dir || !out_dir) usage(argv[0]); // 如果参数不足或输入输出目录未设置,显示用法信息 // 设置信号处理函数 - setup_signal_handlers(); + setup_signal_handlers(); // 设置信号处理函数 // 检查地址卫生选项 - check_asan_opts(); + check_asan_opts(); // 检查ASAN选项 // 处理同步ID和目录检查的逻辑 // 该段代码主要用于验证输入输出目录及模式的有效性 // 在不同的运行模式下进行相应的错误处理 - if (sync_id) fix_up_sync(); + if (sync_id) fix_up_sync(); // 如果有同步ID,修复同步目录 - if (!strcmp(in_dir, out_dir)) - FATAL("Input and output directories can't be the same"); + if (!strcmp(in_dir, out_dir)) // 如果输入目录和输出目录相同 + FATAL("Input and output directories can't be the same"); // 输出错误信息并终止程序 - if (dumb_mode) { + if (dumb_mode) { // 如果是dumb模式 - if (crash_mode) FATAL("-C and -n are mutually exclusive"); - if (qemu_mode) FATAL("-Q and -n are mutually exclusive"); + if (crash_mode) FATAL("-C and -n are mutually exclusive"); // 如果崩溃模式已设置,输出错误信息并终止程序 + if (qemu_mode) FATAL("-Q and -n are mutually exclusive"); // 如果QEMU模式已设置,输出错误信息并终止程序 } // 该代码段用于读取环境变量并根据其值设置相应的标志和超时值 // 主要功能是启动时配置这些参数以调整程序的行为 - if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; - if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; - if (getenv("AFL_NO_ARITH")) no_arith = 1; - if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; - if (getenv("AFL_FAST_CAL")) fast_cal = 1; + if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; // 如果设置了AFL_NO_FORKSRV环境变量,禁用forkserver + if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; // 如果设置了AFL_NO_CPU_RED环境变量,禁用CPU使用率显示 + if (getenv("AFL_NO_ARITH")) no_arith = 1; // 如果设置了AFL_NO_ARITH环境变量,禁用算术操作 + if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; // 如果设置了AFL_SHUFFLE_QUEUE环境变量,启用队列随机化 + if (getenv("AFL_FAST_CAL")) fast_cal = 1; // 如果设置了AFL_FAST_CAL环境变量,启用快速校准 - if (getenv("AFL_HANG_TMOUT")) { - hang_tmout = atoi(getenv("AFL_HANG_TMOUT")); - if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT"); + if (getenv("AFL_HANG_TMOUT")) { // 如果设置了AFL_HANG_TMOUT环境变量 + hang_tmout = atoi(getenv("AFL_HANG_TMOUT")); // 解析挂起超时时间 + if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT"); // 如果解析失败,输出错误信息并终止程序 } // 此函数用于检查环境变量和启动模式的合法性 // 如果在不支持的模式下使用互斥选项,则会引发致命错误 // 处理环境变量以确保正确的库加载 - if (dumb_mode == 2 && no_forkserver) - FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); + if (dumb_mode == 2 && no_forkserver) // 如果是dumb模式且禁用forkserver + FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); // 输出错误信息并终止程序 - if (getenv("AFL_PRELOAD")) { - setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); - setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + if (getenv("AFL_PRELOAD")) { // 如果设置了AFL_PRELOAD环境变量 + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); // 设置LD_PRELOAD环境变量 + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); // 设置DYLD_INSERT_LIBRARIES环境变量 } - if (getenv("AFL_LD_PRELOAD")) - FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); + if (getenv("AFL_LD_PRELOAD")) // 如果设置了AFL_LD_PRELOAD环境变量 + FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); // 输出错误信息并终止程序 - save_cmdline(argc, argv); + save_cmdline(argc, argv); // 保存命令行 - fix_up_banner(argv[optind]); + fix_up_banner(argv[optind]); // 修复横幅 - check_if_tty(); + check_if_tty(); // 检查是否在终端上运行 - get_core_count(); + get_core_count(); // 获取CPU核心数 #ifdef HAVE_AFFINITY // 将当前进程绑定到一个空闲的CPU上,以优化性能 - bind_to_free_cpu(); + bind_to_free_cpu(); // 绑定到空闲CPU #endif /* HAVE_AFFINITY */ /* 检查崩溃处理机制 */ - check_crash_handling(); + check_crash_handling(); // 检查崩溃处理机制 /* 检查CPU调节器设置 */ - check_cpu_governor(); + check_cpu_governor(); // 检查CPU调节器 /* 设置后处理操作 */ - setup_post(); + setup_post(); // 设置后处理 /* 设置共享内存 */ - setup_shm(); + setup_shm(); // 设置共享内存 /* 初始化计数类16 */ - init_count_class16(); + init_count_class16(); // 初始化计数类16 /* 设置目录和文件描述符 */ - setup_dirs_fds(); + setup_dirs_fds(); // 设置目录和文件描述符 /* 读取测试用例 */ - read_testcases(); + read_testcases(); // 读取测试用例 /* 加载自动化设置 */ - load_auto(); + load_auto(); // 加载自动化设置 /* 处理输入的转换 */ - pivot_inputs(); + pivot_inputs(); // 处理输入的转换 // 此代码段为 afl-fuzz.c 文件中的初始化和参数处理部分 // 主要功能是配置执行环境并检查传入的文件参数 // 包括加载额外目录、查找超时、检测文件参数、设置输出文件、检查二进制文件及获取当前时间 - if (extras_dir) load_extras(extras_dir); + if (extras_dir) load_extras(extras_dir); // 如果设置了额外目录,加载额外目录 - if (!timeout_given) find_timeout(); + if (!timeout_given) find_timeout(); // 如果没有设置超时,查找超时 - detect_file_args(argv + optind + 1); + detect_file_args(argv + optind + 1); // 检测文件参数 - if (!out_file) setup_stdio_file(); + if (!out_file) setup_stdio_file(); // 如果没有设置输出文件,设置标准输入输出文件 - check_binary(argv[optind]); + check_binary(argv[optind]); // 检查二进制文件 - start_time = get_cur_time(); + start_time = get_cur_time(); // 获取当前时间 - if (qemu_mode) - use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); + if (qemu_mode) // 如果是QEMU模式 + use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); // 获取QEMU参数 else - use_argv = argv + optind; + use_argv = argv + optind; // 否则使用原始参数 /* * 此函数实现了模糊测试的初始步骤,包括执行干运行、清理队列、 * 显示初始化统计信息、寻找起始位置并写入统计文件。 */ - perform_dry_run(use_argv); + perform_dry_run(use_argv); // 执行干运行 - cull_queue(); + cull_queue(); // 清理队列 - show_init_stats(); + show_init_stats(); // 显示初始化统计信息 - seek_to = find_start_position(); + seek_to = find_start_position(); // 查找起始位置 - write_stats_file(0, 0, 0); - save_auto(); + write_stats_file(0, 0, 0); // 写入统计文件 + save_auto(); // 保存自动化设置 /* * 如果状态指示测试需要停止,则跳转到停止模糊测试的标签。 */ - if (stop_soon) goto stop_fuzzing; + if (stop_soon) goto stop_fuzzing; // 如果需要停止,跳转到停止模糊测试 /* Woop woop woop */ /* * 如果不是在终端上运行,则暂停4秒,增加开始时间。 */ - if (!not_on_tty) { - sleep(4); - start_time += 4000; + if (!not_on_tty) { // 如果在终端上运行 + sleep(4); // 暂停4秒 + start_time += 4000; // 增加开始时间 /* * 再次检查是否需要停止模糊测试。 */ - if (stop_soon) goto stop_fuzzing; + if (stop_soon) goto stop_fuzzing; // 如果需要停止,跳转到停止模糊测试 } // 该段代码实现了一个持续循环,处理模糊测试队列中的条目。 - while (1) { + while (1) { // 无限循环 - u8 skipped_fuzz; + u8 skipped_fuzz; // 是否跳过模糊测试 // 清理当前模糊测试队列 - cull_queue(); + cull_queue(); // 清理队列 - if (!queue_cur) { + if (!queue_cur) { // 如果当前队列为空 // 如果当前队列为空,增加队列周期计数 - queue_cycle++; - current_entry = 0; - cur_skipped_paths = 0; - queue_cur = queue; + queue_cycle++; // 增加队列周期 + current_entry = 0; // 重置当前条目 + cur_skipped_paths = 0; // 重置跳过的路径数 + queue_cur = queue; // 重置当前队列 // 根据指定的跳过路径数量,定位到队列中的特定条目 - while (seek_to) { - current_entry++; - seek_to--; - queue_cur = queue_cur->next; + while (seek_to) { // 查找起始位置 + current_entry++; // 增加当前条目 + seek_to--; // 减少查找位置 + queue_cur = queue_cur->next; // 移动到下一个队列条目 } // 显示当前状态统计 - show_stats(); + show_stats(); // 显示统计信息 - if (not_on_tty) { + if (not_on_tty) { // 如果不在终端上运行 // 在非终端模式下,输出当前队列周期信息 - ACTF("Entering queue cycle %llu.", queue_cycle); - fflush(stdout); + ACTF("Entering queue cycle %llu.", queue_cycle); // 输出队列周期信息 + fflush(stdout); // 刷新输出 } /* If we had a full queue cycle with no new finds, try recombination strategies next. */ // 检查当前队列路径是否与前一个队列路径相同 - if (queued_paths == prev_queued) { + if (queued_paths == prev_queued) { // 如果队列路径数与前一次相同 // 根据是否使用拼接,决定增加无发现循环次数或启用拼接 - if (use_splicing) cycles_wo_finds++; else use_splicing = 1; + if (use_splicing) cycles_wo_finds++; else use_splicing = 1; // 增加无发现循环次数或启用拼接 - } else cycles_wo_finds = 0; + } else cycles_wo_finds = 0; // 否则重置无发现循环次数 // 更新前一个队列路径值 - prev_queued = queued_paths; + prev_queued = queued_paths; // 更新前一个队列路径数 // 检查同步ID,队列循环和环境变量以同步模糊测试器 - if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) - sync_fuzzers(use_argv); + if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) // 如果有同步ID且队列周期为1且设置了AFL_IMPORT_FIRST环境变量 + sync_fuzzers(use_argv); // 同步模糊测试器 } // 执行模糊测试 - skipped_fuzz = fuzz_one(use_argv); + skipped_fuzz = fuzz_one(use_argv); // 执行模糊测试 // 如果没有停止标志且没有跳过模糊测试,根据同步间隔决定是否同步 - if (!stop_soon && sync_id && !skipped_fuzz) { + if (!stop_soon && sync_id && !skipped_fuzz) { // 如果没有停止标志且同步ID存在且没有跳过模糊测试 - if (!(sync_interval_cnt++ % SYNC_INTERVAL)) - sync_fuzzers(use_argv); + if (!(sync_interval_cnt++ % SYNC_INTERVAL)) // 如果同步间隔计数达到同步间隔 + sync_fuzzers(use_argv); // 同步模糊测试器 } // 如果没有停止标志且 exit_1 为真,则设置停止标志 - if (!stop_soon && exit_1) stop_soon = 2; + if (!stop_soon && exit_1) stop_soon = 2; // 如果没有停止标志且exit_1为真,设置停止标志 // 如果设置了停止标志,则跳出循环 - if (stop_soon) break; + if (stop_soon) break; // 如果设置了停止标志,跳出循环 // 更新当前队列指针和当前条目计数 - queue_cur = queue_cur->next; - current_entry++; + queue_cur = queue_cur->next; // 移动到下一个队列条目 + current_entry++; // 增加当前条目计数 } @@ -8238,51 +8198,51 @@ int main(int argc, char** argv) { * 在终止时会杀死子进程和forkserver,并等待forkserver获取资源使用统计信息。 * 此外,还会写入位图和统计文件以保存当前状态。 */ - if (queue_cur) show_stats(); + if (queue_cur) show_stats(); // 如果当前队列条目存在,显示统计信息 /* If we stopped programmatically, we kill the forkserver and the current runner. If we stopped manually, this is done by the signal handler. */ - if (stop_soon == 2) { - if (child_pid > 0) kill(child_pid, SIGKILL); - if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); + if (stop_soon == 2) { // 如果是程序控制的终止 + if (child_pid > 0) kill(child_pid, SIGKILL); // 杀死子进程 + if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); // 杀死forkserver进程 } /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */ - if (waitpid(forksrv_pid, NULL, 0) <= 0) { - WARNF("error waitpid\n"); + if (waitpid(forksrv_pid, NULL, 0) <= 0) { // 等待forkserver进程结束 + WARNF("error waitpid\n"); // 输出等待错误信息 } - write_bitmap(); - write_stats_file(0, 0, 0); - save_auto(); + write_bitmap(); // 写入位图 + write_stats_file(0, 0, 0); // 写入统计文件 + save_auto(); // 保存自动化设置 /* 停止模糊测试的函数 */ stop_fuzzing: SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST, - stop_soon == 2 ? "programmatically" : "by user"); + stop_soon == 2 ? "programmatically" : "by user"); // 输出测试终止信息 /* 运行超过30分钟但仍在进行第一次周期? */ - if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) { + if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) { // 如果队列周期为1且运行时间超过30分钟 SAYF("\n" cYEL "[!] " cRST "Stopped during the first cycle, results may be incomplete.\n" - " (For info on resuming, see %s/README.)\n", doc_path); + " (For info on resuming, see %s/README.)\n", doc_path); // 输出警告信息 } - fclose(plot_file); - destroy_queue(); - destroy_extras(); - ck_free(target_path); - ck_free(sync_id); + fclose(plot_file); // 关闭Gnuplot文件 + destroy_queue(); // 销毁队列 + destroy_extras(); // 销毁额外数据 + ck_free(target_path); // 释放目标路径内存 + ck_free(sync_id); // 释放同步ID内存 - alloc_report(); + alloc_report(); // 输出内存分配报告 - OKF("We're done here. Have a nice day!\n"); + OKF("We're done here. Have a nice day!\n"); // 输出完成信息 - exit(0); + exit(0); // 退出程序 } -#endif /* !AFL_LIB */ +#endif /* !AFL_LIB */ \ No newline at end of file -- 2.34.1