AFL二三事 -- 源码分析 3
本文为《AFL二三事》-- 源码分析系列的第三篇,主要阅读AFL的fuzzer部分的源码,学习AFL的fuzz核心。
当别人都要快的时候,你要慢下来。
AFL中最重要的部分便是fuzzer的实现部分——afl_fuzz.c
,其主要作用是通过不断变异测试用例来影响程序的执行路径。该文件代码量在8000行左右,处于篇幅原因,我们不会对每一个函数进行源码级分析,而是按照功能划分,介绍其中的核心函数。该文件属于AFL整个项目的核心中的核心,强烈建议通读该文件。
在介绍源码的同时,会穿插AFL的整体运行过程和设计思路,辅助理解源码的设计思路。
在功能上,可以总体上分为3部分:
我们将按照以上3个功能对其中的关键函数和流程进行分析。
该循环主要通过 getopt
获取各种环境配置、选项参数等。
调用 sigaction
,注册信号处理函数,设置信号句柄。具体的信号内容如下:
读取环境变量 ASAN_OPTIONS
和 MSAN_OPTIONS
,做一些必要性检查。
如果通过 -M
或者-S
指定了 sync_id
,则更新 out_dir
和 sync_dir
的值:设置 sync_dir
的值为 out_dir
,设置 out_dir
的值为out_dir/sync_id
。
copy当前命令行参数,保存。
检查是否在tty终端上面运行:读取环境变量 AFL_NO_UI
,如果存在,设置 not_on_tty
为1,并返回;通过 ioctl
读取window size,如果报错为 ENOTTY
,表示当前不在一个tty终端运行,设置 not_on_tty
。
该函数用于设置共享内存和 virgin_bits
,属于比较重要的函数,这里我们结合源码来解析一下:
这里通过 trace_bits
和 virgin_bits
两个 bitmap 来分别记录当前的 tuple 信息及整体 tuple 信息,其中 trace_bits
位于共享内存上,便于进行进程间通信。通过 virgin_tmout
和 virgin_crash
两个 bitmap 来记录 fuzz 过程中出现的所有目标程序超时以及崩溃的 tuple 信息。
该函数用于准备输出文件夹和文件描述符,结合源码进行解析:
该函数的源码中,开发者对关键位置均做了清楚的注释,很容易理解,不做过多解释。
该函数会将 in_dir
目录下的测试用例扫描到 queue
中,并且区分该文件是否为经过确定性变异的input,如果是的话跳过,以节省时间。
调用函数 add_to_queue()
将测试用例排成queue队列。该函数会在启动时进行调用。
该函数主要用于将新的test case添加到队列,初始化 fname
文件名称,增加cur_depth
深度,增加 queued_paths
测试用例数量等。
首先,queue_entry
结构体定义如下:
然后在函数内部进行的相关操作如下:
在输出目录中为输入测试用例创建硬链接。
变量 timeout_given
没有被设置时,会调用到该函数。该函数主要是在没有指定 -t
选项进行 resuming session 时,避免一次次地自动调整超时时间。
识别参数中是否有“@@”,如果有,则替换为 out_dir/.cur_input
,没有则返回:
检查指定路径要执行的程序是否存在,是否为shell脚本,同时检查elf文件头是否合法及程序是否被插桩。
调用 get_cur_time()
函数获取开始时间,检查是否处于 qemu_mode
。
该函数是AFL中的一个关键函数,它会执行 input
文件夹下的预先准备的所有测试用例,生成初始化的 queue 和 bitmap,只对初始输入执行一次。函数控制流程图如下:
下面将结合函数源码进行解析(删除部分非关键代码):
总结以上流程:
该函数同样为AFL的一个关键函数,用于新测试用例的校准,在处理输入目录时执行,以便在早期就发现有问题的测试用例,并且在发现新路径时,评估新发现的测试用例的是否可变。该函数在 perform_dry_run
,save_if_interesting
,fuzz_one
,pilot_fuzzing
,core_fuzzing
函数中均有调用。该函数主要用途是初始化并启动fork server,多次运行测试用例,并用 update_bitmap_score
进行初始的byte排序。
函数控制流程图如下:
结合源码进行解读如下:
总结以上过程如下:
AFL的fork server机制避免了多次执行 execve()
函数的多次调用,只需要调用一次然后通过管道发送命令即可。该函数主要用于启动APP和它的fork server。函数整体控制流程图如下:
结合源码梳理一下函数流程:
我们结合fuzzer对该函数的调用来梳理完整的流程如下:
启动目标程序进程后,目标程序会运行一个fork server,fuzzer自身并不负责fork子进程,而是通过管道与fork server通信,由fork server来完成fork以及继续执行目标程序的操作。
对于fuzzer和目标程序之间的通信状态我们可以通过下图来梳理:
结合前面的插桩部分一起来看:
首先,afl-fuzz
会创建两个管道:状态管道和控制管道,然后执行目标程序。此时的目标程序的 main()
函数已经被插桩,程序控制流进入 __afl_maybe_log
中。如果fuzz是第一次执行,则此时的程序就成了fork server们之后的目标程序都由该fork server通过fork生成子进程来运行。fuzz进行过程中,fork server会一直执行fork操作,并将子进程的结束状态通过状态管道传递给 afl-fuzz
。
(对于fork server的具体操作,在前面插桩部分时已经根据源码进行了说明,可以回顾一下。)
该函数主要执行目标应用程序,并进行超时监控,返回状态信息,被调用的程序会更新 trace_bits[]
。
结合源码进行解释:
当我们发现一个新路径时,需要判断发现的新路径是否更“favorable”,也就是是否包含最小的路径集合能遍历到所有bitmap中的位,并在之后的fuzz过程中聚焦在这些路径上。
以上过程的第一步是为bitmap中的每个字节维护一个 top_rated[]
的列表,这里会计算究竟哪些位置是更“合适”的,该函数主要实现该过程。
函数的控制流程图如下:
结合源码进行解释:
在前面讨论的关于case的 top_rated
的计算中,还有一个机制是检查所有的 top_rated[]
条目,然后顺序获取之前没有遇到过的byte的对比分数低的“获胜者”进行标记,标记至少会维持到下一次运行之前。在所有的fuzz步骤中,“favorable”的条目会获得更多的执行时间。
函数的控制流程图如下:
结合源码解析如下:
这里根据网上公开的一个例子来理解该过程:
现假设有如下tuple和seed信息:
tuple: t0, t1, t2, t3, t4
seed: s0, s1, s2
将按照如下过程进行筛选和判断:
进入主循环前的准备工作使用的函数之一,主要作用为在处理输入目录的末尾显示统计信息,警告信息以及硬编码的常量;
进入主循环前的准备工作使用的函数之一,主要作用为在resume时,尝试查找要开始的队列的位置。
也是准备工作函数之一,主要作用为更新统计信息文件以进行无人值守的监视。
该函数主要保存自动生成的extras。
这里是seed变异的主循环处理过程,我们将结合流程图和源码进行详细解读。
主循环的控制流程图如下(将while部分单独设置为了一个函数,只看循环部分即可):
主循环源码:
总结以上内容,该处该过程整体如下:
该函数源码在1000多行,出于篇幅原因,我们简要介绍函数的功能。但强烈建议通读该函数源码,
函数主要是从queue中取出entry进行fuzz,成功返回0,跳过或退出的话返回1。
整体过程:
这里涉及到AFL中的变异策略,不在本次的讨论中,感兴趣的小伙伴可以结合源码自行进行研究。
该函数的主要作用是进行queue同步,先读取有哪些fuzzer文件夹,然后读取其他fuzzer文件夹下的queue文件夹中的测试用例,然后以此执行。如果在执行过程中,发现这些测试用例可以触发新路径,则将测试用例保存到自己的queue文件夹中,并将最后一个同步的测试用例的id写入到 .synced/fuzzer文件夹名
文件中,避免重复运行。
分析完源码,可以感受到,AFL遵循的基本原则是简单有效,没有进行过多的复杂的优化,能够针对fuzz领域的痛点,对症下药,拒绝花里胡哨,给出切实可行的解决方案,在漏洞挖掘领域的意义的确非同凡响。后期的很多先进的fuzz工具基本沿用了AFL的思路,甚至目前为止已基本围绕AFL建立了“生态圈”,涉及到多个平台、多种漏洞挖掘对象,对于安全研究员来说实属利器,值得从事fuzz相关工作的研究员下足功夫去体会AFL的精髓所在。
考虑到篇幅限制,我们没有对AFL中的变异策略进行源码说明,实属遗憾。如果有机会,将新开文章详细介绍AFL的变异策略和源码分析。
while
((opt
=
getopt(argc, argv,
"+i:o:f:m:b:t:T:dnCB:S:M:x:QV"
)) >
0
)
... ...
while
((opt
=
getopt(argc, argv,
"+i:o:f:m:b:t:T:dnCB:S:M:x:QV"
)) >
0
)
... ...
信号 |
作用 |
SIGHUP/SIGINT/SIGTERM |
处理各种“stop”情况 |
SIGALRM |
处理超时的情况 |
SIGWINCH |
处理窗口大小 |
SIGUSER1 |
用户自定义信号,这里定义为skip request |
SIGSTP/SIGPIPE |
不是很重要的一些信号,可以不用关心 |
/
*
Configure shared memory
and
virgin_bits. This
is
called at startup.
*
/
EXP_ST void setup_shm(void) {
u8
*
shm_str;
if
(!in_bitmap) memset(virgin_bits,
255
, MAP_SIZE);
/
/
如果 in_bitmap 为空,调用 memset 初始化数组 virgin_bits[MAP_SIZE] 的每个元素的值为 ‘
255
’。
memset(virgin_tmout,
255
, MAP_SIZE);
/
/
调用 memset 初始化数组 virgin_tmout[MAP_SIZE] 的每个元素的值为 ‘
255
’。
memset(virgin_crash,
255
, MAP_SIZE);
/
/
调用 memset 初始化数组 virgin_crash[MAP_SIZE] 的每个元素的值为 ‘
255
’。
shm_id
=
shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL |
0600
);
/
/
调用 shmget 函数分配一块共享内存,并将返回的共享内存标识符保存到 shm_id
if
(shm_id <
0
) PFATAL(
"shmget() failed"
);
atexit(remove_shm);
/
/
注册 atexit handler 为 remove_shm
shm_str
=
alloc_printf(
"%d"
, shm_id);
/
/
创建字符串 shm_str
/
*
If somebody
is
asking us to fuzz instrumented binaries
in
dumb mode,
we don
't want them to detect instrumentation, since we won'
t be sending
fork server commands. This should be replaced with better auto
-
detection
later on, perhaps?
*
/
if
(!dumb_mode) setenv(SHM_ENV_VAR, shm_str,
1
);
/
/
如果不是dumb_mode,设置环境变量 SHM_ENV_VAR 的值为 shm_str
ck_free(shm_str);
trace_bits
=
shmat(shm_id, NULL,
0
);
/
/
设置 trace_bits 并初始化为
0
if
(trace_bits
=
=
(void
*
)
-
1
) PFATAL(
"shmat() failed"
);
}
/
*
Configure shared memory
and
virgin_bits. This
is
called at startup.
*
/
EXP_ST void setup_shm(void) {
u8
*
shm_str;
if
(!in_bitmap) memset(virgin_bits,
255
, MAP_SIZE);
/
/
如果 in_bitmap 为空,调用 memset 初始化数组 virgin_bits[MAP_SIZE] 的每个元素的值为 ‘
255
’。
memset(virgin_tmout,
255
, MAP_SIZE);
/
/
调用 memset 初始化数组 virgin_tmout[MAP_SIZE] 的每个元素的值为 ‘
255
’。
memset(virgin_crash,
255
, MAP_SIZE);
/
/
调用 memset 初始化数组 virgin_crash[MAP_SIZE] 的每个元素的值为 ‘
255
’。
shm_id
=
shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL |
0600
);
/
/
调用 shmget 函数分配一块共享内存,并将返回的共享内存标识符保存到 shm_id
if
(shm_id <
0
) PFATAL(
"shmget() failed"
);
atexit(remove_shm);
/
/
注册 atexit handler 为 remove_shm
shm_str
=
alloc_printf(
"%d"
, shm_id);
/
/
创建字符串 shm_str
/
*
If somebody
is
asking us to fuzz instrumented binaries
in
dumb mode,
we don
't want them to detect instrumentation, since we won'
t be sending
fork server commands. This should be replaced with better auto
-
detection
later on, perhaps?
*
/
if
(!dumb_mode) setenv(SHM_ENV_VAR, shm_str,
1
);
/
/
如果不是dumb_mode,设置环境变量 SHM_ENV_VAR 的值为 shm_str
ck_free(shm_str);
trace_bits
=
shmat(shm_id, NULL,
0
);
/
/
设置 trace_bits 并初始化为
0
if
(trace_bits
=
=
(void
*
)
-
1
) PFATAL(
"shmat() failed"
);
}
EXP_ST void setup_dirs_fds(void) {
u8
*
tmp;
s32 fd;
ACTF(
"Setting up output directories..."
);
if
(sync_id && mkdir(sync_dir,
0700
) && errno !
=
EEXIST)
PFATAL(
"Unable to create '%s'"
, sync_dir);
/
*
如果sync_id,且创建sync_dir文件夹并设置权限为
0700
,如果报错单errno不是 EEXIST ,抛出异常
*
/
if
(mkdir(out_dir,
0700
)) {
/
/
创建out_dir, 权限为
0700
if
(errno !
=
EEXIST) PFATAL(
"Unable to create '%s'"
, out_dir);
maybe_delete_out_dir();
}
else
{
if
(in_place_resume)
/
/
创建成功
FATAL(
"Resume attempted but old output directory not found"
);
out_dir_fd
=
open
(out_dir, O_RDONLY);
/
/
以只读模式打开,返回fd:out_dir_fd
if
(out_dir_fd <
0
|| flock(out_dir_fd, LOCK_EX | LOCK_NB))
PFATAL(
"Unable to flock() output directory."
);
}
/
*
Queue directory
for
any
starting & discovered paths.
*
/
tmp
=
alloc_printf(
"%s/queue"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
/
/
创建 out_dir
/
queue 文件夹,权限为
0700
ck_free(tmp);
/
*
Top
-
level directory
for
queue metadata used
for
session
resume
and
related tasks.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/"
, out_dir);
/
/
创建 out_dir
/
queue
/
.state 文件夹,用于保存session resume 和相关tasks的队列元数据。
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
Directory
for
flagging queue entries that went through
deterministic fuzzing
in
the past.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/deterministic_done/"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
Directory with the auto
-
selected dictionary entries.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/auto_extras/"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
The
set
of paths currently deemed redundant.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/redundant_edges/"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
The
set
of paths showing variable behavior.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/variable_behavior/"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
Sync directory
for
keeping track of cooperating fuzzers.
*
/
if
(sync_id) {
tmp
=
alloc_printf(
"%s/.synced/"
, out_dir);
if
(mkdir(tmp,
0700
) && (!in_place_resume || errno !
=
EEXIST))
PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
}
/
*
All
recorded crashes.
*
/
tmp
=
alloc_printf(
"%s/crashes"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
All
recorded hangs.
*
/
tmp
=
alloc_printf(
"%s/hangs"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
Generally useful
file
descriptors.
*
/
dev_null_fd
=
open
(
"/dev/null"
, O_RDWR);
if
(dev_null_fd <
0
) PFATAL(
"Unable to open /dev/null"
);
dev_urandom_fd
=
open
(
"/dev/urandom"
, O_RDONLY);
if
(dev_urandom_fd <
0
) PFATAL(
"Unable to open /dev/urandom"
);
/
*
Gnuplot output
file
.
*
/
tmp
=
alloc_printf(
"%s/plot_data"
, out_dir);
fd
=
open
(tmp, O_WRONLY | O_CREAT | O_EXCL,
0600
);
if
(fd <
0
) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
plot_file
=
fdopen(fd,
"w"
);
if
(!plot_file) PFATAL(
"fdopen() failed"
);
fprintf(plot_file,
"# unix_time, cycles_done, cur_path, paths_total, "
"pending_total, pending_favs, map_size, unique_crashes, "
"unique_hangs, max_depth, execs_per_sec\n"
);
/
*
ignore errors
*
/
EXP_ST void setup_dirs_fds(void) {
u8
*
tmp;
s32 fd;
ACTF(
"Setting up output directories..."
);
if
(sync_id && mkdir(sync_dir,
0700
) && errno !
=
EEXIST)
PFATAL(
"Unable to create '%s'"
, sync_dir);
/
*
如果sync_id,且创建sync_dir文件夹并设置权限为
0700
,如果报错单errno不是 EEXIST ,抛出异常
*
/
if
(mkdir(out_dir,
0700
)) {
/
/
创建out_dir, 权限为
0700
if
(errno !
=
EEXIST) PFATAL(
"Unable to create '%s'"
, out_dir);
maybe_delete_out_dir();
}
else
{
if
(in_place_resume)
/
/
创建成功
FATAL(
"Resume attempted but old output directory not found"
);
out_dir_fd
=
open
(out_dir, O_RDONLY);
/
/
以只读模式打开,返回fd:out_dir_fd
if
(out_dir_fd <
0
|| flock(out_dir_fd, LOCK_EX | LOCK_NB))
PFATAL(
"Unable to flock() output directory."
);
}
/
*
Queue directory
for
any
starting & discovered paths.
*
/
tmp
=
alloc_printf(
"%s/queue"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
/
/
创建 out_dir
/
queue 文件夹,权限为
0700
ck_free(tmp);
/
*
Top
-
level directory
for
queue metadata used
for
session
resume
and
related tasks.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/"
, out_dir);
/
/
创建 out_dir
/
queue
/
.state 文件夹,用于保存session resume 和相关tasks的队列元数据。
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
Directory
for
flagging queue entries that went through
deterministic fuzzing
in
the past.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/deterministic_done/"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
Directory with the auto
-
selected dictionary entries.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/auto_extras/"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
The
set
of paths currently deemed redundant.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/redundant_edges/"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
The
set
of paths showing variable behavior.
*
/
tmp
=
alloc_printf(
"%s/queue/.state/variable_behavior/"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
Sync directory
for
keeping track of cooperating fuzzers.
*
/
if
(sync_id) {
tmp
=
alloc_printf(
"%s/.synced/"
, out_dir);
if
(mkdir(tmp,
0700
) && (!in_place_resume || errno !
=
EEXIST))
PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
}
/
*
All
recorded crashes.
*
/
tmp
=
alloc_printf(
"%s/crashes"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
All
recorded hangs.
*
/
tmp
=
alloc_printf(
"%s/hangs"
, out_dir);
if
(mkdir(tmp,
0700
)) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
/
*
Generally useful
file
descriptors.
*
/
dev_null_fd
=
open
(
"/dev/null"
, O_RDWR);
if
(dev_null_fd <
0
) PFATAL(
"Unable to open /dev/null"
);
dev_urandom_fd
=
open
(
"/dev/urandom"
, O_RDONLY);
if
(dev_urandom_fd <
0
) PFATAL(
"Unable to open /dev/urandom"
);
/
*
Gnuplot output
file
.
*
/
tmp
=
alloc_printf(
"%s/plot_data"
, out_dir);
fd
=
open
(tmp, O_WRONLY | O_CREAT | O_EXCL,
0600
);
if
(fd <
0
) PFATAL(
"Unable to create '%s'"
, tmp);
ck_free(tmp);
plot_file
=
fdopen(fd,
"w"
);
if
(!plot_file) PFATAL(
"fdopen() failed"
);
fprintf(plot_file,
"# unix_time, cycles_done, cur_path, paths_total, "
"pending_total, pending_favs, map_size, unique_crashes, "
"unique_hangs, max_depth, execs_per_sec\n"
);
/
*
ignore errors
*
/
struct queue_entry {
u8
*
fname;
/
*
File
name
for
the test case
*
/
u32
len
;
/
*
Input
length
*
/
u8 cal_failed,
/
*
Calibration failed?
*
/
trim_done,
/
*
Trimmed?
*
/
was_fuzzed,
/
*
Had
any
fuzzing done yet?
*
/
passed_det,
/
*
Deterministic stages passed?
*
/
has_new_cov,
/
*
Triggers new coverage?
*
/
var_behavior,
/
*
Variable behavior?
*
/
favored,
/
*
Currently favored?
*
/
fs_redundant;
/
*
Marked as redundant
in
the fs?
*
/
u32 bitmap_size,
/
*
Number of bits
set
in
bitmap
*
/
exec_cksum;
/
*
Checksum of the execution trace
*
/
u64 exec_us,
/
*
Execution time (us)
*
/
handicap,
/
*
Number of queue cycles behind
*
/
depth;
/
*
Path depth
*
/
u8
*
trace_mini;
/
*
Trace bytes,
if
kept
*
/
u32 tc_ref;
/
*
Trace bytes ref count
*
/
struct queue_entry
*
next
,
/
*
Next
element,
if
any
*
/
*
next_100;
/
*
100
elements ahead
*
/
};
struct queue_entry {
u8
*
fname;
/
*
File
name
for
the test case
*
/
u32
len
;
/
*
Input
length
*
/
u8 cal_failed,
/
*
Calibration failed?
*
/
trim_done,
/
*
Trimmed?
*
/
was_fuzzed,
/
*
Had
any
fuzzing done yet?
*
/
passed_det,
/
*
Deterministic stages passed?
*
/
has_new_cov,
/
*
Triggers new coverage?
*
/
var_behavior,
/
*
Variable behavior?
*
/
favored,
/
*
Currently favored?
*
/
fs_redundant;
/
*
Marked as redundant
in
the fs?
*
/
u32 bitmap_size,
/
*
Number of bits
set
in
bitmap
*
/
exec_cksum;
/
*
Checksum of the execution trace
*
/
u64 exec_us,
/
*
Execution time (us)
*
/
handicap,
/
*
Number of queue cycles behind
*
/
depth;
/
*
Path depth
*
/
u8
*
trace_mini;
/
*
Trace bytes,
if
kept
*
/
u32 tc_ref;
/
*
Trace bytes ref count
*
/
struct queue_entry
*
next
,
/
*
Next
element,
if
any
*
/
*
next_100;
/
*
100
elements ahead
*
/
};
/
*
Append new test case to the queue.
*
/
static void add_to_queue(u8
*
fname, u32
len
, u8 passed_det) {
struct queue_entry
*
q
=
ck_alloc(sizeof(struct queue_entry));
/
/
通过ck_alloc分配一个 queue_entry 结构体,并进行初始化
q
-
>fname
=
fname;
q
-
>
len
=
len
;
q
-
>depth
=
cur_depth
+
1
;
q
-
>passed_det
=
passed_det;
if
(q
-
>depth > max_depth) max_depth
=
q
-
>depth;
if
(queue_top) {
queue_top
-
>
next
=
q;
queue_top
=
q;
}
else
q_prev100
=
queue
=
queue_top
=
q;
queued_paths
+
+
;
/
/
queue计数器加
1
pending_not_fuzzed
+
+
;
/
/
待fuzz的样例计数器加
1
cycles_wo_finds
=
0
;
/
*
Set
next_100 pointer
for
every
100th
element (index
0
,
100
, etc) to allow faster iteration.
*
/
if
((queued_paths
-
1
)
%
100
=
=
0
&& queued_paths >
1
) {
q_prev100
-
>next_100
=
q;
q_prev100
=
q;
}
last_path_time
=
get_cur_time();
}
/
*
Append new test case to the queue.
*
/
static void add_to_queue(u8
*
fname, u32
len
, u8 passed_det) {
struct queue_entry
*
q
=
ck_alloc(sizeof(struct queue_entry));
/
/
通过ck_alloc分配一个 queue_entry 结构体,并进行初始化
q
-
>fname
=
fname;
q
-
>
len
=
len
;
q
-
>depth
=
cur_depth
+
1
;
q
-
>passed_det
=
passed_det;
if
(q
-
>depth > max_depth) max_depth
=
q
-
>depth;
if
(queue_top) {
queue_top
-
>
next
=
q;
queue_top
=
q;
}
else
q_prev100
=
queue
=
queue_top
=
q;
queued_paths
+
+
;
/
/
queue计数器加
1
pending_not_fuzzed
+
+
;
/
/
待fuzz的样例计数器加
1
cycles_wo_finds
=
0
;
/
*
Set
next_100 pointer
for
every
100th
element (index
0
,
100
, etc) to allow faster iteration.
*
/
if
((queued_paths
-
1
)
%
100
=
=
0
&& queued_paths >
1
) {
q_prev100
-
>next_100
=
q;
q_prev100
=
q;
}
last_path_time
=
get_cur_time();
}
/
*
Detect @@
in
args.
*
/
EXP_ST void detect_file_args(char
*
*
argv) {
u32 i
=
0
;
u8
*
cwd
=
getcwd(NULL,
0
);
if
(!cwd) PFATAL(
"getcwd() failed"
);
while
(argv[i]) {
u8
*
aa_loc
=
strstr(argv[i],
"@@"
);
/
/
查找@@
if
(aa_loc) {
u8
*
aa_subst,
*
n_arg;
/
*
If we don't have a
file
name chosen yet, use a safe default.
*
/
if
(!out_file)
out_file
=
alloc_printf(
"%s/.cur_input"
, out_dir);
/
*
Be sure that we're always using fully
-
qualified paths.
*
/
if
(out_file[
0
]
=
=
'/'
) aa_subst
=
out_file;
else
aa_subst
=
alloc_printf(
"%s/%s"
, cwd, out_file);
/
*
Construct a replacement argv value.
*
/
*
aa_loc
=
0
;
n_arg
=
alloc_printf(
"%s%s%s"
, argv[i], aa_subst, aa_loc
+
2
);
argv[i]
=
n_arg;
*
aa_loc
=
'@'
;
if
(out_file[
0
] !
=
'/'
) ck_free(aa_subst);
}
i
+
+
;
}
free(cwd);
/
*
not
tracked
*
/
}
/
*
Detect @@
in
args.
*
/
EXP_ST void detect_file_args(char
*
*
argv) {
u32 i
=
0
;
u8
*
cwd
=
getcwd(NULL,
0
);
if
(!cwd) PFATAL(
"getcwd() failed"
);
while
(argv[i]) {
u8
*
aa_loc
=
strstr(argv[i],
"@@"
);
/
/
查找@@
if
(aa_loc) {
u8
*
aa_subst,
*
n_arg;
/
*
If we don't have a
file
name chosen yet, use a safe default.
*
/
if
(!out_file)
out_file
=
alloc_printf(
"%s/.cur_input"
, out_dir);
/
*
Be sure that we're always using fully
-
qualified paths.
*
/
if
(out_file[
0
]
=
=
'/'
) aa_subst
=
out_file;
else
aa_subst
=
alloc_printf(
"%s/%s"
, cwd, out_file);
/
*
Construct a replacement argv value.
*
/
*
aa_loc
=
0
;
n_arg
=
alloc_printf(
"%s%s%s"
, argv[i], aa_subst, aa_loc
+
2
);
argv[i]
=
n_arg;
*
aa_loc
=
'@'
;
if
(out_file[
0
] !
=
'/'
) ck_free(aa_subst);
}
i
+
+
;
}
free(cwd);
/
*
not
tracked
*
/
}
/
*
Perform dry run of
all
test cases to confirm that the app
is
working as
expected. This
is
done only
for
the initial inputs,
and
only once.
*
/
static void perform_dry_run(char
*
*
argv) {
struct queue_entry
*
q
=
queue;
/
/
创建queue_entry结构体
u32 cal_failures
=
0
;
u8
*
skip_crashes
=
getenv(
"AFL_SKIP_CRASHES"
);
/
/
读取环境变量 AFL_SKIP_CRASHES
while
(q) {
/
/
遍历队列
u8
*
use_mem;
u8 res;
s32 fd;
u8
*
fn
=
strrchr(q
-
>fname,
'/'
)
+
1
;
ACTF(
"Attempting dry run with '%s'..."
, fn);
fd
=
open
(q
-
>fname, O_RDONLY);
if
(fd <
0
) PFATAL(
"Unable to open '%s'"
, q
-
>fname);
use_mem
=
ck_alloc_nozero(q
-
>
len
);
if
(read(fd, use_mem, q
-
>
len
) !
=
q
-
>
len
)
FATAL(
"Short read from '%s'"
, q
-
>fname);
/
/
打开q
-
>fname,读取到分配的内存中
close(fd);
res
=
calibrate_case(argv, q, use_mem,
0
,
1
);
/
/
调用函数calibrate_case校准测试用例
ck_free(use_mem);
if
(stop_soon)
return
;
if
(res
=
=
crash_mode || res
=
=
FAULT_NOBITS)
SAYF(cGRA
" len = %u, map size = %u, exec speed = %llu us\n"
cRST,
q
-
>
len
, q
-
>bitmap_size, q
-
>exec_us);
switch (res) {
/
/
判断res的值
case FAULT_NONE:
if
(q
=
=
queue) check_map_coverage();
/
/
如果为头结点,调用check_map_coverage评估覆盖率
if
(crash_mode) FATAL(
"Test case '%s' does *NOT* crash"
, fn);
/
/
抛出异常
break
;
case FAULT_TMOUT:
if
(timeout_given) {
/
/
指定了
-
t 选项
/
*
The
-
t nn
+
syntax
in
the command line sets timeout_given to
'2'
and
instructs afl
-
fuzz to tolerate but skip queue entries that time
out.
*
/
if
(timeout_given >
1
) {
WARNF(
"Test case results in a timeout (skipping)"
);
q
-
>cal_failed
=
CAL_CHANCES;
cal_failures
+
+
;
break
;
}
SAYF(... ...);
FATAL(
"Test case '%s' results in a timeout"
, fn);
}
else
{
SAYF(... ...);
FATAL(
"Test case '%s' results in a timeout"
, fn);
}
case FAULT_CRASH:
if
(crash_mode)
break
;
if
(skip_crashes) {
WARNF(
"Test case results in a crash (skipping)"
);
q
-
>cal_failed
=
CAL_CHANCES;
cal_failures
+
+
;
break
;
}
if
(mem_limit) {
/
/
建议增加内存
SAYF(... ...);
}
else
{
SAYF(... ...);
}
FATAL(
"Test case '%s' results in a crash"
, fn);
case FAULT_ERROR:
FATAL(
"Unable to execute target application ('%s')"
, argv[
0
]);
case FAULT_NOINST:
/
/
测试用例运行没有路径信息
FATAL(
"No instrumentation detected"
);
case FAULT_NOBITS:
/
/
没有出现新路径,判定为无效路径
useless_at_start
+
+
;
if
(!in_bitmap && !shuffle_queue)
WARNF(
"No new instrumentation output, test case may be useless."
);
break
;
}
if
(q
-
>var_behavior) WARNF(
"Instrumentation output varies across runs."
);
q
=
q
-
>
next
;
/
/
读取下一个queue
}
if
(cal_failures) {
if
(cal_failures
=
=
queued_paths)
FATAL(
"All test cases time out%s, giving up!"
,
skip_crashes ?
" or crash"
: "");
WARNF(
"Skipped %u test cases (%0.02f%%) due to timeouts%s."
, cal_failures,
((double)cal_failures)
*
100
/
queued_paths,
skip_crashes ?
" or crashes"
: "");
if
(cal_failures
*
5
> queued_paths)
WARNF(cLRD
"High percentage of rejected test cases, check settings!"
);
}
OKF(
"All test cases processed."
);
}
/
*
Perform dry run of
all
test cases to confirm that the app
is
working as
expected. This
is
done only
for
the initial inputs,
and
only once.
*
/
static void perform_dry_run(char
*
*
argv) {
struct queue_entry
*
q
=
queue;
/
/
创建queue_entry结构体
u32 cal_failures
=
0
;
u8
*
skip_crashes
=
getenv(
"AFL_SKIP_CRASHES"
);
/
/
读取环境变量 AFL_SKIP_CRASHES
while
(q) {
/
/
遍历队列
u8
*
use_mem;
u8 res;
s32 fd;
u8
*
fn
=
strrchr(q
-
>fname,
'/'
)
+
1
;
ACTF(
"Attempting dry run with '%s'..."
, fn);
fd
=
open
(q
-
>fname, O_RDONLY);
if
(fd <
0
) PFATAL(
"Unable to open '%s'"
, q
-
>fname);
use_mem
=
ck_alloc_nozero(q
-
>
len
);
if
(read(fd, use_mem, q
-
>
len
) !
=
q
-
>
len
)
FATAL(
"Short read from '%s'"
, q
-
>fname);
/
/
打开q
-
>fname,读取到分配的内存中
close(fd);
res
=
calibrate_case(argv, q, use_mem,
0
,
1
);
/
/
调用函数calibrate_case校准测试用例
ck_free(use_mem);
if
(stop_soon)
return
;
if
(res
=
=
crash_mode || res
=
=
FAULT_NOBITS)
SAYF(cGRA
" len = %u, map size = %u, exec speed = %llu us\n"
cRST,
q
-
>
len
, q
-
>bitmap_size, q
-
>exec_us);
switch (res) {
/
/
判断res的值
case FAULT_NONE:
if
(q
=
=
queue) check_map_coverage();
/
/
如果为头结点,调用check_map_coverage评估覆盖率
if
(crash_mode) FATAL(
"Test case '%s' does *NOT* crash"
, fn);
/
/
抛出异常
break
;
case FAULT_TMOUT:
if
(timeout_given) {
/
/
指定了
-
t 选项
/
*
The
-
t nn
+
syntax
in
the command line sets timeout_given to
'2'
and
instructs afl
-
fuzz to tolerate but skip queue entries that time
out.
*
/
if
(timeout_given >
1
) {
WARNF(
"Test case results in a timeout (skipping)"
);
q
-
>cal_failed
=
CAL_CHANCES;
cal_failures
+
+
;
break
;
}
SAYF(... ...);
FATAL(
"Test case '%s' results in a timeout"
, fn);
}
else
{
SAYF(... ...);
FATAL(
"Test case '%s' results in a timeout"
, fn);
}
case FAULT_CRASH:
if
(crash_mode)
break
;
if
(skip_crashes) {
WARNF(
"Test case results in a crash (skipping)"
);
q
-
>cal_failed
=
CAL_CHANCES;
cal_failures
+
+
;
break
;
}
if
(mem_limit) {
/
/
建议增加内存
SAYF(... ...);
}
else
{
SAYF(... ...);
}
FATAL(
"Test case '%s' results in a crash"
, fn);
case FAULT_ERROR:
FATAL(
"Unable to execute target application ('%s')"
, argv[
0
]);
case FAULT_NOINST:
/
/
测试用例运行没有路径信息
FATAL(
"No instrumentation detected"
);
case FAULT_NOBITS:
/
/
没有出现新路径,判定为无效路径
useless_at_start
+
+
;
if
(!in_bitmap && !shuffle_queue)
WARNF(
"No new instrumentation output, test case may be useless."
);
break
;
}
if
(q
-
>var_behavior) WARNF(
"Instrumentation output varies across runs."
);
q
=
q
-
>
next
;
/
/
读取下一个queue
}
if
(cal_failures) {
if
(cal_failures
=
=
queued_paths)
FATAL(
"All test cases time out%s, giving up!"
,
skip_crashes ?
" or crash"
: "");
WARNF(
"Skipped %u test cases (%0.02f%%) due to timeouts%s."
, cal_failures,
((double)cal_failures)
*
100
/
queued_paths,
skip_crashes ?
" or crashes"
: "");
if
(cal_failures
*
5
> queued_paths)
WARNF(cLRD
"High percentage of rejected test cases, check settings!"
);
}
OKF(
"All test cases processed."
);
}
/
*
Calibrate a new test case. This
is
done when processing the
input
directory
to warn about flaky
or
otherwise problematic test cases early on;
and
when
new paths are discovered to detect variable behavior
and
so on.
*
/
static u8 calibrate_case(char
*
*
argv, struct queue_entry
*
q, u8
*
use_mem,
u32 handicap, u8 from_queue) {
static u8 first_trace[MAP_SIZE];
/
/
创建 firts_trace[MAP_SIZE]
u8 fault
=
0
, new_bits
=
0
, var_detected
=
0
, hnb
=
0
,
first_run
=
(q
-
>exec_cksum
=
=
0
);
/
/
获取执行追踪结果,判断case是否为第一次运行,若为
0
则表示第一次运行,来自
input
文件夹
u64 start_us, stop_us;
s32 old_sc
=
stage_cur, old_sm
=
stage_max;
u32 use_tmout
=
exec_tmout;
u8
*
old_sn
=
stage_name;
/
/
保存原有 stage_cur, stage_max, stage_name
/
*
Be a bit more generous about timeouts when resuming sessions,
or
when
trying to calibrate already
-
added finds. This helps avoid trouble due
to intermittent latency.
*
/
if
(!from_queue || resuming_fuzz)
/
/
如果from_queue为
0
(表示case不是来自queue)或者resuming_fuzz为
1
(表示处于resuming sessions)
use_tmout
=
MAX
(exec_tmout
+
CAL_TMOUT_ADD,
exec_tmout
*
CAL_TMOUT_PERC
/
100
);
/
/
提升 use_tmout 的值
q
-
>cal_failed
+
+
;
stage_name
=
"calibration"
;
/
/
设置 stage_name
stage_max
=
fast_cal ?
3
: CAL_CYCLES;
/
/
设置 stage_max,新测试用例的校准周期数
/
*
Make sure the fork server
is
up before we do anything,
and
let's
not
count its spin
-
up time toward binary calibration.
*
/
if
(dumb_mode !
=
1
&& !no_fork server && !forksrv_pid)
init_fork server(argv);
/
/
没有运行在dumb_mode,没有禁用fork server,切forksrv_pid为
0
时,启动fork server
if
(q
-
>exec_cksum) {
/
/
判断是否为新case(如果这个queue不是来自
input
文件夹)
memcpy(first_trace, trace_bits, MAP_SIZE);
hnb
=
has_new_bits(virgin_bits);
if
(hnb > new_bits) new_bits
=
hnb;
}
start_us
=
get_cur_time_us();
for
(stage_cur
=
0
; stage_cur < stage_max; stage_cur
+
+
) {
/
/
开始执行 calibration stage,总计执行 stage_max 轮
u32 cksum;
if
(!first_run && !(stage_cur
%
stats_update_freq)) show_stats();
/
/
queue不是来自
input
,第一轮calibration stage执行结束,刷新一次展示界面
write_to_testcase(use_mem, q
-
>
len
);
fault
=
run_target(argv, use_tmout);
/
*
stop_soon
is
set
by the handler
for
Ctrl
+
C. When it's pressed,
we want to bail out quickly.
*
/
if
(stop_soon || fault !
=
crash_mode) goto abort_calibration;
if
(!dumb_mode && !stage_cur && !count_bytes(trace_bits)) {
/
/
如果 calibration stage第一次运行,且不在dumb_mode,共享内存中没有任何路径
fault
=
FAULT_NOINST;
goto abort_calibration;
}
cksum
=
hash32(trace_bits, MAP_SIZE, HASH_CONST);
if
(q
-
>exec_cksum !
=
cksum) {
hnb
=
has_new_bits(virgin_bits);
if
(hnb > new_bits) new_bits
=
hnb;
if
(q
-
>exec_cksum) {
/
/
不等于exec_cksum,表示第一次运行,或在相同参数下,每次执行,cksum不同,表示是一个路径可变的queue
u32 i;
for
(i
=
0
; i < MAP_SIZE; i
+
+
) {
if
(!var_bytes[i] && first_trace[i] !
=
trace_bits[i]) {
/
/
从
0
到MAP_SIZE进行遍历, first_trace[i] !
=
trace_bits[i],表示发现了可变queue
var_bytes[i]
=
1
;
stage_max
=
CAL_CYCLES_LONG;
}
}
var_detected
=
1
;
}
else
{
q
-
>exec_cksum
=
cksum;
/
/
q
-
>exec_cksum
=
0
,表示第一次执行queue,则设置计算出来的本次执行的cksum
memcpy(first_trace, trace_bits, MAP_SIZE);
}
}
}
stop_us
=
get_cur_time_us();
total_cal_us
+
=
stop_us
-
start_us;
/
/
保存所有轮次的总执行时间
total_cal_cycles
+
=
stage_max;
/
/
保存总轮次
/
*
OK, let's collect some stats about the performance of this test case.
This
is
used
for
fuzzing air time calculations
in
calculate_score().
*
/
q
-
>exec_us
=
(stop_us
-
start_us)
/
stage_max;
/
/
单次执行时间的平均值
q
-
>bitmap_size
=
count_bytes(trace_bits);
/
/
最后一次执行所覆盖的路径数
q
-
>handicap
=
handicap;
q
-
>cal_failed
=
0
;
total_bitmap_size
+
=
q
-
>bitmap_size;
/
/
加上queue所覆盖的路径数
total_bitmap_entries
+
+
;
update_bitmap_score(q);
/
*
If this case didn't result
in
new output
from
the instrumentation, tell
parent. This
is
a non
-
critical problem, but something to warn the user
about.
*
/
if
(!dumb_mode && first_run && !fault && !new_bits) fault
=
FAULT_NOBITS;
abort_calibration:
if
(new_bits
=
=
2
&& !q
-
>has_new_cov) {
q
-
>has_new_cov
=
1
;
queued_with_cov
+
+
;
}
/
*
Mark variable paths.
*
/
if
(var_detected) {
/
/
queue是可变路径
var_byte_count
=
count_bytes(var_bytes);
if
(!q
-
>var_behavior) {
mark_as_variable(q);
queued_variable
+
+
;
}
}
/
/
恢复之前的stage值
stage_name
=
old_sn;
stage_cur
=
old_sc;
stage_max
=
old_sm;
if
(!first_run) show_stats();
return
fault;
}
/
*
Calibrate a new test case. This
is
done when processing the
input
directory
to warn about flaky
or
otherwise problematic test cases early on;
and
when
new paths are discovered to detect variable behavior
and
so on.
*
/
static u8 calibrate_case(char
*
*
argv, struct queue_entry
*
q, u8
*
use_mem,
u32 handicap, u8 from_queue) {
static u8 first_trace[MAP_SIZE];
/
/
创建 firts_trace[MAP_SIZE]
u8 fault
=
0
, new_bits
=
0
, var_detected
=
0
, hnb
=
0
,
first_run
=
(q
-
>exec_cksum
=
=
0
);
/
/
获取执行追踪结果,判断case是否为第一次运行,若为
0
则表示第一次运行,来自
input
文件夹
u64 start_us, stop_us;
s32 old_sc
=
stage_cur, old_sm
=
stage_max;
u32 use_tmout
=
exec_tmout;
u8
*
old_sn
=
stage_name;
/
/
保存原有 stage_cur, stage_max, stage_name
/
*
Be a bit more generous about timeouts when resuming sessions,
or
when
trying to calibrate already
-
added finds. This helps avoid trouble due
to intermittent latency.
*
/
if
(!from_queue || resuming_fuzz)
/
/
如果from_queue为
0
(表示case不是来自queue)或者resuming_fuzz为
1
(表示处于resuming sessions)
use_tmout
=
MAX
(exec_tmout
+
CAL_TMOUT_ADD,
exec_tmout
*
CAL_TMOUT_PERC
/
100
);
/
/
提升 use_tmout 的值
q
-
>cal_failed
+
+
;
stage_name
=
"calibration"
;
/
/
设置 stage_name
stage_max
=
fast_cal ?
3
: CAL_CYCLES;
/
/
设置 stage_max,新测试用例的校准周期数
/
*
Make sure the fork server
is
up before we do anything,
and
let's
not
count its spin
-
up time toward binary calibration.
*
/
if
(dumb_mode !
=
1
&& !no_fork server && !forksrv_pid)
init_fork server(argv);
/
/
没有运行在dumb_mode,没有禁用fork server,切forksrv_pid为
0
时,启动fork server
if
(q
-
>exec_cksum) {
/
/
判断是否为新case(如果这个queue不是来自
input
文件夹)
memcpy(first_trace, trace_bits, MAP_SIZE);
hnb
=
has_new_bits(virgin_bits);
if
(hnb > new_bits) new_bits
=
hnb;
}
start_us
=
get_cur_time_us();
for
(stage_cur
=
0
; stage_cur < stage_max; stage_cur
+
+
) {
/
/
开始执行 calibration stage,总计执行 stage_max 轮
u32 cksum;
if
(!first_run && !(stage_cur
%
stats_update_freq)) show_stats();
/
/
queue不是来自
input
,第一轮calibration stage执行结束,刷新一次展示界面
write_to_testcase(use_mem, q
-
>
len
);
fault
=
run_target(argv, use_tmout);
/
*
stop_soon
is
set
by the handler
for
Ctrl
+
C. When it's pressed,
we want to bail out quickly.
*
/
if
(stop_soon || fault !
=
crash_mode) goto abort_calibration;
if
(!dumb_mode && !stage_cur && !count_bytes(trace_bits)) {
/
/
如果 calibration stage第一次运行,且不在dumb_mode,共享内存中没有任何路径
fault
=
FAULT_NOINST;
goto abort_calibration;
}
cksum
=
hash32(trace_bits, MAP_SIZE, HASH_CONST);
if
(q
-
>exec_cksum !
=
cksum) {
hnb
=
has_new_bits(virgin_bits);
if
(hnb > new_bits) new_bits
=
hnb;
if
(q
-
>exec_cksum) {
/
/
不等于exec_cksum,表示第一次运行,或在相同参数下,每次执行,cksum不同,表示是一个路径可变的queue
u32 i;
for
(i
=
0
; i < MAP_SIZE; i
+
+
) {
if
(!var_bytes[i] && first_trace[i] !
=
trace_bits[i]) {
/
/
从
0
到MAP_SIZE进行遍历, first_trace[i] !
=
trace_bits[i],表示发现了可变queue
var_bytes[i]
=
1
;
stage_max
=
CAL_CYCLES_LONG;
}
}
var_detected
=
1
;
}
else
{
q
-
>exec_cksum
=
cksum;
/
/
q
-
>exec_cksum
=
0
,表示第一次执行queue,则设置计算出来的本次执行的cksum
memcpy(first_trace, trace_bits, MAP_SIZE);
}
}
}
stop_us
=
get_cur_time_us();
total_cal_us
+
=
stop_us
-
start_us;
/
/
保存所有轮次的总执行时间
total_cal_cycles
+
=
stage_max;
/
/
保存总轮次
/
*
OK, let's collect some stats about the performance of this test case.
This
is
used
for
fuzzing air time calculations
in
calculate_score().
*
/
q
-
>exec_us
=
(stop_us
-
start_us)
/
stage_max;
/
/
单次执行时间的平均值
q
-
>bitmap_size
=
count_bytes(trace_bits);
/
/
最后一次执行所覆盖的路径数
q
-
>handicap
=
handicap;
q
-
>cal_failed
=
0
;
total_bitmap_size
+
=
q
-
>bitmap_size;
/
/
加上queue所覆盖的路径数
total_bitmap_entries
+
+
;
update_bitmap_score(q);
/
*
If this case didn't result
in
new output
from
the instrumentation, tell
parent. This
is
a non
-
critical problem, but something to warn the user
about.
*
/
if
(!dumb_mode && first_run && !fault && !new_bits) fault
=
FAULT_NOBITS;
abort_calibration:
if
(new_bits
=
=
2
&& !q
-
>has_new_cov) {
q
-
>has_new_cov
=
1
;
queued_with_cov
+
+
;
}
/
*
Mark variable paths.
*
/
if
(var_detected) {
/
/
queue是可变路径
var_byte_count
=
count_bytes(var_bytes);
if
(!q
-
>var_behavior) {
mark_as_variable(q);
queued_variable
+
+
;
}
}
/
/
恢复之前的stage值
stage_name
=
old_sn;
stage_cur
=
old_sc;
stage_max
=
old_sm;
if
(!first_run) show_stats();
return
fault;
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2021-9-27 16:58
被有毒编辑
,原因: