|
|
|
@ -7775,6 +7775,7 @@ static void save_cmdline(u32 argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
/* Main entry point */
|
|
|
|
|
|
|
|
|
|
// 主函数,程序的入口点
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
s32 opt;
|
|
|
|
@ -7788,94 +7789,114 @@ int main(int argc, char** argv) {
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
struct timezone tz;
|
|
|
|
|
|
|
|
|
|
SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
|
|
|
|
|
|
|
|
|
|
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
|
|
|
|
|
|
|
|
|
gettimeofday(&tv, &tz);
|
|
|
|
|
srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
|
|
|
|
|
|
|
|
|
|
// argv 处理
|
|
|
|
|
while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0)
|
|
|
|
|
|
|
|
|
|
switch (opt) {
|
|
|
|
|
|
|
|
|
|
case 'i': /* input dir */
|
|
|
|
|
|
|
|
|
|
// 初始 corpus 目录
|
|
|
|
|
if (in_dir) FATAL("Multiple -i options not supported");
|
|
|
|
|
in_dir = optarg;
|
|
|
|
|
|
|
|
|
|
// 若使用 "-i -",则表示 in-place resume
|
|
|
|
|
if (!strcmp(in_dir, "-")) in_place_resume = 1;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'o': /* output dir */
|
|
|
|
|
|
|
|
|
|
if (out_dir) FATAL("Multiple -o options not supported");
|
|
|
|
|
out_dir = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'M': { /* master sync ID */
|
|
|
|
|
|
|
|
|
|
u8* c;
|
|
|
|
|
|
|
|
|
|
if (sync_id) FATAL("Multiple -S or -M options not supported");
|
|
|
|
|
sync_id = ck_strdup(optarg);
|
|
|
|
|
|
|
|
|
|
if ((c = strchr(sync_id, ':'))) {
|
|
|
|
|
|
|
|
|
|
*c = 0;
|
|
|
|
|
|
|
|
|
|
if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
|
|
|
|
|
!master_id || !master_max || master_id > master_max ||
|
|
|
|
|
master_max > 1000000) FATAL("Bogus master ID passed to -M");
|
|
|
|
|
SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************
|
|
|
|
|
* 函数/方法: 主程序的参数处理
|
|
|
|
|
* 描述: 该段代码负责解析命令行参数,并对输入输出目录、文件、同步ID以及超时等选项进行处理和验证。
|
|
|
|
|
***************************************************************/
|
|
|
|
|
|
|
|
|
|
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
|
|
|
|
|
|
|
|
|
gettimeofday(&tv, &tz);
|
|
|
|
|
srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
|
|
|
|
|
|
|
|
|
|
// argv 处理
|
|
|
|
|
while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0)
|
|
|
|
|
|
|
|
|
|
switch (opt) {
|
|
|
|
|
|
|
|
|
|
case 'i': /* input dir */
|
|
|
|
|
|
|
|
|
|
// 初始 corpus 目录
|
|
|
|
|
if (in_dir) FATAL("Multiple -i options not supported");
|
|
|
|
|
in_dir = optarg;
|
|
|
|
|
|
|
|
|
|
// 若使用 "-i -",则表示 in-place resume
|
|
|
|
|
if (!strcmp(in_dir, "-")) in_place_resume = 1;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* 处理输出目录的命令行选项 */
|
|
|
|
|
case 'o': /* output dir */
|
|
|
|
|
|
|
|
|
|
if (out_dir) FATAL("Multiple -o options not supported");
|
|
|
|
|
out_dir = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'M': { /* master sync ID */
|
|
|
|
|
// 处理选项 'M',用于设定主同步 ID
|
|
|
|
|
|
|
|
|
|
u8* c;
|
|
|
|
|
|
|
|
|
|
if (sync_id) FATAL("Multiple -S or -M options not supported");
|
|
|
|
|
sync_id = ck_strdup(optarg);
|
|
|
|
|
|
|
|
|
|
if ((c = strchr(sync_id, ':'))) {
|
|
|
|
|
// 解析同步ID中的主ID和最大ID
|
|
|
|
|
|
|
|
|
|
*c = 0;
|
|
|
|
|
|
|
|
|
|
if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
|
|
|
|
|
!master_id || !master_max || master_id > master_max ||
|
|
|
|
|
master_max > 1000000) FATAL("Bogus master ID passed to -M");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
force_deterministic = 1;
|
|
|
|
|
// 强制使用确定性执行
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
force_deterministic = 1;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
|
|
|
|
|
|
if (sync_id) FATAL("Multiple -S or -M options not supported");
|
|
|
|
|
sync_id = ck_strdup(optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'f': /* target file */
|
|
|
|
|
|
|
|
|
|
if (out_file) FATAL("Multiple -f options not supported");
|
|
|
|
|
out_file = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'x': /* dictionary */
|
|
|
|
|
|
|
|
|
|
if (extras_dir) FATAL("Multiple -x options not supported");
|
|
|
|
|
extras_dir = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 't': { /* timeout */
|
|
|
|
|
|
|
|
|
|
u8 suffix = 0;
|
|
|
|
|
|
|
|
|
|
if (timeout_given) FATAL("Multiple -t options not supported");
|
|
|
|
|
|
|
|
|
|
if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
|
|
|
|
|
optarg[0] == '-') FATAL("Bad syntax used for -t");
|
|
|
|
|
|
|
|
|
|
if (exec_tmout < 5) FATAL("Dangerously low value of -t");
|
|
|
|
|
|
|
|
|
|
if (suffix == '+') timeout_given = 2; else timeout_given = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理-S选项的代码逻辑
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* 该代码处理不同命令行选项的解析,包括同步ID、目标文件和字典目录等。 *
|
|
|
|
|
* 当检测到重复的选项时,程序会调用FATAL函数报错。 *
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
case 'S':
|
|
|
|
|
|
|
|
|
|
if (sync_id) FATAL("Multiple -S or -M options not supported");
|
|
|
|
|
sync_id = ck_strdup(optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'f': /* target file */
|
|
|
|
|
|
|
|
|
|
if (out_file) FATAL("Multiple -f options not supported");
|
|
|
|
|
out_file = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'x': /* dictionary */
|
|
|
|
|
|
|
|
|
|
if (extras_dir) FATAL("Multiple -x options not supported");
|
|
|
|
|
extras_dir = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
|
* 处理命令行选项中与超时相关的参数
|
|
|
|
|
* 支持-t选项,用于设置执行超时时间
|
|
|
|
|
**************************************/
|
|
|
|
|
case 't': { /* timeout */
|
|
|
|
|
|
|
|
|
|
u8 suffix = 0;
|
|
|
|
|
|
|
|
|
|
if (timeout_given) FATAL("Multiple -t options not supported");
|
|
|
|
|
|
|
|
|
|
if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
|
|
|
|
|
optarg[0] == '-') FATAL("Bad syntax used for -t");
|
|
|
|
|
|
|
|
|
|
if (exec_tmout < 5) FATAL("Dangerously low value of -t");
|
|
|
|
|
|
|
|
|
|
if (suffix == '+') timeout_given = 2; else timeout_given = 1;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'm': { /* mem limit */
|
|
|
|
|
// 处理内存限制选项的相关逻辑
|
|
|
|
|
|
|
|
|
|
u8 suffix = 'M';
|
|
|
|
|
|
|
|
|
@ -7883,15 +7904,16 @@ int main(int argc, char** argv) {
|
|
|
|
|
mem_limit_given = 1;
|
|
|
|
|
|
|
|
|
|
if (!strcmp(optarg, "none")) {
|
|
|
|
|
|
|
|
|
|
// 如果选项参数为"none",则将内存限制设置为0
|
|
|
|
|
mem_limit = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解析命令行参数,获取内存限制和单位后缀
|
|
|
|
|
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
|
|
|
|
|
optarg[0] == '-') FATAL("Bad syntax used for -m");
|
|
|
|
|
|
|
|
|
|
// 根据单位后缀调整内存限制的值
|
|
|
|
|
switch (suffix) {
|
|
|
|
|
|
|
|
|
|
case 'T': mem_limit *= 1024 * 1024; break;
|
|
|
|
@ -7903,8 +7925,10 @@ int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查内存限制值是否低于安全阈值
|
|
|
|
|
if (mem_limit < 5) FATAL("Dangerously low value of -m");
|
|
|
|
|
|
|
|
|
|
// 在32位系统上检查内存限制的范围
|
|
|
|
|
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
|
|
|
|
|
FATAL("Value of -m out of range on 32-bit systems");
|
|
|
|
|
|
|
|
|
@ -7913,10 +7937,11 @@ int main(int argc, char** argv) {
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'b': { /* bind CPU core */
|
|
|
|
|
|
|
|
|
|
// 处理 CPU 绑定选项,确保只能使用一次
|
|
|
|
|
if (cpu_to_bind_given) FATAL("Multiple -b options not supported");
|
|
|
|
|
cpu_to_bind_given = 1;
|
|
|
|
|
|
|
|
|
|
// 解析用户输入的 CPU 核心编号,检查输入合法性
|
|
|
|
|
if (sscanf(optarg, "%u", &cpu_to_bind) < 1 ||
|
|
|
|
|
optarg[0] == '-') FATAL("Bad syntax used for -b");
|
|
|
|
|
|
|
|
|
@ -7924,6 +7949,7 @@ int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 处理命令行参数,选择跳过确定性测试的选项 */
|
|
|
|
|
case 'd': /* skip deterministic */
|
|
|
|
|
|
|
|
|
|
if (skip_deterministic) FATAL("Multiple -d options not supported");
|
|
|
|
@ -7932,24 +7958,19 @@ int main(int argc, char** argv) {
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'B': /* load bitmap */
|
|
|
|
|
|
|
|
|
|
/* This is a secret undocumented option! It is useful if you find
|
|
|
|
|
an interesting test case during a normal fuzzing process, and want
|
|
|
|
|
to mutate it without rediscovering any of the test cases already
|
|
|
|
|
found during an earlier run.
|
|
|
|
|
|
|
|
|
|
To use this mode, you need to point -B to the fuzz_bitmap produced
|
|
|
|
|
by an earlier run for the exact same binary... and that's it.
|
|
|
|
|
|
|
|
|
|
I only used this once or twice to get variants of a particular
|
|
|
|
|
file, so I'm not making this an official setting. */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
该代码段处理加载位图的选项。
|
|
|
|
|
位图功能是一个未文档化的选项,适用于在正常模糊测试过程中找到有趣的测试用例后,进行变异而不重新发现先前运行中已找到的测试用例。
|
|
|
|
|
使用此模式时,需要将 -B 指向之前运行相同二进制文件生成的 fuzz_bitmap。
|
|
|
|
|
该功能仅在少数几次中使用过,因此不做为官方设置。
|
|
|
|
|
*/
|
|
|
|
|
if (in_bitmap) FATAL("Multiple -B options not supported");
|
|
|
|
|
|
|
|
|
|
in_bitmap = optarg;
|
|
|
|
|
read_bitmap(in_bitmap);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* 处理命令行参数选项 'C',用于设置崩溃模式 */
|
|
|
|
|
case 'C': /* crash mode */
|
|
|
|
|
|
|
|
|
|
if (crash_mode) FATAL("Multiple -C options not supported");
|
|
|
|
@ -7957,27 +7978,34 @@ int main(int argc, char** argv) {
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'n': /* dumb mode */
|
|
|
|
|
|
|
|
|
|
// 处理-dumb模式的选项,当程序收到'-n'时触发
|
|
|
|
|
|
|
|
|
|
if (dumb_mode) FATAL("Multiple -n options not supported");
|
|
|
|
|
if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* 处理命令行选项 */
|
|
|
|
|
case 'T': /* banner */
|
|
|
|
|
|
|
|
|
|
/* 检查是否支持多个 -T 选项 */
|
|
|
|
|
if (use_banner) FATAL("Multiple -T options not supported");
|
|
|
|
|
use_banner = optarg;
|
|
|
|
|
break;
|
|
|
|
|
// 处理QEMU模式的代码段
|
|
|
|
|
// 如果启用QEMU模式,检查是否已指定多个-Q选项
|
|
|
|
|
// 如果未指定内存限制,则使用默认的QEMU内存限制
|
|
|
|
|
// 在处理完成后退出switch语句
|
|
|
|
|
case 'Q': /* QEMU mode */
|
|
|
|
|
|
|
|
|
|
if (qemu_mode) FATAL("Multiple -Q options not supported");
|
|
|
|
|
qemu_mode = 1;
|
|
|
|
|
|
|
|
|
|
if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'Q': /* QEMU mode */
|
|
|
|
|
|
|
|
|
|
if (qemu_mode) FATAL("Multiple -Q options not supported");
|
|
|
|
|
qemu_mode = 1;
|
|
|
|
|
|
|
|
|
|
if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* 处理选项 'V' 的情况,显示版本号 */
|
|
|
|
|
case 'V': /* Show version number */
|
|
|
|
|
|
|
|
|
|
/* Version number has been printed already, just quit. */
|
|
|
|
@ -7989,44 +8017,57 @@ int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (optind == argc || !in_dir || !out_dir) usage(argv[0]);
|
|
|
|
|
|
|
|
|
|
setup_signal_handlers();
|
|
|
|
|
check_asan_opts();
|
|
|
|
|
|
|
|
|
|
if (sync_id) fix_up_sync();
|
|
|
|
|
|
|
|
|
|
if (!strcmp(in_dir, out_dir))
|
|
|
|
|
FATAL("Input and output directories can't be the same");
|
|
|
|
|
|
|
|
|
|
if (dumb_mode) {
|
|
|
|
|
|
|
|
|
|
if (crash_mode) FATAL("-C and -n are mutually exclusive");
|
|
|
|
|
if (qemu_mode) FATAL("-Q and -n are mutually exclusive");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1;
|
|
|
|
|
if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1;
|
|
|
|
|
if (getenv("AFL_NO_ARITH")) no_arith = 1;
|
|
|
|
|
if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1;
|
|
|
|
|
if (getenv("AFL_FAST_CAL")) fast_cal = 1;
|
|
|
|
|
|
|
|
|
|
if (getenv("AFL_HANG_TMOUT")) {
|
|
|
|
|
hang_tmout = atoi(getenv("AFL_HANG_TMOUT"));
|
|
|
|
|
if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dumb_mode == 2 && no_forkserver)
|
|
|
|
|
FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");
|
|
|
|
|
// 检查命令行参数以及输入输出目录是否有效,若无效则显示使用方法
|
|
|
|
|
if (optind == argc || !in_dir || !out_dir) usage(argv[0]);
|
|
|
|
|
|
|
|
|
|
// 设置信号处理函数
|
|
|
|
|
setup_signal_handlers();
|
|
|
|
|
// 检查地址卫生选项
|
|
|
|
|
check_asan_opts();
|
|
|
|
|
|
|
|
|
|
// 处理同步ID和目录检查的逻辑
|
|
|
|
|
// 该段代码主要用于验证输入输出目录及模式的有效性
|
|
|
|
|
// 在不同的运行模式下进行相应的错误处理
|
|
|
|
|
|
|
|
|
|
if (sync_id) fix_up_sync();
|
|
|
|
|
|
|
|
|
|
if (!strcmp(in_dir, out_dir))
|
|
|
|
|
FATAL("Input and output directories can't be the same");
|
|
|
|
|
|
|
|
|
|
if (dumb_mode) {
|
|
|
|
|
|
|
|
|
|
if (crash_mode) FATAL("-C and -n are mutually exclusive");
|
|
|
|
|
if (qemu_mode) FATAL("-Q and -n are mutually exclusive");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getenv("AFL_PRELOAD")) {
|
|
|
|
|
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
|
|
|
|
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
|
|
|
|
}
|
|
|
|
|
// 该代码段用于读取环境变量并根据其值设置相应的标志和超时值
|
|
|
|
|
// 主要功能是启动时配置这些参数以调整程序的行为
|
|
|
|
|
if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1;
|
|
|
|
|
if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1;
|
|
|
|
|
if (getenv("AFL_NO_ARITH")) no_arith = 1;
|
|
|
|
|
if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1;
|
|
|
|
|
if (getenv("AFL_FAST_CAL")) fast_cal = 1;
|
|
|
|
|
|
|
|
|
|
if (getenv("AFL_HANG_TMOUT")) {
|
|
|
|
|
hang_tmout = atoi(getenv("AFL_HANG_TMOUT"));
|
|
|
|
|
if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getenv("AFL_LD_PRELOAD"))
|
|
|
|
|
FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
|
|
|
|
|
// 此函数用于检查环境变量和启动模式的合法性
|
|
|
|
|
// 如果在不支持的模式下使用互斥选项,则会引发致命错误
|
|
|
|
|
// 处理环境变量以确保正确的库加载
|
|
|
|
|
|
|
|
|
|
if (dumb_mode == 2 && no_forkserver)
|
|
|
|
|
FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");
|
|
|
|
|
|
|
|
|
|
if (getenv("AFL_PRELOAD")) {
|
|
|
|
|
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
|
|
|
|
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getenv("AFL_LD_PRELOAD"))
|
|
|
|
|
FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
|
|
|
|
|
|
|
|
|
|
save_cmdline(argc, argv);
|
|
|
|
|
|
|
|
|
@ -8037,104 +8078,142 @@ int main(int argc, char** argv) {
|
|
|
|
|
get_core_count();
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_AFFINITY
|
|
|
|
|
// 将当前进程绑定到一个空闲的CPU上,以优化性能
|
|
|
|
|
bind_to_free_cpu();
|
|
|
|
|
#endif /* HAVE_AFFINITY */
|
|
|
|
|
|
|
|
|
|
/* 检查崩溃处理机制 */
|
|
|
|
|
check_crash_handling();
|
|
|
|
|
/* 检查CPU调节器设置 */
|
|
|
|
|
check_cpu_governor();
|
|
|
|
|
|
|
|
|
|
/* 设置后处理操作 */
|
|
|
|
|
setup_post();
|
|
|
|
|
/* 设置共享内存 */
|
|
|
|
|
setup_shm();
|
|
|
|
|
/* 初始化计数类16 */
|
|
|
|
|
init_count_class16();
|
|
|
|
|
|
|
|
|
|
/* 设置目录和文件描述符 */
|
|
|
|
|
setup_dirs_fds();
|
|
|
|
|
/* 读取测试用例 */
|
|
|
|
|
read_testcases();
|
|
|
|
|
/* 加载自动化设置 */
|
|
|
|
|
load_auto();
|
|
|
|
|
|
|
|
|
|
/* 处理输入的转换 */
|
|
|
|
|
pivot_inputs();
|
|
|
|
|
// 此代码段为 afl-fuzz.c 文件中的初始化和参数处理部分
|
|
|
|
|
// 主要功能是配置执行环境并检查传入的文件参数
|
|
|
|
|
// 包括加载额外目录、查找超时、检测文件参数、设置输出文件、检查二进制文件及获取当前时间
|
|
|
|
|
|
|
|
|
|
if (extras_dir) load_extras(extras_dir);
|
|
|
|
|
|
|
|
|
|
if (!timeout_given) find_timeout();
|
|
|
|
|
|
|
|
|
|
detect_file_args(argv + optind + 1);
|
|
|
|
|
|
|
|
|
|
if (!out_file) setup_stdio_file();
|
|
|
|
|
|
|
|
|
|
check_binary(argv[optind]);
|
|
|
|
|
|
|
|
|
|
start_time = get_cur_time();
|
|
|
|
|
|
|
|
|
|
if (qemu_mode)
|
|
|
|
|
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
|
|
|
|
else
|
|
|
|
|
use_argv = argv + optind;
|
|
|
|
|
|
|
|
|
|
if (extras_dir) load_extras(extras_dir);
|
|
|
|
|
|
|
|
|
|
if (!timeout_given) find_timeout();
|
|
|
|
|
|
|
|
|
|
detect_file_args(argv + optind + 1);
|
|
|
|
|
|
|
|
|
|
if (!out_file) setup_stdio_file();
|
|
|
|
|
|
|
|
|
|
check_binary(argv[optind]);
|
|
|
|
|
|
|
|
|
|
start_time = get_cur_time();
|
|
|
|
|
|
|
|
|
|
if (qemu_mode)
|
|
|
|
|
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
|
|
|
|
else
|
|
|
|
|
use_argv = argv + optind;
|
|
|
|
|
|
|
|
|
|
perform_dry_run(use_argv);
|
|
|
|
|
|
|
|
|
|
cull_queue();
|
|
|
|
|
|
|
|
|
|
show_init_stats();
|
|
|
|
|
|
|
|
|
|
seek_to = find_start_position();
|
|
|
|
|
|
|
|
|
|
write_stats_file(0, 0, 0);
|
|
|
|
|
save_auto();
|
|
|
|
|
|
|
|
|
|
if (stop_soon) goto stop_fuzzing;
|
|
|
|
|
|
|
|
|
|
/* Woop woop woop */
|
|
|
|
|
|
|
|
|
|
if (!not_on_tty) {
|
|
|
|
|
sleep(4);
|
|
|
|
|
start_time += 4000;
|
|
|
|
|
/*
|
|
|
|
|
* 此函数实现了模糊测试的初始步骤,包括执行干运行、清理队列、
|
|
|
|
|
* 显示初始化统计信息、寻找起始位置并写入统计文件。
|
|
|
|
|
*/
|
|
|
|
|
perform_dry_run(use_argv);
|
|
|
|
|
|
|
|
|
|
cull_queue();
|
|
|
|
|
|
|
|
|
|
show_init_stats();
|
|
|
|
|
|
|
|
|
|
seek_to = find_start_position();
|
|
|
|
|
|
|
|
|
|
write_stats_file(0, 0, 0);
|
|
|
|
|
save_auto();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 如果状态指示测试需要停止,则跳转到停止模糊测试的标签。
|
|
|
|
|
*/
|
|
|
|
|
if (stop_soon) goto stop_fuzzing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Woop woop woop */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 如果不是在终端上运行,则暂停4秒,增加开始时间。
|
|
|
|
|
*/
|
|
|
|
|
if (!not_on_tty) {
|
|
|
|
|
sleep(4);
|
|
|
|
|
start_time += 4000;
|
|
|
|
|
/*
|
|
|
|
|
* 再次检查是否需要停止模糊测试。
|
|
|
|
|
*/
|
|
|
|
|
if (stop_soon) goto stop_fuzzing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 该段代码实现了一个持续循环,处理模糊测试队列中的条目。
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
|
|
u8 skipped_fuzz;
|
|
|
|
|
|
|
|
|
|
cull_queue();
|
|
|
|
|
|
|
|
|
|
if (!queue_cur) {
|
|
|
|
|
|
|
|
|
|
queue_cycle++;
|
|
|
|
|
current_entry = 0;
|
|
|
|
|
cur_skipped_paths = 0;
|
|
|
|
|
queue_cur = queue;
|
|
|
|
|
|
|
|
|
|
while (seek_to) {
|
|
|
|
|
current_entry++;
|
|
|
|
|
seek_to--;
|
|
|
|
|
queue_cur = queue_cur->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
show_stats();
|
|
|
|
|
|
|
|
|
|
if (not_on_tty) {
|
|
|
|
|
ACTF("Entering queue cycle %llu.", queue_cycle);
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u8 skipped_fuzz;
|
|
|
|
|
|
|
|
|
|
// 清理当前模糊测试队列
|
|
|
|
|
cull_queue();
|
|
|
|
|
|
|
|
|
|
if (!queue_cur) {
|
|
|
|
|
|
|
|
|
|
// 如果当前队列为空,增加队列周期计数
|
|
|
|
|
queue_cycle++;
|
|
|
|
|
current_entry = 0;
|
|
|
|
|
cur_skipped_paths = 0;
|
|
|
|
|
queue_cur = queue;
|
|
|
|
|
|
|
|
|
|
// 根据指定的跳过路径数量,定位到队列中的特定条目
|
|
|
|
|
while (seek_to) {
|
|
|
|
|
current_entry++;
|
|
|
|
|
seek_to--;
|
|
|
|
|
queue_cur = queue_cur->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示当前状态统计
|
|
|
|
|
show_stats();
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
|
|
|
|
|
// 根据是否使用拼接,决定增加无发现循环次数或启用拼接
|
|
|
|
|
if (use_splicing) cycles_wo_finds++; else use_splicing = 1;
|
|
|
|
|
|
|
|
|
|
} else cycles_wo_finds = 0;
|
|
|
|
|
|
|
|
|
|
// 更新前一个队列路径值
|
|
|
|
|
prev_queued = queued_paths;
|
|
|
|
|
|
|
|
|
|
// 检查同步ID,队列循环和环境变量以同步模糊测试器
|
|
|
|
|
if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST"))
|
|
|
|
|
sync_fuzzers(use_argv);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行模糊测试
|
|
|
|
|
skipped_fuzz = fuzz_one(use_argv);
|
|
|
|
|
|
|
|
|
|
// 如果没有停止标志且没有跳过模糊测试,根据同步间隔决定是否同步
|
|
|
|
|
if (!stop_soon && sync_id && !skipped_fuzz) {
|
|
|
|
|
|
|
|
|
|
if (!(sync_interval_cnt++ % SYNC_INTERVAL))
|
|
|
|
@ -8142,38 +8221,47 @@ int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果没有停止标志且 exit_1 为真,则设置停止标志
|
|
|
|
|
if (!stop_soon && exit_1) stop_soon = 2;
|
|
|
|
|
|
|
|
|
|
// 如果设置了停止标志,则跳出循环
|
|
|
|
|
if (stop_soon) break;
|
|
|
|
|
|
|
|
|
|
queue_cur = queue_cur->next;
|
|
|
|
|
current_entry++;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (queue_cur) show_stats();
|
|
|
|
|
// 更新当前队列指针和当前条目计数
|
|
|
|
|
queue_cur = queue_cur->next;
|
|
|
|
|
current_entry++;
|
|
|
|
|
|
|
|
|
|
/* If we stopped programmatically, we kill the forkserver and the current runner.
|
|
|
|
|
If we stopped manually, this is done by the signal handler. */
|
|
|
|
|
if (stop_soon == 2) {
|
|
|
|
|
if (child_pid > 0) kill(child_pid, SIGKILL);
|
|
|
|
|
if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL);
|
|
|
|
|
}
|
|
|
|
|
/* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */
|
|
|
|
|
if (waitpid(forksrv_pid, NULL, 0) <= 0) {
|
|
|
|
|
WARNF("error waitpid\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write_bitmap();
|
|
|
|
|
write_stats_file(0, 0, 0);
|
|
|
|
|
save_auto();
|
|
|
|
|
/*
|
|
|
|
|
* 该段代码用于处理程序终止的情况,包括被程序控制的终止和手动终止。
|
|
|
|
|
* 在终止时会杀死子进程和forkserver,并等待forkserver获取资源使用统计信息。
|
|
|
|
|
* 此外,还会写入位图和统计文件以保存当前状态。
|
|
|
|
|
*/
|
|
|
|
|
if (queue_cur) show_stats();
|
|
|
|
|
|
|
|
|
|
/* If we stopped programmatically, we kill the forkserver and the current runner.
|
|
|
|
|
If we stopped manually, this is done by the signal handler. */
|
|
|
|
|
if (stop_soon == 2) {
|
|
|
|
|
if (child_pid > 0) kill(child_pid, SIGKILL);
|
|
|
|
|
if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL);
|
|
|
|
|
}
|
|
|
|
|
/* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */
|
|
|
|
|
if (waitpid(forksrv_pid, NULL, 0) <= 0) {
|
|
|
|
|
WARNF("error waitpid\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write_bitmap();
|
|
|
|
|
write_stats_file(0, 0, 0);
|
|
|
|
|
save_auto();
|
|
|
|
|
|
|
|
|
|
/* 停止模糊测试的函数 */
|
|
|
|
|
stop_fuzzing:
|
|
|
|
|
|
|
|
|
|
SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
|
|
|
|
|
stop_soon == 2 ? "programmatically" : "by user");
|
|
|
|
|
|
|
|
|
|
/* Running for more than 30 minutes but still doing first cycle? */
|
|
|
|
|
/* 运行超过30分钟但仍在进行第一次周期? */
|
|
|
|
|
|
|
|
|
|
if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) {
|
|
|
|
|
|
|
|
|
|