From ad899967e38a4bbf78ae1129cf86561dcf8a9bc3 Mon Sep 17 00:00:00 2001 From: Satori5ama <1242330740@qq.com> Date: Thu, 9 Jan 2025 08:16:35 +0800 Subject: [PATCH] =?UTF-8?q?afl-fuzz.c=204800-5500=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/.vscode/settings.json | 3 +- src/afl-fuzz.c | 518 ++++++++++++++++++-------------------- 2 files changed, 253 insertions(+), 268 deletions(-) diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index 9f2d256..db0a9aa 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -10,6 +10,7 @@ "stdlib.h": "c", "alloc-inl.h": "c", "debug.h": "c", - "android-ashmem.h": "c" + "android-ashmem.h": "c", + "limits": "c" } } \ No newline at end of file diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index d22d88d..ff147b5 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -4810,19 +4810,19 @@ static u8 could_be_bitflip(u32 xor_val) { u32 sh = 0; - if (!xor_val) return 1; + if (!xor_val) return 1; // 如果xor_val为0,返回1,表示可能是位翻转 /* Shift left until first bit set. */ - + // 左移直到找到第一个置位(1)的位 while (!(xor_val & 1)) { sh++; xor_val >>= 1; } /* 1-, 2-, and 4-bit patterns are OK anywhere. */ - + // 1位、2位和4位的模式在任何地方都是允许的 if (xor_val == 1 || xor_val == 3 || xor_val == 15) return 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. */ - + // 8位、16位和32位的模式只有在移位因子能被8整除时才允许,因为这些操作的步长是8 if (sh & 7) return 0; if (xor_val == 0xff || xor_val == 0xffff || xor_val == 0xffffffff) @@ -4835,26 +4835,26 @@ static u8 could_be_bitflip(u32 xor_val) { /* 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; - 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++) { 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. */ - + // 如果只有一个字节不同且值在范围内,返回1 if (diffs == 1) { if ((u8)(ov - nv) <= ARITH_MAX || @@ -4862,10 +4862,10 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { } - if (blen == 1) return 0; + if (blen == 1) return 0; // 如果blen为1,返回0 /* See if two-byte adjustments to any byte would produce this result. */ - + // 检查是否可以通过对任何字节进行双字节调整来产生这个结果 diffs = 0; for (i = 0; i < blen / 2; i++) { @@ -4873,18 +4873,18 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { 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. */ - + // 如果只有一个字不同且值在范围内,返回1 if (diffs == 1) { if ((u16)(ov - nv) <= ARITH_MAX || (u16)(nv - ov) <= ARITH_MAX) return 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; @@ -4892,13 +4892,13 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { } /* Finally, let's do the same thing for dwords. */ - + // 最后,对双字进行同样的操作 if (blen == 4) { if ((u32)(old_val - new_val) <= ARITH_MAX || (u32)(new_val - old_val) <= ARITH_MAX) return 1; - new_val = SWAP32(new_val); + new_val = SWAP32(new_val); // 交换字节顺序 old_val = SWAP32(old_val); if ((u32)(old_val - new_val) <= ARITH_MAX || @@ -4911,6 +4911,7 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { } + /* Last but not least, a similar helper to see if insertion of an interesting integer is redundant given the insertions done for shorter blen. The last param (check_le) is set if the caller @@ -4921,47 +4922,56 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { u32 i, j; + // 如果旧值和新值相同,直接返回1 if (old_val == new_val) return 1; - /* See if one-byte insertions from interesting_8 over old_val could - produce new_val. */ + /* 检查是否可以通过在old_val中插入interesting_8中的单字节值来生成new_val */ + // 遍历每个字节位置 for (i = 0; i < blen; i++) { + // 遍历interesting_8数组中的每个单字节值 for (j = 0; j < sizeof(interesting_8); j++) { + // 将old_val的第i个字节替换为interesting_8[j],生成临时值tval u32 tval = (old_val & ~(0xff << (i * 8))) | (((u8)interesting_8[j]) << (i * 8)); + // 如果tval等于new_val,返回1 if (new_val == tval) return 1; } } - /* Bail out unless we're also asked to examine two-byte LE insertions - as a preparation for BE attempts. */ + /* 如果blen为2且不需要检查小端序(check_le为0),则直接返回0 */ if (blen == 2 && !check_le) return 0; - /* See if two-byte insertions over old_val could give us new_val. */ + /* 检查是否可以通过在old_val中插入interesting_16中的双字节值来生成new_val */ + // 遍历每个双字节位置 for (i = 0; i < blen - 1; i++) { + // 遍历interesting_16数组中的每个双字节值 for (j = 0; j < sizeof(interesting_16) / 2; j++) { + // 将old_val的第i个双字节替换为interesting_16[j],生成临时值tval u32 tval = (old_val & ~(0xffff << (i * 8))) | (((u16)interesting_16[j]) << (i * 8)); + // 如果tval等于new_val,返回1 if (new_val == tval) return 1; - /* Continue here only if blen > 2. */ + /* 如果blen大于2,继续检查交换字节序后的值 */ if (blen > 2) { + // 将old_val的第i个双字节替换为interesting_16[j]的字节交换值,生成临时值tval tval = (old_val & ~(0xffff << (i * 8))) | (SWAP16(interesting_16[j]) << (i * 8)); + // 如果tval等于new_val,返回1 if (new_val == tval) return 1; } @@ -4970,24 +4980,26 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { } + // 如果blen为4且需要检查小端序(check_le为1),则检查四字节插入 if (blen == 4 && check_le) { - /* See if four-byte insertions could produce the same result - (LE only). */ + /* 检查是否可以通过插入interesting_32中的四字节值来生成new_val(仅小端序) */ + // 遍历interesting_32数组中的每个四字节值 for (j = 0; j < sizeof(interesting_32) / 4; j++) + // 如果new_val等于interesting_32[j],返回1 if (new_val == (u32)interesting_32[j]) return 1; } + // 如果没有找到匹配的值,返回0 return 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) { @@ -5001,30 +5013,26 @@ static u8 fuzz_one(char** argv) { u8 a_collect[MAX_AUTO_EXTRA]; u32 a_len = 0; -#ifdef IGNORE_FINDS - /* In IGNORE_FINDS mode, skip any entries that weren't in the - initial data set. */ +#ifdef IGNORE_FINDS + /* 在 IGNORE_FINDS 模式下,跳过任何不在初始数据集中的条目。 */ if (queue_cur->depth > 1) return 1; #else 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. */ - + /* 如果我们有任何被标记为 favored 且未被 fuzz 的新条目, + 可能会跳过已经 fuzz 过或非 favored 的条目。 */ 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. */ - + /* 否则,仍然可能跳过非 favored 的条目,尽管频率较低。 + 对于已经 fuzz 过的输入,跳过的概率较高, + 对于从未 fuzz 过的条目,跳过的概率较低。 */ if (queue_cycle > 1 && !queue_cur->was_fuzzed) { if (UR(100) < SKIP_NFAV_NEW_PROB) return 1; @@ -5040,13 +5048,13 @@ static u8 fuzz_one(char** argv) { #endif /* ^IGNORE_FINDS */ if (not_on_tty) { + /* 如果不是在终端上运行,输出当前 fuzz 的测试用例信息。 */ ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found)...", current_entry, queued_paths, unique_crashes); fflush(stdout); } - /* Map the test case into memory. */ - + /* 将测试用例映射到内存中。 */ fd = open(queue_cur->fname, O_RDONLY); if (fd < 0) PFATAL("Unable to open '%s'", queue_cur->fname); @@ -5059,10 +5067,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); subseq_tmouts = 0; @@ -5070,7 +5076,7 @@ static u8 fuzz_one(char** argv) { cur_depth = queue_cur->depth; /******************************************* - * CALIBRATION (only if failed earlier on) * + * 校准(仅在之前失败时进行) * *******************************************/ if (queue_cur->cal_failed) { @@ -5079,10 +5085,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; res = calibrate_case(argv, queue_cur, in_buf, queue_cycle - 1, 0); @@ -5100,7 +5105,7 @@ static u8 fuzz_one(char** argv) { } /************ - * TRIMMING * + * 修剪 * ************/ if (!dumb_mode && !queue_cur->trim_done) { @@ -5115,8 +5120,7 @@ static u8 fuzz_one(char** argv) { goto abandon_entry; } - /* Don't retry trimming, even if it failed. */ - + /* 即使修剪失败,也不要重试。 */ queue_cur->trim_done = 1; if (len != queue_cur->len) len = queue_cur->len; @@ -5126,28 +5130,26 @@ static u8 fuzz_one(char** argv) { memcpy(out_buf, in_buf, len); /********************* - * PERFORMANCE SCORE * + * 性能评分 * *********************/ 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 参数,或者我们已经对该条目进行了确定性 fuzz(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. */ - + /* 如果执行路径校验和将该条目排除在主实例的范围内, + 则跳过确定性 fuzz。 */ if (master_max && (queue_cur->exec_cksum % master_max) != master_id - 1) goto havoc_stage; doing_det = 1; /********************************************* - * SIMPLE BITFLIP (+dictionary construction) * + * 简单的位翻转(+字典构建) * *********************************************/ #define FLIP_BIT(_ar, _b) do { \ @@ -5156,90 +5158,76 @@ static u8 fuzz_one(char** argv) { _arf[(_bf) >> 3] ^= (128 >> ((_bf) & 7)); \ } while (0) + /* Single walking bit. */ - stage_short = "flip1"; - stage_max = len << 3; - stage_name = "bitflip 1/1"; + stage_short = "flip1"; // 设置当前阶段的简短名称为 "flip1" + stage_max = len << 3; // 计算最大阶段数,len 是输入数据的长度,左移3位相当于乘以8(每个字节8位) + stage_name = "bitflip 1/1"; // 设置当前阶段的完整名称为 "bitflip 1/1" - stage_val_type = STAGE_VAL_NONE; + stage_val_type = STAGE_VAL_NONE; // 设置当前阶段的值类型为无(STAGE_VAL_NONE) - orig_hit_cnt = queued_paths + unique_crashes; + orig_hit_cnt = queued_paths + unique_crashes; // 记录当前阶段的初始命中计数,包括已排队的路径和唯一的崩溃数 - prev_cksum = queue_cur->exec_cksum; + prev_cksum = queue_cur->exec_cksum; // 记录当前队列项的校验和 - for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { + for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { // 遍历每个位 - stage_cur_byte = stage_cur >> 3; + stage_cur_byte = stage_cur >> 3; // 计算当前字节的位置,stage_cur 右移3位相当于除以8 - FLIP_BIT(out_buf, stage_cur); + FLIP_BIT(out_buf, stage_cur); // 翻转当前位 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; // 执行模糊测试,如果返回非零值则跳转到 abandon_entry - FLIP_BIT(out_buf, stage_cur); + 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: + /* 在翻转每个字节的最低位时,执行一个额外的技巧来检测可能的语法标记。本质上,这个想法是,如果你有一个像这样的二进制数据块: 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) { + if (!dumb_mode && (stage_cur & 7) == 7) { // 如果不是 dumb_mode 并且当前位是字节的最后一位 - u32 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + u32 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算当前跟踪位的校验和 - if (stage_cur == stage_max - 1 && cksum == prev_cksum) { + 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++; + if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3]; // 如果自动收集数组未满,则添加当前字节 + a_len++; // 增加自动收集数组的长度 - if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) - maybe_add_auto(a_collect, a_len); + if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) // 如果自动收集数组的长度在最小和最大范围内 + maybe_add_auto(a_collect, a_len); // 可能将自动收集的字符串添加到字典中 - } else if (cksum != prev_cksum) { + } 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); + if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) // 如果自动收集数组的长度在最小和最大范围内 + maybe_add_auto(a_collect, a_len); // 可能将自动收集的字符串添加到字典中 - a_len = 0; - prev_cksum = cksum; + a_len = 0; // 重置自动收集数组的长度 + prev_cksum = cksum; // 更新前一个校验和 } - /* 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) { + if (cksum != queue_cur->exec_cksum) { // 如果校验和与当前队列项的校验和不同 - if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3]; - a_len++; + if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3]; // 如果自动收集数组未满,则添加当前字节 + a_len++; // 增加自动收集数组的长度 } @@ -5247,68 +5235,69 @@ static u8 fuzz_one(char** argv) { } - new_hit_cnt = queued_paths + unique_crashes; + new_hit_cnt = queued_paths + unique_crashes; // 计算新的命中计数 - stage_finds[STAGE_FLIP1] += new_hit_cnt - orig_hit_cnt; - stage_cycles[STAGE_FLIP1] += stage_max; + 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"; - stage_max = (len << 3) - 1; + stage_name = "bitflip 2/1"; // 设置当前阶段的完整名称为 "bitflip 2/1" + stage_short = "flip2"; // 设置当前阶段的简短名称为 "flip2" + stage_max = (len << 3) - 1; // 计算最大阶段数,len 是输入数据的长度,左移3位相当于乘以8,减去1 - orig_hit_cnt = new_hit_cnt; + orig_hit_cnt = new_hit_cnt; // 记录当前阶段的初始命中计数 - for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { + for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { // 遍历每个位 - stage_cur_byte = stage_cur >> 3; + stage_cur_byte = stage_cur >> 3; // 计算当前字节的位置 - FLIP_BIT(out_buf, stage_cur); - FLIP_BIT(out_buf, stage_cur + 1); + FLIP_BIT(out_buf, stage_cur); // 翻转当前位 + FLIP_BIT(out_buf, stage_cur + 1); // 翻转下一个位 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; // 执行模糊测试,如果返回非零值则跳转到 abandon_entry - FLIP_BIT(out_buf, stage_cur); - FLIP_BIT(out_buf, stage_cur + 1); + FLIP_BIT(out_buf, stage_cur); // 恢复当前位的翻转 + FLIP_BIT(out_buf, stage_cur + 1); // 恢复下一个位的翻转 } - new_hit_cnt = queued_paths + unique_crashes; + new_hit_cnt = queued_paths + unique_crashes; // 计算新的命中计数 - stage_finds[STAGE_FLIP2] += new_hit_cnt - orig_hit_cnt; - stage_cycles[STAGE_FLIP2] += stage_max; + 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"; - stage_max = (len << 3) - 3; + stage_name = "bitflip 4/1"; // 设置当前阶段的完整名称为 "bitflip 4/1" + stage_short = "flip4"; // 设置当前阶段的简短名称为 "flip4" + stage_max = (len << 3) - 3; // 计算最大阶段数,len 是输入数据的长度,左移3位相当于乘以8,减去3 - orig_hit_cnt = new_hit_cnt; + orig_hit_cnt = new_hit_cnt; // 记录当前阶段的初始命中计数 - for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { + for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { // 遍历每个位 - stage_cur_byte = stage_cur >> 3; + stage_cur_byte = stage_cur >> 3; // 计算当前字节的位置 - FLIP_BIT(out_buf, stage_cur); - FLIP_BIT(out_buf, stage_cur + 1); - FLIP_BIT(out_buf, stage_cur + 2); - FLIP_BIT(out_buf, stage_cur + 3); + FLIP_BIT(out_buf, stage_cur); // 翻转当前位 + FLIP_BIT(out_buf, stage_cur + 1); // 翻转下一个位 + FLIP_BIT(out_buf, stage_cur + 2); // 翻转下下个位 + FLIP_BIT(out_buf, stage_cur + 3); // 翻转下下下个位 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; // 执行模糊测试,如果返回非零值则跳转到 abandon_entry - FLIP_BIT(out_buf, stage_cur); - FLIP_BIT(out_buf, stage_cur + 1); - FLIP_BIT(out_buf, stage_cur + 2); - FLIP_BIT(out_buf, stage_cur + 3); + FLIP_BIT(out_buf, stage_cur); // 恢复当前位的翻转 + FLIP_BIT(out_buf, stage_cur + 1); // 恢复下一个位的翻转 + FLIP_BIT(out_buf, stage_cur + 2); // 恢复下下个位的翻转 + FLIP_BIT(out_buf, stage_cur + 3); // 恢复下下下个位的翻转 } - new_hit_cnt = queued_paths + unique_crashes; + new_hit_cnt = queued_paths + unique_crashes; // 计算新的命中计数 + + stage_finds[STAGE_FLIP4] += new_hit_cnt - orig_hit_cnt; // 更新当前阶段的发现计数 + stage_cycles[STAGE_FLIP4] += stage_max; // 更新当前阶段的循环计数 - stage_finds[STAGE_FLIP4] += new_hit_cnt - orig_hit_cnt; - stage_cycles[STAGE_FLIP4] += stage_max; /* Effector map setup. These macros calculate: @@ -5318,230 +5307,225 @@ static u8 fuzz_one(char** argv) { */ -#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2) -#define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1)) -#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) +#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2) // 计算位置 _p 在 effector map 中的索引 +#define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1)) // 计算位置 _x 在 effector map 中的偏移量 +#define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l)) // 计算长度 _l 对应的 effector map 的长度 +#define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1) // 计算从位置 _p 开始长度为 _l 的跨度在 effector map 中的长度 - /* Initialize effector map for the next step (see comments below). Always - flag first and last byte as doing something. */ + /* 初始化 effector map 用于下一步(见下方注释)。始终标记第一个和最后一个字节为有效。 */ - eff_map = ck_alloc(EFF_ALEN(len)); - eff_map[0] = 1; + eff_map = ck_alloc(EFF_ALEN(len)); // 分配 effector map 的内存 + eff_map[0] = 1; // 标记第一个字节为有效 - if (EFF_APOS(len - 1) != 0) { - eff_map[EFF_APOS(len - 1)] = 1; - eff_cnt++; + if (EFF_APOS(len - 1) != 0) { // 如果最后一个字节不在第一个块中 + eff_map[EFF_APOS(len - 1)] = 1; // 标记最后一个字节为有效 + eff_cnt++; // 增加有效字节计数 } - /* Walking byte. */ + /* 单字节翻转阶段 */ - stage_name = "bitflip 8/8"; - stage_short = "flip8"; - stage_max = len; + stage_name = "bitflip 8/8"; // 设置阶段名称为 "bitflip 8/8" + stage_short = "flip8"; // 设置阶段简称为 "flip8" + stage_max = len; // 设置阶段最大值为输入长度 - orig_hit_cnt = new_hit_cnt; + orig_hit_cnt = new_hit_cnt; // 记录当前阶段的命中计数 - for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { + for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { // 遍历每个字节 - stage_cur_byte = stage_cur; + stage_cur_byte = stage_cur; // 记录当前字节位置 - out_buf[stage_cur] ^= 0xFF; + out_buf[stage_cur] ^= 0xFF; // 翻转当前字节的所有位 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + 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)]) { + if (!eff_map[EFF_APOS(stage_cur)]) { // 如果当前字节在 effector map 中未被标记为有效 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); + if (!dumb_mode && len >= EFF_MIN_LEN) // 如果不是 dumb 模式且长度大于等于最小长度 + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算校验和 else - cksum = ~queue_cur->exec_cksum; + cksum = ~queue_cur->exec_cksum; // 使用队列当前项的校验和的补码 - if (cksum != queue_cur->exec_cksum) { - eff_map[EFF_APOS(stage_cur)] = 1; - eff_cnt++; + if (cksum != queue_cur->exec_cksum) { // 如果校验和与当前执行校验和不匹配 + eff_map[EFF_APOS(stage_cur)] = 1; // 标记当前字节为有效 + eff_cnt++; // 增加有效字节计数 } } - out_buf[stage_cur] ^= 0xFF; + out_buf[stage_cur] ^= 0xFF; // 恢复当前字节的原始值 } - /* 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. */ + /* 如果 effector map 的密度超过 EFF_MAX_PERC,则将整个 map 标记为值得模糊测试, + 因为无论如何我们都不会节省太多时间。 */ if (eff_cnt != EFF_ALEN(len) && - eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { + eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { // 如果有效字节比例超过最大百分比 - memset(eff_map, 1, EFF_ALEN(len)); + memset(eff_map, 1, EFF_ALEN(len)); // 将整个 effector map 标记为有效 - blocks_eff_select += EFF_ALEN(len); + blocks_eff_select += EFF_ALEN(len); // 增加有效块选择计数 } else { - blocks_eff_select += eff_cnt; + blocks_eff_select += eff_cnt; // 增加有效块选择计数 } - blocks_eff_total += EFF_ALEN(len); + blocks_eff_total += EFF_ALEN(len); // 增加总有效块计数 - new_hit_cnt = queued_paths + unique_crashes; + new_hit_cnt = queued_paths + unique_crashes; // 更新命中计数 - stage_finds[STAGE_FLIP8] += new_hit_cnt - orig_hit_cnt; - stage_cycles[STAGE_FLIP8] += stage_max; + 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; + if (len < 2) goto skip_bitflip; // 如果长度小于 2,跳过双字节翻转阶段 - stage_name = "bitflip 16/8"; - stage_short = "flip16"; - stage_cur = 0; - stage_max = len - 1; + stage_name = "bitflip 16/8"; // 设置阶段名称为 "bitflip 16/8" + stage_short = "flip16"; // 设置阶段简称为 "flip16" + stage_cur = 0; // 重置当前阶段索引 + stage_max = len - 1; // 设置阶段最大值为长度减一 - orig_hit_cnt = new_hit_cnt; + orig_hit_cnt = new_hit_cnt; // 记录当前阶段的命中计数 - for (i = 0; i < len - 1; i++) { + for (i = 0; i < len - 1; i++) { // 遍历每对字节 - /* Let's consult the effector map... */ + /* 让我们参考 effector map... */ - if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { - stage_max--; - continue; + if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { // 如果当前字节和下一个字节在 effector map 中未被标记为有效 + stage_max--; // 减少阶段最大值 + continue; // 跳过当前字节对 } - stage_cur_byte = i; + stage_cur_byte = i; // 记录当前字节位置 - *(u16*)(out_buf + i) ^= 0xFFFF; + *(u16*)(out_buf + i) ^= 0xFFFF; // 翻转当前字节对的所有位 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - - *(u16*)(out_buf + i) ^= 0xFFFF; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; // 执行模糊测试 + stage_cur++; // 增加当前阶段索引 + *(u16*)(out_buf + i) ^= 0xFFFF; // 恢复当前字节对的原始值 } - new_hit_cnt = queued_paths + unique_crashes; + new_hit_cnt = queued_paths + unique_crashes; // 更新命中计数 - stage_finds[STAGE_FLIP16] += new_hit_cnt - orig_hit_cnt; - stage_cycles[STAGE_FLIP16] += stage_max; + stage_finds[STAGE_FLIP16] += new_hit_cnt - orig_hit_cnt; // 更新阶段发现计数 + stage_cycles[STAGE_FLIP16] += stage_max; // 更新阶段循环计数 - if (len < 4) goto skip_bitflip; + if (len < 4) goto skip_bitflip; // 如果长度小于 4,跳过四字节翻转阶段 - /* Four walking bytes. */ + /* 四字节翻转阶段 */ - stage_name = "bitflip 32/8"; - stage_short = "flip32"; - stage_cur = 0; - stage_max = len - 3; + stage_name = "bitflip 32/8"; // 设置阶段名称为 "bitflip 32/8" + stage_short = "flip32"; // 设置阶段简称为 "flip32" + stage_cur = 0; // 重置当前阶段索引 + stage_max = len - 3; // 设置阶段最大值为长度减三 - orig_hit_cnt = new_hit_cnt; + orig_hit_cnt = new_hit_cnt; // 记录当前阶段的命中计数 - for (i = 0; i < len - 3; i++) { + for (i = 0; i < len - 3; i++) { // 遍历每四个字节 - /* Let's consult the effector map... */ + /* 让我们参考 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--; - continue; + !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) { // 如果当前四个字节在 effector map 中未被标记为有效 + stage_max--; // 减少阶段最大值 + continue; // 跳过当前四个字节 } - stage_cur_byte = i; + stage_cur_byte = i; // 记录当前字节位置 - *(u32*)(out_buf + i) ^= 0xFFFFFFFF; + *(u32*)(out_buf + i) ^= 0xFFFFFFFF; // 翻转当前四个字节的所有位 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; // 执行模糊测试 + stage_cur++; // 增加当前阶段索引 - *(u32*)(out_buf + i) ^= 0xFFFFFFFF; + *(u32*)(out_buf + i) ^= 0xFFFFFFFF; // 恢复当前四个字节的原始值 } - new_hit_cnt = queued_paths + unique_crashes; + new_hit_cnt = queued_paths + unique_crashes; // 更新命中计数 - stage_finds[STAGE_FLIP32] += new_hit_cnt - orig_hit_cnt; - stage_cycles[STAGE_FLIP32] += stage_max; + stage_finds[STAGE_FLIP32] += new_hit_cnt - orig_hit_cnt; // 更新阶段发现计数 + stage_cycles[STAGE_FLIP32] += stage_max; // 更新阶段循环计数 skip_bitflip: - if (no_arith) goto skip_arith; + if (no_arith) goto skip_arith; // 如果不需要进行算术操作,跳过算术操作阶段 /********************** * ARITHMETIC INC/DEC * **********************/ - /* 8-bit arithmetics. */ + /* 8-bit arithmetics. */ // 8位算术操作 - stage_name = "arith 8/8"; - stage_short = "arith8"; - stage_cur = 0; - stage_max = 2 * len * ARITH_MAX; + stage_name = "arith 8/8"; // 当前阶段的名称 + stage_short = "arith8"; // 当前阶段的简称 + stage_cur = 0; // 当前阶段的进度 + stage_max = 2 * len * ARITH_MAX; // 当前阶段的最大进度 - stage_val_type = STAGE_VAL_LE; + stage_val_type = STAGE_VAL_LE; // 当前阶段的值类型(小端序) - orig_hit_cnt = new_hit_cnt; + orig_hit_cnt = new_hit_cnt; // 保存当前的命中计数 - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) { // 遍历输入缓冲区中的每个字节 - u8 orig = out_buf[i]; + u8 orig = out_buf[i]; // 获取当前字节的原始值 - /* Let's consult the effector map... */ + /* Let's consult the effector map... */ // 检查effector map - if (!eff_map[EFF_APOS(i)]) { - stage_max -= 2 * ARITH_MAX; - continue; + if (!eff_map[EFF_APOS(i)]) { // 如果当前字节不在effector map中 + stage_max -= 2 * ARITH_MAX; // 减少最大进度 + continue; // 跳过当前字节 } - stage_cur_byte = i; + stage_cur_byte = i; // 记录当前处理的字节位置 - for (j = 1; j <= ARITH_MAX; j++) { + for (j = 1; j <= ARITH_MAX; j++) { // 遍历ARITH_MAX范围内的值 - u8 r = orig ^ (orig + j); + u8 r = orig ^ (orig + j); // 计算原始值与增加j后的值的异或结果 /* Do arithmetic operations only if the result couldn't be a product - of a bitflip. */ + of a bitflip. */ // 只有在结果不可能是位翻转的情况下才进行算术操作 - if (!could_be_bitflip(r)) { + if (!could_be_bitflip(r)) { // 如果结果不可能是位翻转 - stage_cur_val = j; - out_buf[i] = orig + j; + stage_cur_val = j; // 记录当前的值 + out_buf[i] = orig + j; // 修改当前字节的值 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; // 调用fuzz函数,如果返回非零则退出 + stage_cur++; // 增加当前阶段的进度 - } else stage_max--; + } else stage_max--; // 否则减少最大进度 - r = orig ^ (orig - j); + r = orig ^ (orig - j); // 计算原始值与减少j后的值的异或结果 - if (!could_be_bitflip(r)) { + if (!could_be_bitflip(r)) { // 如果结果不可能是位翻转 - stage_cur_val = -j; - out_buf[i] = orig - j; + stage_cur_val = -j; // 记录当前的值(负数) + out_buf[i] = orig - j; // 修改当前字节的值 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; // 调用fuzz函数,如果返回非零则退出 + stage_cur++; // 增加当前阶段的进度 - } else stage_max--; + } else stage_max--; // 否则减少最大进度 - out_buf[i] = orig; + out_buf[i] = orig; // 恢复当前字节的原始值 } } + // 计算新命中计数,是排队路径和唯一崩溃的总和 new_hit_cnt = queued_paths + unique_crashes;