|
|
|
@ -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;
|
|
|
|
|
|
|
|
|
|