AFL源码分析(四)

本文最后更新于:2023年10月6日 晚上

AFL源码分析(四)

afl-fuzz.c

main函数主循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
while (1) {

u8 skipped_fuzz;

cull_queue(); // 精简队列

if (!queue_cur) { // 如果queue_cur为空,代表所有queue都被执行完一轮

queue_cycle++; // 所有queue被完整执行了多少轮
current_entry = 0;
cur_skipped_paths = 0;
queue_cur = queue; // 开始新一轮fuzz

while (seek_to) { // resume fuzz
current_entry++;
seek_to--;
queue_cur = queue_cur->next; // 从seek_to指定的queue项开始执行
}

show_stats(); // 显示stat信息

if (not_on_tty) {
ACTF("Entering queue cycle %llu.", queue_cycle);
fflush(stdout);
}

/* If we had a full queue cycle with no new finds, try
recombination strategies next. */

if (queued_paths == prev_queued) { // 代表在完整的一轮执行里都没有发现任何一个新的case

if (use_splicing) cycles_wo_finds++; else use_splicing = 1; // 若use_splicing为1(通过-d参数设置),代表接下来要通过splice重组queue里的case

} else cycles_wo_finds = 0;

prev_queued = queued_paths; // 设置prev_queued为当前发现的queued_paths

if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) // 若sync_id不为空,且queue_cycle等于1
sync_fuzzers(use_argv); // 读取其他sync文件夹下的queue,保存到主queue中

}

skipped_fuzz = fuzz_one(use_argv); // 对queue_cur进行测试,若不执行返回1,否则返回0

if (!stop_soon && sync_id && !skipped_fuzz) { // 若stop_soon为空,sync_id不为空,skipped_fuzz为空

if (!(sync_interval_cnt++ % SYNC_INTERVAL)) // 同步sync文件夹下的queue
sync_fuzzers(use_argv);

}

if (!stop_soon && exit_1) stop_soon = 2; // 若stop_soon为空,exit_1不为空,设置stop_soon为2

if (stop_soon) break; // 跳出死循环唯一途径

queue_cur = queue_cur->next; // 设置queue_cur为queue_cur的next
current_entry++;

}

fuzz_one

fuzz_one作为fuzz的核心函数,主要有以下几个阶段:

  • 前期准备阶段
  • CALIBRATION阶段
  • TRIMMING阶段
  • PERFORMANCE SCORE阶段
  • SIMPLE BITFLIP (+dictionary construction) 简单位翻转
  • ARITHMETIC INC/DEC 算术加减
  • INTERESTING VALUES
  • DICTIONARY STUFF
  • RANDOM HAVOC
  • SPLICING
  • 后期收尾阶段

前期准备阶段

根据概率判断是否开启fuzz,建立文件映射,建立输出缓冲区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* 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. */

static u8 fuzz_one(char** argv) {

s32 len, fd, temp_len, i, j;
u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0;
u64 havoc_queued, orig_hit_cnt, new_hit_cnt;
u32 splice_cycle = 0, perf_score = 100, orig_perf, prev_cksum, eff_cnt = 1;

u8 ret_val = 1, doing_det = 0;

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. */
// 在IGNORE_FINDS模式下,跳过所有不在初始数据集中的条目。

if (queue_cur->depth > 1) return 1;

#else

if (pending_favored) { // pending_favored不为空的条件下,若was_fuzzed不为空,或者favored为空,则99%概率直接返回1

/* If we have any favored, non-fuzzed new arrivals in the queue,
possibly skip to them at the expense of already-fuzzed or non-favored
cases. */

if ((queue_cur->was_fuzzed || !queue_cur->favored) &&
UR(100) < SKIP_TO_NEW_PROB) return 1; // 99%

} else if (!dumb_mode && !queue_cur->favored && queued_paths > 10) { // 若不是简易模式,favored为空,且queued_paths大于10

/* Otherwise, still possibly skip non-favored cases, albeit less often.
The odds of skipping stuff are higher for already-fuzzed inputs and
lower for never-fuzzed entries. */

if (queue_cycle > 1 && !queue_cur->was_fuzzed) { // 若queue_cycle大于1,且queue_cur没有被fuzz过,75%概率直接返回1

if (UR(100) < SKIP_NFAV_NEW_PROB) return 1; // 75%

} else { // 否则queue_cur被fuzz过,95%概率返回1

if (UR(100) < SKIP_NFAV_OLD_PROB) return 1; // 95%

}

}

#endif /* ^IGNORE_FINDS */

if (not_on_tty) { // 若不是在tty终端
ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found)...",
current_entry, queued_paths, unique_crashes); // 打印路径,crash信息
fflush(stdout); // 刷新stdout缓冲区
}

/* Map the test case into memory. */

fd = open(queue_cur->fname, O_RDONLY); // 以只读模式打开queue_cur->fname,即打开测试文件

if (fd < 0) PFATAL("Unable to open '%s'", queue_cur->fname);

len = queue_cur->len;

orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); // 建立可读可写的文件映射

if (orig_in == MAP_FAILED) PFATAL("Unable to mmap '%s'", queue_cur->fname);

close(fd); // 关闭queue_cur->fname

/* 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 = ck_alloc_nozero(len); // 建立输出缓冲区

subseq_tmouts = 0;

cur_depth = queue_cur->depth;

CALIBRATION

若评估失败,且评估次数小于3次,再次评估测试用例。根据评估用例判断是否继续进行fuzz。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if (queue_cur->cal_failed) {  // case存在评估错误

u8 res = FAULT_TMOUT;

if (queue_cur->cal_failed < CAL_CHANCES) { // 小于3次,再次校准

/* 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 */

queue_cur->exec_cksum = 0;

res = calibrate_case(argv, queue_cur, in_buf, queue_cycle - 1, 0); // 再次评估测试用例

if (res == FAULT_ERROR) // 若res为FAULT_ERROR,报错并推出
FATAL("Unable to execute target application");

}

if (stop_soon || res != crash_mode) { // 若stop_soon不为空,或者res不等于crash_mode
cur_skipped_paths++; // cur_skipped_paths + 1
goto abandon_entry; // 跳转到abandon_entry
}

}

TRIMMING

对queue_cur进行剪枝,初始化out_buf。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if (!dumb_mode && !queue_cur->trim_done) {    // 若不是简易模式,且queue_cur还未被trim过

u8 res = trim_case(argv, queue_cur, in_buf); // 对queue_cur进行trim

if (res == FAULT_ERROR) // 若res为FAULT_ERROR,报错并推出
FATAL("Unable to execute target application");

if (stop_soon) { // 若stop_soon不为空
cur_skipped_paths++;
goto abandon_entry;
}

/* Don't retry trimming, even if it failed. */

queue_cur->trim_done = 1; // 设置queue_cur的trim标志

if (len != queue_cur->len) len = queue_cur->len; // trim后更新queue长度

}

memcpy(out_buf, in_buf, len); // 将in_buf长度为len的内容拷贝到out_buf

PERFORMANCE SCORE

计算测试用例得分,进行简单的deterministic。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
orig_perf = perf_score = calculate_score(queue_cur);  // 计算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). */

if (skip_deterministic || queue_cur->was_fuzzed || queue_cur->passed_det)
goto havoc_stage; // 若跳过deterministic,或者已经被fuzz过,或者已经经过deterministic了,跳转到havoc_stage

/* Skip deterministic fuzzing if exec path checksum puts this out of scope
for this master instance. */
// 跳过从fuzz的确定性fuzz
if (master_max && (queue_cur->exec_cksum % master_max) != master_id - 1)
goto havoc_stage;

doing_det = 1;

SIMPLE BITFLIP (+dictionary construction)

对out_buf做简单的位翻转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#define FLIP_BIT(_ar, _b) do { \
u8* _arf = (u8*)(_ar); \
u32 _bf = (_b); \
_arf[(_bf) >> 3] ^= (128 >> ((_bf) & 7)); \
} while (0) // 对每一个byte进行逐位取反操作

/* Single walking bit. */

stage_short = "flip1";
stage_max = len << 3;
stage_name = "bitflip 1/1";

stage_val_type = STAGE_VAL_NONE;

orig_hit_cnt = queued_paths + unique_crashes;

prev_cksum = queue_cur->exec_cksum;

for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { // 循环stage_max轮次

stage_cur_byte = stage_cur >> 3;

FLIP_BIT(out_buf, stage_cur); // out_buf按位取反

if (common_fuzz_stuff(argv, out_buf, len)) // 调用common_fuzz_stuff进行fuzz,保存interesting种子
goto abandon_entry;

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.

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.

*/

if (!dumb_mode && (stage_cur & 7) == 7) { // stage_cur = 7(111), 15(1111), 23(10111), ...

u32 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算trace_bits校验和

// 当循环到stage_max - 1次时,比较ck_sum与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 < 32,添加当前字符到a_collec
a_len++;

if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) // 若a_len长度为合法token长度
maybe_add_auto(a_collect, a_len); // 将累计的a_collect数组内容添加到a_extras数组中

} else if (cksum != prev_cksum) { // 若路径不同,即发现了新的token

/* 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); // 将a_collect添加到a_extras数组中

a_len = 0;
prev_cksum = cksum; // 设置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) { // 若路径不相同,添加当前字节到a_collect中

if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3];
a_len++;

}

}

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_FLIP1] += new_hit_cnt - orig_hit_cnt; // stage_finds[STAGE_FLIP1]的值加上在整个FLIP_BIT中新发现的路径和Crash总和
stage_cycles[STAGE_FLIP1] += stage_max; // stage_cycles[STAGE_FLIP1]的值加上在整个FLIP_BIT中执行的target次数stage_max

/* Two walking bits. */

stage_name = "bitflip 2/1"; // bitflip 2/1,与上述bitflip 1/1过程一样,只是连续翻转2位
stage_short = "flip2";
stage_max = (len << 3) - 1;

orig_hit_cnt = new_hit_cnt;

for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {

stage_cur_byte = stage_cur >> 3;

FLIP_BIT(out_buf, stage_cur);
FLIP_BIT(out_buf, stage_cur + 1);

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;

FLIP_BIT(out_buf, stage_cur);
FLIP_BIT(out_buf, stage_cur + 1);

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_FLIP2] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_FLIP2] += stage_max;

/* Four walking bits. */

stage_name = "bitflip 4/1"; // bitflip 4/1,与上述bitflip 1/1过程一样,只是连续翻转4位
stage_short = "flip4";
stage_max = (len << 3) - 3;

orig_hit_cnt = new_hit_cnt;

for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {

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

if (common_fuzz_stuff(argv, out_buf, len)) goto 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);

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_FLIP4] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_FLIP4] += stage_max;

/* Effector map setup. These macros calculate:

EFF_APOS - position of a particular file offset in the map.
EFF_ALEN - length of a map with a particular number of bytes.
EFF_SPAN_ALEN - map span for a sequence of bytes.

*/

#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2) // p >> 3
#define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1)) // x & 7
#define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l)) // (l >> 3) + !! (l & 7)
#define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1) // (p + l - 1) >> 3 - (p >> 3) + 1

/* Initialize effector map for the next step (see comments below). Always
flag first and last byte as doing something. */

eff_map = ck_alloc(EFF_ALEN(len)); // 为eff_map分配空间,对每个byte进行翻转时,如果其造成执行路径与原始路径不一致,
// 就将该byte在effector map中标记为1,即“有效”的,否则标记为0,即“无效”的。
eff_map[0] = 1;

if (EFF_APOS(len - 1) != 0) { // len > 9
eff_map[EFF_APOS(len - 1)] = 1;
eff_cnt++;
}

/* Walking byte. */

stage_name = "bitflip 8/8"; // bitflip 8/8与上述位翻转策略不同,以字节为单位进行翻转
stage_short = "flip8";
stage_max = len;

orig_hit_cnt = new_hit_cnt;

for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {

stage_cur_byte = stage_cur;

out_buf[stage_cur] ^= 0xFF; // 翻转策略,与0xff进行异或

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)]) { // eff_map[stage_cur >> 3] == 0

u32 cksum;

/* If in dumb mode or if the file is very short, just flag everything
without wasting time on checksums. */

if (!dumb_mode && len >= EFF_MIN_LEN) // 如果不是dumb_mode且len >= 128,计算校验和
cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
else // 否则,校验和等于exec_cksum按位取反
cksum = ~queue_cur->exec_cksum;

if (cksum != queue_cur->exec_cksum) { // 若发现了新的路径
eff_map[EFF_APOS(stage_cur)] = 1; // 设置eff_map[stage_cur >> 3] = 1
eff_cnt++; // eff_cnt + 1
}

}

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. */

if (eff_cnt != EFF_ALEN(len) &&
eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { // eff_map密度大于90(默认)

memset(eff_map, 1, EFF_ALEN(len)); // 将eff_map全部设置为1

blocks_eff_select += EFF_ALEN(len); // block_eff_select + EFF_ALEN(len)

} else {

blocks_eff_select += eff_cnt; // block_eff_select + eff_cnt

}

blocks_eff_total += EFF_ALEN(len); // block_eff_total + EFF_ALEN(len)

new_hit_cnt = queued_paths + unique_crashes;

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;

stage_name = "bitflip 16/8"; // bitflip 16/8,与 bitflip 8/8翻转过程相似,一次性翻转两个字节
stage_short = "flip16";
stage_cur = 0;
stage_max = len - 1;

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len - 1; i++) {

/* Let's consult the effector map... */

if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { // 检查eff_map连续两bytes是否为0
stage_max--; // stage_max - 1
continue;
}

stage_cur_byte = i;

*(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;

stage_finds[STAGE_FLIP16] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_FLIP16] += stage_max;

if (len < 4) goto skip_bitflip; // 如果len < 4,跳转到skip_bitflip

/* Four walking bytes. */

stage_name = "bitflip 32/8"; // bitflip 32/8,与bitflip 8/8翻转策略一致,但是一次性翻转4bytes
stage_short = "flip32";
stage_cur = 0;
stage_max = len - 3;

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len - 3; i++) {

/* Let's consult the effector map... */
if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
!eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) { // 检查eff_map连续4bytes是否为空
stage_max--;
continue;
}

stage_cur_byte = i;

*(u32*)(out_buf + i) ^= 0xFFFFFFFF; // 翻转4个字节

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

*(u32*)(out_buf + i) ^= 0xFFFFFFFF; // 翻转复位

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_FLIP32] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_FLIP32] += stage_max;

skip_bitflip:

if (no_arith) goto skip_arith; // 若no_arith不为空,跳转到skip_arith

ARITHMETIC INC/DEC

对测试用例做简单的算术加减变异,不区分大小端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/* 8-bit arithmetics. */ 

stage_name = "arith 8/8"; // 1byte算数的加减
stage_short = "arith8";
stage_cur = 0;
stage_max = 2 * len * ARITH_MAX;

stage_val_type = STAGE_VAL_LE;

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len; i++) {

u8 orig = out_buf[i];

/* Let's consult the effector map... */

if (!eff_map[EFF_APOS(i)]) { // 判断该字节对应eff_map是否有效
stage_max -= 2 * ARITH_MAX;
continue;
}

stage_cur_byte = i;

for (j = 1; j <= ARITH_MAX; j++) { // j <= 35

u8 r = orig ^ (orig + j);

/* Do arithmetic operations only if the result couldn't be a product
of a bitflip. */

// 判断是否可以通过bitflip变异方式获得本次变异结果,若不可以,执行下面分支,否则跳过此阶段

if (!could_be_bitflip(r)) {

stage_cur_val = j;
out_buf[i] = orig + j; // 加法变异

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

r = orig ^ (orig - j);

if (!could_be_bitflip(r)) {

stage_cur_val = -j;
out_buf[i] = orig - j; // 减法变异

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

out_buf[i] = orig; // 恢复为原来的值

}

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_ARITH8] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_ARITH8] += stage_max;

/* 16-bit arithmetics, both endians. */

if (len < 2) goto skip_arith;

stage_name = "arith 16/8"; // arith 16/8 变异方式类似于arith 8/8,只是一次性处理2个字节
stage_short = "arith16";
stage_cur = 0;
stage_max = 4 * (len - 1) * ARITH_MAX;

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len - 1; i++) {

u16 orig = *(u16*)(out_buf + i);

/* Let's consult the effector map... */

if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { // 判断eff_map[EFF_APOS(i)]与eff_map[EFF_APOS(i + 1)]两个字节是否为空
stage_max -= 4 * ARITH_MAX;
continue;
}

stage_cur_byte = i;

for (j = 1; j <= ARITH_MAX; j++) {

u16 r1 = orig ^ (orig + j), // 小端
r2 = orig ^ (orig - j), // 小端
r3 = orig ^ SWAP16(SWAP16(orig) + j), // 大端
r4 = orig ^ SWAP16(SWAP16(orig) - j); // 大端

/* Try little endian addition and subtraction first. Do it only
if the operation would affect more than one byte (hence the
& 0xff overflow checks) and if it couldn't be a product of
a bitflip. */

stage_val_type = STAGE_VAL_LE;

if ((orig & 0xff) + j > 0xff && !could_be_bitflip(r1)) { // 若r1不能通过bitflip得到,且(orig & 0xff) + j > 0xff

stage_cur_val = j;
*(u16*)(out_buf + i) = orig + j; // 加法变异

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

if ((orig & 0xff) < j && !could_be_bitflip(r2)) { // 若r2不能通过bitflip得到,且(orig & 0xff) < j

stage_cur_val = -j;
*(u16*)(out_buf + i) = orig - j; // 减法变异

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

/* Big endian comes next. Same deal. */

stage_val_type = STAGE_VAL_BE; // 大端

if ((orig >> 8) + j > 0xff && !could_be_bitflip(r3)) { // 若r3不能通过bitflip得到,且(orig >> 8) + j > 0xff

stage_cur_val = j;
*(u16*)(out_buf + i) = SWAP16(SWAP16(orig) + j); // 大端加法变异

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

if ((orig >> 8) < j && !could_be_bitflip(r4)) { // 若r1不能通过bitflip得到,且(orig >> 8) < j

stage_cur_val = -j;
*(u16*)(out_buf + i) = SWAP16(SWAP16(orig) - j); // 大端减法变异

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

*(u16*)(out_buf + i) = orig; // 恢复原始值

}

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_ARITH16] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_ARITH16] += stage_max;

/* 32-bit arithmetics, both endians. */

if (len < 4) goto skip_arith;

stage_name = "arith 32/8"; // arith 32/8,与arith 16/8类同,一次性处理4个字节,不再赘述
stage_short = "arith32";
stage_cur = 0;
stage_max = 4 * (len - 3) * ARITH_MAX;

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len - 3; i++) {

u32 orig = *(u32*)(out_buf + i);

/* Let's consult the effector map... */

if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
!eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) {
stage_max -= 4 * ARITH_MAX;
continue;
}

stage_cur_byte = i;

for (j = 1; j <= ARITH_MAX; j++) {

u32 r1 = orig ^ (orig + j),
r2 = orig ^ (orig - j),
r3 = orig ^ SWAP32(SWAP32(orig) + j),
r4 = orig ^ SWAP32(SWAP32(orig) - j);

/* Little endian first. Same deal as with 16-bit: we only want to
try if the operation would have effect on more than two bytes. */

stage_val_type = STAGE_VAL_LE;

if ((orig & 0xffff) + j > 0xffff && !could_be_bitflip(r1)) {

stage_cur_val = j;
*(u32*)(out_buf + i) = orig + j;

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

if ((orig & 0xffff) < j && !could_be_bitflip(r2)) {

stage_cur_val = -j;
*(u32*)(out_buf + i) = orig - j;

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

/* Big endian next. */

stage_val_type = STAGE_VAL_BE;

if ((SWAP32(orig) & 0xffff) + j > 0xffff && !could_be_bitflip(r3)) {

stage_cur_val = j;
*(u32*)(out_buf + i) = SWAP32(SWAP32(orig) + j);

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

if ((SWAP32(orig) & 0xffff) < j && !could_be_bitflip(r4)) {

stage_cur_val = -j;
*(u32*)(out_buf + i) = SWAP32(SWAP32(orig) - j);

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

*(u32*)(out_buf + i) = orig;

}

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_ARITH32] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_ARITH32] += stage_max;

skip_arith:

INTERESTING VALUES

将out_buf随机替换为AFL提供的interesting内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/**********************
* INTERESTING VALUES * // Interesting,将outbuf中的字节替换成AFL内部的值
**********************/

stage_name = "interest 8/8";
stage_short = "int8";
stage_cur = 0;
stage_max = len * sizeof(interesting_8);

stage_val_type = STAGE_VAL_LE;

orig_hit_cnt = new_hit_cnt;

/* Setting 8-bit integers. */

for (i = 0; i < len; i++) {

u8 orig = out_buf[i];

/* Let's consult the effector map... */

if (!eff_map[EFF_APOS(i)]) { // 判断eff_map是否有效
stage_max -= sizeof(interesting_8);
continue;
}

stage_cur_byte = i;

for (j = 0; j < sizeof(interesting_8); j++) { // 遍历interesting_8数组,判断是否能通过bitflip或者arith变异得到

/* Skip if the value could be a product of bitflips or arithmetics. */

if (could_be_bitflip(orig ^ (u8)interesting_8[j]) ||
could_be_arith(orig, (u8)interesting_8[j], 1)) {
stage_max--;
continue;
}

stage_cur_val = interesting_8[j];
out_buf[i] = interesting_8[j]; // 将out_buf[i]替换为interesting_8[j]

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;

out_buf[i] = orig; // 恢复原始值
stage_cur++;

}

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_INTEREST8] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_INTEREST8] += stage_max;

/* Setting 16-bit integers, both endians. */

if (no_arith || len < 2) goto skip_interest;

stage_name = "interest 16/8"; // interest 16/8,与interest 8/8阶段类似
stage_short = "int16";
stage_cur = 0;
stage_max = 2 * (len - 1) * (sizeof(interesting_16) >> 1);

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len - 1; i++) { // 判断连续eff_map是否不为0

u16 orig = *(u16*)(out_buf + i);

/* Let's consult the effector map... */

if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) {
stage_max -= sizeof(interesting_16);
continue;
}

stage_cur_byte = i;

for (j = 0; j < sizeof(interesting_16) / 2; j++) { // 循环遍历interesting_16数组

stage_cur_val = interesting_16[j];

/* Skip if this could be a product of a bitflip, arithmetics,
or single-byte interesting value insertion. */

if (!could_be_bitflip(orig ^ (u16)interesting_16[j]) &&
!could_be_arith(orig, (u16)interesting_16[j], 2) &&
!could_be_interest(orig, (u16)interesting_16[j], 2, 0)) { // 判断是否可以通过bitflip,arith,或者interest 8/8变异得到

stage_val_type = STAGE_VAL_LE;

*(u16*)(out_buf + i) = interesting_16[j]; // 替换为interesting_16[j]

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

// 大端
if ((u16)interesting_16[j] != SWAP16(interesting_16[j]) &&
!could_be_bitflip(orig ^ SWAP16(interesting_16[j])) &&
!could_be_arith(orig, SWAP16(interesting_16[j]), 2) &&
!could_be_interest(orig, SWAP16(interesting_16[j]), 2, 1)) {

stage_val_type = STAGE_VAL_BE;

*(u16*)(out_buf + i) = SWAP16(interesting_16[j]); // 替换为intere[16]
if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

}

*(u16*)(out_buf + i) = orig; // 恢复原始值

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_INTEREST16] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_INTEREST16] += stage_max;

if (len < 4) goto skip_interest;

/* Setting 32-bit integers, both endians. */

stage_name = "interest 32/8"; // interest 32/8,与interest 16/8阶段类似,不再赘述
stage_short = "int32";
stage_cur = 0;
stage_max = 2 * (len - 3) * (sizeof(interesting_32) >> 2);

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len - 3; i++) {

u32 orig = *(u32*)(out_buf + i);

/* Let's consult the effector map... */

if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
!eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) { // 判断相邻4位是否有效
stage_max -= sizeof(interesting_32) >> 1;
continue;
}

stage_cur_byte = i;

for (j = 0; j < sizeof(interesting_32) / 4; j++) {

stage_cur_val = interesting_32[j];

/* Skip if this could be a product of a bitflip, arithmetics,
or word interesting value insertion. */

if (!could_be_bitflip(orig ^ (u32)interesting_32[j]) &&
!could_be_arith(orig, interesting_32[j], 4) &&
!could_be_interest(orig, interesting_32[j], 4, 0)) {

stage_val_type = STAGE_VAL_LE;

*(u32*)(out_buf + i) = interesting_32[j];

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

if ((u32)interesting_32[j] != SWAP32(interesting_32[j]) &&
!could_be_bitflip(orig ^ SWAP32(interesting_32[j])) &&
!could_be_arith(orig, SWAP32(interesting_32[j]), 4) &&
!could_be_interest(orig, SWAP32(interesting_32[j]), 4, 1)) {

stage_val_type = STAGE_VAL_BE;

*(u32*)(out_buf + i) = SWAP32(interesting_32[j]);
if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
stage_cur++;

} else stage_max--;

}

*(u32*)(out_buf + i) = orig;

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_INTEREST32] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_INTEREST32] += stage_max;

skip_interest:

DICTIONARY STUFF

基于用户提供的extras,以及自动生成的a_extras进行替换和插入变异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/********************
* DICTIONARY STUFF * // 基于用户提供的extra token进行一定的变异,主要有替换和插入两种模式
********************/

if (!extras_cnt) goto skip_user_extras; // 若extras_cnt为空,跳转到skip_user_extras

/* Overwrite with user-supplied extras. */

stage_name = "user extras (over)"; // user extras (over)
stage_short = "ext_UO";
stage_cur = 0;
stage_max = extras_cnt * len;

stage_val_type = STAGE_VAL_NONE;

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len; i++) {

u32 last_len = 0;

stage_cur_byte = i;

/* Extras are sorted by size, from smallest to largest. This means
that we don't have to worry about restoring the buffer in
between writes at a particular offset determined by the outer
loop. */

for (j = 0; j < extras_cnt; j++) { // 遍历用户提供的extras

/* Skip extras probabilistically if extras_cnt > MAX_DET_EXTRAS. Also
skip them if there's no room to insert the payload, if the token
is redundant, or if its entire span has no bytes set in the effector
map. */

// 若extras_cnt > 200 且 UR(extras_cnt) >= 200(随机性) 或者 extras[j]的长度大于len - i
// 或者extras[j].data与out_buf相同,或者没有在指定范围的eff_map找到1,跳过此stage
if ((extras_cnt > MAX_DET_EXTRAS && UR(extras_cnt) >= MAX_DET_EXTRAS) ||
extras[j].len > len - i ||
!memcmp(extras[j].data, out_buf + i, extras[j].len) ||
!memchr(eff_map + EFF_APOS(i), 1, EFF_SPAN_ALEN(i, extras[j].len))) {

stage_max--;
continue;

}

last_len = extras[j].len;
memcpy(out_buf + i, extras[j].data, last_len); // 将out_buf替换为extras[j]

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;

stage_cur++;

}

/* Restore all the clobbered memory. */
memcpy(out_buf + i, in_buf + i, last_len); // 恢复原始值

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_EXTRAS_UO] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_EXTRAS_UO] += stage_max;

/* Insertion of user-supplied extras. */

stage_name = "user extras (insert)"; // user extras (insert)
stage_short = "ext_UI";
stage_cur = 0;
stage_max = extras_cnt * len;

orig_hit_cnt = new_hit_cnt;

ex_tmp = ck_alloc(len + MAX_DICT_FILE);

for (i = 0; i <= len; i++) {

stage_cur_byte = i;

for (j = 0; j < extras_cnt; j++) {

if (len + extras[j].len > MAX_FILE) { // 若len + extras[j].len > MAX_FILE,跳过此阶段
stage_max--;
continue;
}

/* Insert token */
memcpy(ex_tmp + i, extras[j].data, extras[j].len); // 从ex_tmp + i开始插入extras[j].len长度的数据

/* Copy tail */
memcpy(ex_tmp + i + extras[j].len, out_buf + i, len - i); // 将剩下的数据复制到ex_tmp

if (common_fuzz_stuff(argv, ex_tmp, len + extras[j].len)) {
ck_free(ex_tmp);
goto abandon_entry;
}

stage_cur++;

}

/* Copy head */
ex_tmp[i] = out_buf[i]; // 恢复原来数据

}

ck_free(ex_tmp);

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_EXTRAS_UI] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_EXTRAS_UI] += stage_max;

skip_user_extras:

if (!a_extras_cnt) goto skip_extras;

stage_name = "auto extras (over)"; // auto extras (over), 与 user extras (over)类似,这里替换的是a_extras
stage_short = "ext_AO";
stage_cur = 0;
stage_max = MIN(a_extras_cnt, USE_AUTO_EXTRAS) * len;

stage_val_type = STAGE_VAL_NONE;

orig_hit_cnt = new_hit_cnt;

for (i = 0; i < len; i++) {

u32 last_len = 0;

stage_cur_byte = i;

for (j = 0; j < MIN(a_extras_cnt, USE_AUTO_EXTRAS); j++) {

/* See the comment in the earlier code; extras are sorted by size. */

if (a_extras[j].len > len - i ||
!memcmp(a_extras[j].data, out_buf + i, a_extras[j].len) ||
!memchr(eff_map + EFF_APOS(i), 1, EFF_SPAN_ALEN(i, a_extras[j].len))) {

stage_max--;
continue;

}

last_len = a_extras[j].len;
memcpy(out_buf + i, a_extras[j].data, last_len); // 替换out_buf + i的数据为a_extras[j].data

if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;

stage_cur++;

}

/* Restore all the clobbered memory. */
memcpy(out_buf + i, in_buf + i, last_len); // 恢复原始数据

}

new_hit_cnt = queued_paths + unique_crashes;

stage_finds[STAGE_EXTRAS_AO] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_EXTRAS_AO] += stage_max;

skip_extras:

/* If we made this to here without jumping to havoc_stage or abandon_entry,
we're properly done with deterministic steps and can mark it as such
in the .state/ directory. */

if (!queue_cur->passed_det) mark_as_det_done(queue_cur); // 若queue_cur->passed_det为空,调用mark_as_det_done对cur进行标记

RANDOM HAVOC

随机变异阶段,有很大的随机性。

主要有以下变异方式:

  1. 随机翻转out_buf字节

  2. 随机选中interesting_8的值替换out_buf中的随机byte

  3. 随机选中interesting_16的值替换out_buf中的随机2bytes

  4. 随机选中interesting_32的值替换out_buf中的随机4bytes

  5. 随机选择out_buf中的1byte并随机减去一个值

  6. 随机选择out_buf中的1byte并随机加上一个值

  7. 随机选择out_buf中的2bytes并随机减去一个值

  8. 随机选择out_buf中的2bytes并随机加去一个值

  9. 随机选择out_buf中的4bytes并随机减去一个值

  10. 随机选择out_buf中的4bytes并随机加上一个值

  11. 随机选择out_buf中1byte与1~255中某个值进行异或

  12. 随机删除随机长度随机位置的值

  13. 随机删除随机长度随机位置的值

  14. 随机插入随机长度值

  15. 随机替换随机长度值

  16. 指定dict时,随机替换token

  17. 随机插入token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
 /****************
* RANDOM HAVOC * // 随机变异阶段
****************/

havoc_stage:

stage_cur_byte = -1;

/* The havoc stage mutation code is also invoked when splicing files; if the
splice_cycle variable is set, generate different descriptions and such. */

if (!splice_cycle) { // 若splice_cycle为空,标记为havoc阶段

stage_name = "havoc";
stage_short = "havoc";
stage_max = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) *
perf_score / havoc_div / 100;

} else { // 否则,标记为splice阶段

static u8 tmp[32];

perf_score = orig_perf;

sprintf(tmp, "splice %u", splice_cycle);
stage_name = tmp;
stage_short = "splice";
stage_max = SPLICE_HAVOC * perf_score / havoc_div / 100;

}

if (stage_max < HAVOC_MIN) stage_max = HAVOC_MIN;

temp_len = len;

orig_hit_cnt = queued_paths + unique_crashes;

havoc_queued = queued_paths;

/* We essentially just do several thousand runs (depending on perf_score)
where we take the input file and make random stacked tweaks. */

for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {

u32 use_stacking = 1 << (1 + UR(HAVOC_STACK_POW2)); // 随机设置switch轮数

stage_cur_val = use_stacking;

for (i = 0; i < use_stacking; i++) {

switch (UR(15 + ((extras_cnt + a_extras_cnt) ? 2 : 0))) { // 随机产生值作为case

case 0: // 随机翻转out_buf字节

/* Flip a single bit somewhere. Spooky! */

FLIP_BIT(out_buf, UR(temp_len << 3));
break;

case 1: // 随机选中interesting_8的值替换out_buf中的随机byte

/* Set byte to interesting value. */

out_buf[UR(temp_len)] = interesting_8[UR(sizeof(interesting_8))];
break;

case 2: // 随机选中interesting_16的值替换out_buf中的随机2bytes

/* Set word to interesting value, randomly choosing endian. */

if (temp_len < 2) break;

if (UR(2)) {

*(u16*)(out_buf + UR(temp_len - 1)) =
interesting_16[UR(sizeof(interesting_16) >> 1)];

} else {

*(u16*)(out_buf + UR(temp_len - 1)) = SWAP16(
interesting_16[UR(sizeof(interesting_16) >> 1)]);

}

break;

case 3: // 随机选中interesting_32的值替换out_buf中的随机4bytes

/* Set dword to interesting value, randomly choosing endian. */

if (temp_len < 4) break;

if (UR(2)) {

*(u32*)(out_buf + UR(temp_len - 3)) =
interesting_32[UR(sizeof(interesting_32) >> 2)];

} else {

*(u32*)(out_buf + UR(temp_len - 3)) = SWAP32(
interesting_32[UR(sizeof(interesting_32) >> 2)]);

}

break;

case 4: // 随机选择out_buf中的1byte并随机减去一个值

/* Randomly subtract from byte. */

out_buf[UR(temp_len)] -= 1 + UR(ARITH_MAX);
break;

case 5: // 随机选择out_buf中的1byte并随机加上一个值

/* Randomly add to byte. */

out_buf[UR(temp_len)] += 1 + UR(ARITH_MAX);
break;

case 6: // 随机选择out_buf中的2bytes并随机减去一个值

/* Randomly subtract from word, random endian. */

if (temp_len < 2) break;

if (UR(2)) {

u32 pos = UR(temp_len - 1);

*(u16*)(out_buf + pos) -= 1 + UR(ARITH_MAX);

} else {

u32 pos = UR(temp_len - 1);
u16 num = 1 + UR(ARITH_MAX);

*(u16*)(out_buf + pos) =
SWAP16(SWAP16(*(u16*)(out_buf + pos)) - num);

}

break;

case 7: // 随机选择out_buf中的2bytes并随机加去一个值

/* Randomly add to word, random endian. */

if (temp_len < 2) break;

if (UR(2)) {

u32 pos = UR(temp_len - 1);

*(u16*)(out_buf + pos) += 1 + UR(ARITH_MAX);

} else {

u32 pos = UR(temp_len - 1);
u16 num = 1 + UR(ARITH_MAX);

*(u16*)(out_buf + pos) =
SWAP16(SWAP16(*(u16*)(out_buf + pos)) + num);

}

break;

case 8: // 随机选择out_buf中的4bytes并随机减去一个值

/* Randomly subtract from dword, random endian. */

if (temp_len < 4) break;

if (UR(2)) {

u32 pos = UR(temp_len - 3);

*(u32*)(out_buf + pos) -= 1 + UR(ARITH_MAX);

} else {

u32 pos = UR(temp_len - 3);
u32 num = 1 + UR(ARITH_MAX);

*(u32*)(out_buf + pos) =
SWAP32(SWAP32(*(u32*)(out_buf + pos)) - num);

}

break;

case 9: // 随机选择out_buf中的4bytes并随机加上一个值

/* Randomly add to dword, random endian. */

if (temp_len < 4) break;

if (UR(2)) {

u32 pos = UR(temp_len - 3);

*(u32*)(out_buf + pos) += 1 + UR(ARITH_MAX);

} else {

u32 pos = UR(temp_len - 3);
u32 num = 1 + UR(ARITH_MAX);

*(u32*)(out_buf + pos) =
SWAP32(SWAP32(*(u32*)(out_buf + pos)) + num);

}

break;

case 10: // 随机选择out_buf中1byte与1~255中某个值进行异或

/* Just set a random byte to a random value. Because,
why not. We use XOR with 1-255 to eliminate the
possibility of a no-op. */

out_buf[UR(temp_len)] ^= 1 + UR(255);
break;

case 11 ... 12: { // 随机删除随机长度随机位置的值

/* Delete bytes. We're making this a bit more likely
than insertion (the next option) in hopes of keeping
files reasonably small. */

u32 del_from, del_len;

if (temp_len < 2) break;

/* Don't delete too much. */

del_len = choose_block_len(temp_len - 1); // 随机选取长度

del_from = UR(temp_len - del_len + 1); // 随机选取删除位置

memmove(out_buf + del_from, out_buf + del_from + del_len,
temp_len - del_from - del_len);

temp_len -= del_len;

break;

}

case 13: // 随机插入

if (temp_len + HAVOC_BLK_XL < MAX_FILE) { // temp_len + 32768 < 1024 * 1024

/* Clone bytes (75%) or insert a block of constant bytes (25%). */

u8 actually_clone = UR(4);
u32 clone_from, clone_to, clone_len;
u8* new_buf;

if (actually_clone) { // 75%

clone_len = choose_block_len(temp_len);
clone_from = UR(temp_len - clone_len + 1);

} else { // 25%

clone_len = choose_block_len(HAVOC_BLK_XL);
clone_from = 0;

}

clone_to = UR(temp_len);

new_buf = ck_alloc_nozero(temp_len + clone_len);

/* Head */

memcpy(new_buf, out_buf, clone_to);

/* Inserted part */

if (actually_clone)
memcpy(new_buf + clone_to, out_buf + clone_from, clone_len);
else
memset(new_buf + clone_to,
UR(2) ? UR(256) : out_buf[UR(temp_len)], clone_len);

/* Tail */
memcpy(new_buf + clone_to + clone_len, out_buf + clone_to,
temp_len - clone_to);

ck_free(out_buf);
out_buf = new_buf;
temp_len += clone_len;

}

break;

case 14: { // 随机替换

/* Overwrite bytes with a randomly selected chunk (75%) or fixed
bytes (25%). */

u32 copy_from, copy_to, copy_len;

if (temp_len < 2) break;

copy_len = choose_block_len(temp_len - 1);

copy_from = UR(temp_len - copy_len + 1);
copy_to = UR(temp_len - copy_len + 1);

if (UR(4)) { // 75%

if (copy_from != copy_to)
memmove(out_buf + copy_to, out_buf + copy_from, copy_len);

} else memset(out_buf + copy_to,
UR(2) ? UR(256) : out_buf[UR(temp_len)], copy_len);

break;

}

/* Values 15 and 16 can be selected only if there are any extras
present in the dictionaries. */

case 15: { // 指定dict时,随机替换token

/* Overwrite bytes with an extra. */

if (!extras_cnt || (a_extras_cnt && UR(2))) {

/* No user-specified extras or odds in our favor. Let's use an
auto-detected one. */

u32 use_extra = UR(a_extras_cnt);
u32 extra_len = a_extras[use_extra].len;
u32 insert_at;

if (extra_len > temp_len) break;

insert_at = UR(temp_len - extra_len + 1);
memcpy(out_buf + insert_at, a_extras[use_extra].data, extra_len);

} else {

/* No auto extras or odds in our favor. Use the dictionary. */

u32 use_extra = UR(extras_cnt);
u32 extra_len = extras[use_extra].len;
u32 insert_at;

if (extra_len > temp_len) break;

insert_at = UR(temp_len - extra_len + 1);
memcpy(out_buf + insert_at, extras[use_extra].data, extra_len);

}

break;

}

case 16: { // 随机插入token

u32 use_extra, extra_len, insert_at = UR(temp_len + 1);
u8* new_buf;

/* Insert an extra. Do the same dice-rolling stuff as for the
previous case. */

if (!extras_cnt || (a_extras_cnt && UR(2))) {

use_extra = UR(a_extras_cnt);
extra_len = a_extras[use_extra].len;

if (temp_len + extra_len >= MAX_FILE) break;

new_buf = ck_alloc_nozero(temp_len + extra_len);

/* Head */
memcpy(new_buf, out_buf, insert_at);

/* Inserted part */
memcpy(new_buf + insert_at, a_extras[use_extra].data, extra_len);

} else {

use_extra = UR(extras_cnt);
extra_len = extras[use_extra].len;

if (temp_len + extra_len >= MAX_FILE) break;

new_buf = ck_alloc_nozero(temp_len + extra_len);

/* Head */
memcpy(new_buf, out_buf, insert_at);

/* Inserted part */
memcpy(new_buf + insert_at, extras[use_extra].data, extra_len);

}

/* Tail */
memcpy(new_buf + insert_at + extra_len, out_buf + insert_at,
temp_len - insert_at);

ck_free(out_buf);
out_buf = new_buf;
temp_len += extra_len;

break;

}

}

}

if (common_fuzz_stuff(argv, out_buf, temp_len))
goto abandon_entry;

/* out_buf might have been mangled a bit, so let's restore it to its
original size and shape. */

if (temp_len < len) out_buf = ck_realloc(out_buf, len);
temp_len = len;
memcpy(out_buf, in_buf, len); // 恢复out_buf

/* If we're finding new stuff, let's run for a bit longer, limits
permitting. */

if (queued_paths != havoc_queued) { // 发现了新路径

if (perf_score <= HAVOC_MAX_MULT * 100) {
stage_max *= 2;
perf_score *= 2;
}

havoc_queued = queued_paths;

}

}

new_hit_cnt = queued_paths + unique_crashes;

if (!splice_cycle) { // HAVOC
stage_finds[STAGE_HAVOC] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_HAVOC] += stage_max;
} else { // SPLICE
stage_finds[STAGE_SPLICE] += new_hit_cnt - orig_hit_cnt;
stage_cycles[STAGE_SPLICE] += stage_max;
}

#ifndef IGNORE_FINDS

SPLICING

拼接测试用例,重新进行Random Havoc。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 /************
* SPLICING * // spling
************/

/* This is a last-resort strategy triggered by a full round with no findings.
It takes the current input file, randomly selects another input, and
splices them together at some offset, then relies on the havoc
code to mutate that blob. */

retry_splicing: // 拼接测试用例,重新Random Havoc

if (use_splicing && splice_cycle++ < SPLICE_CYCLES &&
queued_paths > 1 && queue_cur->len > 1) { // 若

struct queue_entry* target;
u32 tid, split_at;
u8* new_buf;
s32 f_diff, l_diff;

/* First of all, if we've modified in_buf for havoc, let's clean that
up... */

if (in_buf != orig_in) { // 恢复in_buf
ck_free(in_buf);
in_buf = orig_in;
len = queue_cur->len;
}

/* Pick a random queue entry and seek to it. Don't splice with yourself. */

do { tid = UR(queued_paths); } while (tid == current_entry); // 随机挑选目标

splicing_with = tid;
target = queue;

while (tid >= 100) { target = target->next_100; tid -= 100; } // 纠正tid取值,选取合适的target
while (tid--) target = target->next;

/* Make sure that the target has a reasonable length. */

while (target && (target->len < 2 || target == queue_cur)) { // 选取合适的target
target = target->next;
splicing_with++;
}

if (!target) goto retry_splicing;

/* Read the testcase into a new buffer. */

fd = open(target->fname, O_RDONLY); // 打开target->fname

if (fd < 0) PFATAL("Unable to open '%s'", target->fname);

new_buf = ck_alloc_nozero(target->len); // 为new_buf分配空间

ck_read(fd, new_buf, target->len, target->fname); // 将文件内容读取到new_buf中

close(fd);

/* Find a suitable splicing location, somewhere between the first and
the last differing byte. Bail out if the difference is just a single
byte or so. */

locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff);

if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { // 判断splice是否合法
ck_free(new_buf);
goto retry_splicing;
}

/* Split somewhere between the first and last differing byte. */

split_at = f_diff + UR(l_diff - f_diff); // 随机选取位置切割new_buf

/* Do the thing. */

len = target->len;
memcpy(new_buf, in_buf, split_at);
in_buf = new_buf;

ck_free(out_buf);
out_buf = ck_alloc_nozero(len);
memcpy(out_buf, in_buf, len); // 拼接out_buf和new_buf

goto havoc_stage; // 跳转到havoc_stage,进行havoc

}

#endif /* !IGNORE_FINDS */

后期收尾阶段

设置fuzz标志位,取消映射,释放缓冲区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  ret_val = 0;  // 设置ret_val为0

abandon_entry:

splicing_with = -1;

/* Update pending_not_fuzzed count if we made it through the calibration
cycle and have not seen this entry before. */

// 若stop_soon为空,且cal_failed为空,was_fuzzed为空
if (!stop_soon && !queue_cur->cal_failed && !queue_cur->was_fuzzed) {
queue_cur->was_fuzzed = 1; // 设置was_fuzzed为1,表示已经fuzz过
pending_not_fuzzed--; // pending_noe_fuzzed - 1
if (queue_cur->favored) pending_favored--; // pending_favored - 1
}

munmap(orig_in, queue_cur->len); // 取消文件映射

if (in_buf != orig_in) ck_free(in_buf); // 释放缓冲区
ck_free(out_buf);
ck_free(eff_map);

return ret_val;

#undef FLIP_BIT

}

trim_case

对测试用例进行剪枝,找到最小的符合原来路径的输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/* Trim all new test cases to save cycles when doing deterministic checks. The
trimmer uses power-of-two increments somewhere between 1/16 and 1/1024 of
file size, to keep the stage short and sweet. */

static u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) {

static u8 tmp[64];
static u8 clean_trace[MAP_SIZE];

u8 needs_write = 0, fault = 0;
u32 trim_exec = 0;
u32 remove_len;
u32 len_p2;

/* Although the trimmer will be less useful when variable behavior is
detected, it will still work to some extent, so we don't check for
this. */

if (q->len < 5) return 0; // 若queue的长度小于5,直接返回

stage_name = tmp;
bytes_trim_in += q->len; // 保存trim的字节总数

/* Select initial chunk len, starting with large steps. */

len_p2 = next_p2(q->len); // 查找q->len的下一个2的幂

remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES); // 取len_p2 / 16 或者4之间最大的作为初始步长

/* Continue until the number of steps gets too high or the stepover
gets too small. */

while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) { // 若remove_len大于等于len_p2 / 1024或者4

u32 remove_pos = remove_len;

sprintf(tmp, "trim %s/%s", DI(remove_len), DI(remove_len));

stage_cur = 0;
stage_max = q->len / remove_len; // 计算最多阶段数量

while (remove_pos < q->len) { // 每次循环remove_len步长,直到遍历整个文件

u32 trim_avail = MIN(remove_len, q->len - remove_pos);
u32 cksum;
// 从in_buf开始,写入remove_pos字节,然后跳过remove_pos + trim_avail字节,写入到out_file(.cur_input)中
write_with_gap(in_buf, q->len, remove_pos, trim_avail);

fault = run_target(argv, exec_tmout); // 执行目标程序
trim_execs++;

if (stop_soon || fault == FAULT_ERROR) goto abort_trimming; // 若stop_soon不为空,或者fault为错误,跳转到abort_trimming

/* Note that we don't keep track of crashes or hangs here; maybe TODO? */

cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);

/* If the deletion had no impact on the trace, make it permanent. This
isn't perfect for variable-path inputs, but we're just making a
best-effort pass, so it's not a big deal if we end up with false
negatives every now and then. */

if (cksum == q->exec_cksum) { // 判断cksum与q->exec_cksum,即判断是否产生了新的路径
// 若没有产生新的路径,进行剪枝操作

u32 move_tail = q->len - remove_pos - trim_avail;

q->len -= trim_avail; // 从q->len中减去trim_avail
len_p2 = next_p2(q->len); // 继续找q->len的下一个2的幂

memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail,
move_tail); // 从in_buf[remove_pos]开始,删除trim_avail个字节

/* Let's save a clean trace, which will be needed by
update_bitmap_score once we're done with the trimming stuff. */

if (!needs_write) { // 若needs_write为空,设置needs_write为1

needs_write = 1;
memcpy(clean_trace, trace_bits, MAP_SIZE); // 保存当前trace_bits到clean_trace中,便于后续恢复

}

} else remove_pos += remove_len;

/* Since this can be slow, update the screen every now and then. */

if (!(trim_exec++ % stats_update_freq)) show_stats(); // 若达到了stats_update_freq的倍数,显示stat
stage_cur++;

}

remove_len >>= 1; // remove_len / 2

}

/* If we have made changes to in_buf, we also need to update the on-disk
version of the test case. */

if (needs_write) { // 若needs_write不为空

s32 fd;

unlink(q->fname); /* ignore errors */

fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); // 以只写模式打开q->fname

if (fd < 0) PFATAL("Unable to create '%s'", q->fname);

ck_write(fd, in_buf, q->len, q->fname); // 将in_buf写入fname文件中
close(fd);

memcpy(trace_bits, clean_trace, MAP_SIZE); // 恢复trace_bits的值
update_bitmap_score(q);

}

abort_trimming:

bytes_trim_out += q->len;
return fault;

}

calculate_score

根据queue entry的执行速度、覆盖到的path数和路径深度来评估出一个得分,这个得分perf_score在后面havoc的时候使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* Calculate case desirability score to adjust the length of havoc fuzzing.
A helper function for fuzz_one(). Maybe some of these constants should
go into config.h. */

static u32 calculate_score(struct queue_entry* q) {

u32 avg_exec_us = total_cal_us / total_cal_cycles; // 计算每轮平均执行时间
u32 avg_bitmap_size = total_bitmap_size / total_bitmap_entries; // 计算每轮平均bitmap带线啊哦
u32 perf_score = 100; // 设置初始值perf_score为100

/* Adjust score based on execution speed of this path, compared to the
global average. Multiplier ranges from 0.1x to 3x. Fast inputs are
less expensive to fuzz, so we're giving them more air time. */

// 根据执行时间与avg_exec_us的大小,更新perf_score
if (q->exec_us * 0.1 > avg_exec_us) perf_score = 10;
else if (q->exec_us * 0.25 > avg_exec_us) perf_score = 25;
else if (q->exec_us * 0.5 > avg_exec_us) perf_score = 50;
else if (q->exec_us * 0.75 > avg_exec_us) perf_score = 75;
else if (q->exec_us * 4 < avg_exec_us) perf_score = 300;
else if (q->exec_us * 3 < avg_exec_us) perf_score = 200;
else if (q->exec_us * 2 < avg_exec_us) perf_score = 150;

/* Adjust score based on bitmap size. The working theory is that better
coverage translates to better targets. Multiplier from 0.25x to 3x. */

// 根据当前queue的bitmap_size与avg_bitmap_size的大小,更新perf_score
if (q->bitmap_size * 0.3 > avg_bitmap_size) perf_score *= 3;
else if (q->bitmap_size * 0.5 > avg_bitmap_size) perf_score *= 2;
else if (q->bitmap_size * 0.75 > avg_bitmap_size) perf_score *= 1.5;
else if (q->bitmap_size * 3 < avg_bitmap_size) perf_score *= 0.25;
else if (q->bitmap_size * 2 < avg_bitmap_size) perf_score *= 0.5;
else if (q->bitmap_size * 1.5 < avg_bitmap_size) perf_score *= 0.75;

/* Adjust score based on handicap. Handicap is proportional to how late
in the game we learned about this path. Latecomers are allowed to run
for a bit longer until they catch up with the rest. */

if (q->handicap >= 4) {

perf_score *= 4;
q->handicap -= 4;

} else if (q->handicap) {

perf_score *= 2;
q->handicap--;

}

/* Final adjustment based on input depth, under the assumption that fuzzing
deeper test cases is more likely to reveal stuff that can't be
discovered with traditional fuzzers. */

// 根据当前q-depth落入的区间,更新perf_score
switch (q->depth) {

case 0 ... 3: break;
case 4 ... 7: perf_score *= 2; break;
case 8 ... 13: perf_score *= 3; break;
case 14 ... 25: perf_score *= 4; break;
default: perf_score *= 5;

}

/* Make sure that we don't go over limit. */

if (perf_score > HAVOC_MAX_MULT * 100) perf_score = HAVOC_MAX_MULT * 100;

return perf_score;

}

common_fuzz_stuff

修改测试用例,执行target程序,处理结果。处理成功返回0,否则返回1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/* Write a modified test case, run program, process results. Handle
error conditions, returning 1 if it's time to bail out. This is
a helper function for fuzz_one(). */

EXP_ST u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) {

u8 fault;

// static u8* (*post_handler)(u8* buf, u32* len);
if (post_handler) { // 若post_handler不为空

out_buf = post_handler(out_buf, &len); // 调用post_handler,即afl_postprocess处理out_buf
if (!out_buf || !len) return 0; // 若out_buf为空,或者len等于0,直接返回

}

write_to_testcase(out_buf, len); // 将out_buf写入out_file

fault = run_target(argv, exec_tmout); // 执行目标程序

if (stop_soon) return 1;

if (fault == FAULT_TMOUT) { // 若fault等于FAULT_TMOUT,即出现了超时错误

if (subseq_tmouts++ > TMOUT_LIMIT) { // subseq_tmouts + 1
cur_skipped_paths++; // cur_skipped_paths + 1
return 1;
}

} else subseq_tmouts = 0;

/* Users can hit us with SIGUSR1 to request the current input
to be abandoned. */

if (skip_requested) { // 若skip_requested不为空,即当前输入被抛弃

skip_requested = 0;
cur_skipped_paths++;
return 1;

}

/* This handles FAULT_ERROR for us: */

queued_discovered += save_if_interesting(argv, out_buf, len, fault); // 发现了新路径,queued_discovered + 1

if (!(stage_cur % stats_update_freq) || stage_cur + 1 == stage_max)
show_stats(); // 若stage_cur是stats_update_freq的整数倍或是执行到最后一轮,展示stats

return 0;

}

save_if_interesting

判断case是否为interesting模式。若是crash,则保存case到queue文件夹以及crash文件夹中,设置keeping为1,返回1;若是tmout,保存到hangs文件夹中,返回0。其它情况也返回0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* Check if the result of an execve() during routine fuzzing is interesting,
save or queue the input test case for further analysis if so. Returns 1 if
entry is saved, 0 otherwise. */

static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {

u8 *fn = "";
u8 hnb;
s32 fd;
u8 keeping = 0, res;

if (fault == crash_mode) { // 若fault为crash_mode

/* Keep only if there are new bits in the map, add to queue for
future fuzzing, etc. */

if (!(hnb = has_new_bits(virgin_bits))) { // 判断是否发现了新的路径或者path的命中率增加
if (crash_mode) total_crashes++; // 若crash_mode不为空,total_crashed + 1
return 0;
}

#ifndef SIMPLE_FILES

fn = alloc_printf("%s/queue/id:%06u,%s", out_dir, queued_paths,
describe_op(hnb)); // fn = out_dir/queue/id:

#else

fn = alloc_printf("%s/queue/id_%06u", out_dir, queued_paths);

#endif /* ^!SIMPLE_FILES */

add_to_queue(fn, len, 0); // 将fn添加到队列中

if (hnb == 2) { // 发现了新路径
queue_top->has_new_cov = 1;
queued_with_cov++;
}

queue_top->exec_cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算trace_bits校验和

/* Try to calibrate inline; this also calls update_bitmap_score() when
successful. */

res = calibrate_case(argv, queue_top, mem, queue_cycle - 1, 0); // 对新添加的路径进行评测

if (res == FAULT_ERROR) // 若res返回值为FAULT_ERROR,报错并退出
FATAL("Unable to execute target application");

fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); // 以只写方式打开fn文件
if (fd < 0) PFATAL("Unable to create '%s'", fn);
ck_write(fd, mem, len, fn); // 将mem写入文件
close(fd);

keeping = 1;

}

switch (fault) { // 判断报错类型

case FAULT_TMOUT: // FAULT_TMOUT

/* Timeouts are not very interesting, but we're still obliged to keep
a handful of samples. We use the presence of new bits in the
hang-specific bitmap as a signal of uniqueness. In "dumb" mode, we
just keep everything. */

total_tmouts++;

if (unique_hangs >= KEEP_UNIQUE_HANG) return keeping; // 若unique_hangs >= 500,返回keeping

if (!dumb_mode) { // 若不是简易模式

#ifdef WORD_SIZE_64
simplify_trace((u64*)trace_bits); // 简化路径
#else
simplify_trace((u32*)trace_bits);
#endif /* ^WORD_SIZE_64 */

if (!has_new_bits(virgin_tmout)) return keeping; // 若没有发现新的超时路径,返回kepping

}

unique_tmouts++; // unique_tmounts + 1

/* Before saving, we make sure that it's a genuine hang by re-running
the target with a more generous timeout (unless the default timeout
is already generous). */

if (exec_tmout < hang_tmout) { // 若exec_tmout < hang_tmout,

u8 new_fault;
write_to_testcase(mem, len); // 将mem写入out_file(.cur_input)中
new_fault = run_target(argv, hang_tmout); // 重新执行目标程序

/* A corner case that one user reported bumping into: increasing the
timeout actually uncovers a crash. Make sure we don't discard it if
so. */

if (!stop_soon && new_fault == FAULT_CRASH) goto keep_as_crash; // 若FAULT是FAULT_CRASH,跳转到keep_as_crash

if (stop_soon || new_fault != FAULT_TMOUT) return keeping;

}

#ifndef SIMPLE_FILES

fn = alloc_printf("%s/hangs/id:%06llu,%s", out_dir,
unique_hangs, describe_op(0));

#else

fn = alloc_printf("%s/hangs/id_%06llu", out_dir,
unique_hangs);

#endif /* ^!SIMPLE_FILES */

unique_hangs++; // unique_hangs + 1

last_hang_time = get_cur_time();

break;

case FAULT_CRASH: // FAULT_CRASH

keep_as_crash:

/* This is handled in a manner roughly similar to timeouts,
except for slightly different limits and no need to re-run test
cases. */

total_crashes++; // 总crashes + 1

if (unique_crashes >= KEEP_UNIQUE_CRASH) return keeping;

if (!dumb_mode) { // 若不是dumb_mode

#ifdef WORD_SIZE_64
simplify_trace((u64*)trace_bits); // 简化路径
#else
simplify_trace((u32*)trace_bits);
#endif /* ^WORD_SIZE_64 */

if (!has_new_bits(virgin_crash)) return keeping; // 判断是否发现新的crash

}

if (!unique_crashes) write_crash_readme(); // 创建readme文件

#ifndef SIMPLE_FILES

fn = alloc_printf("%s/crashes/id:%06llu,sig:%02u,%s", out_dir,
unique_crashes, kill_signal, describe_op(0));

#else

fn = alloc_printf("%s/crashes/id_%06llu_%02u", out_dir, unique_crashes,
kill_signal);

#endif /* ^!SIMPLE_FILES */

unique_crashes++; // unique_crashes + 1

last_crash_time = get_cur_time();
last_crash_execs = total_execs;

break;

case FAULT_ERROR: FATAL("Unable to execute target application");

default: return keeping;

}

/* If we're here, we apparently want to save the crash or hang
test case, too. */

fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600); // 打开fn文件
if (fd < 0) PFATAL("Unable to create '%s'", fn);
ck_write(fd, mem, len, fn); // 保存mem到fn文件
close(fd);

ck_free(fn);

return keeping;

}

simplify_trace

简化路径信息,将命中路径设置为0x80,未命中的设置为1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* Destructively simplify trace by eliminating hit count information
and replacing it with 0x80 or 0x01 depending on whether the tuple
is hit or not. Called on every new crash or timeout, should be
reasonably fast. */

static const u8 simplify_lookup[256] = {

[0] = 1,
[1 ... 255] = 128

};

#ifdef WORD_SIZE_64

static void simplify_trace(u64* mem) {

u32 i = MAP_SIZE >> 3;

while (i--) {

/* Optimize for sparse bitmaps. */

if (unlikely(*mem)) {

u8* mem8 = (u8*)mem;

mem8[0] = simplify_lookup[mem8[0]];
mem8[1] = simplify_lookup[mem8[1]];
mem8[2] = simplify_lookup[mem8[2]];
mem8[3] = simplify_lookup[mem8[3]];
mem8[4] = simplify_lookup[mem8[4]];
mem8[5] = simplify_lookup[mem8[5]];
mem8[6] = simplify_lookup[mem8[6]];
mem8[7] = simplify_lookup[mem8[7]];

} else *mem = 0x0101010101010101ULL;

mem++;

}

}

参考链接

https://eternalsakura13.com/2020/08/23/afl/

https://hollk.blog.csdn.net/category_11470526.html

https://paper.seebug.org/1732/

https://www.z1r0.top/2023/03/23/AFL-fuzz%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/


AFL源码分析(四)
http://example.com/2023/10/04/AFL源码分析四/
作者
l1s00t
发布于
2023年10月4日
更新于
2023年10月6日
许可协议