afl-fuzz.c 4800-5500行

pull/17/head
Satori5ama 8 months ago
parent ce013b31c9
commit ad899967e3

@ -10,6 +10,7 @@
"stdlib.h": "c",
"alloc-inl.h": "c",
"debug.h": "c",
"android-ashmem.h": "c"
"android-ashmem.h": "c",
"limits": "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. */
/* 从队列中取出当前条目,对其进行模糊测试。这个函数有点长...
01 */
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 参数,或者我们已经对该条目进行了确定性 fuzzwas_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;

Loading…
Cancel
Save