|
|
|
@ -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,6 +8078,7 @@ int main(int argc, char** argv) {
|
|
|
|
|
get_core_count();
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_AFFINITY
|
|
|
|
|
// 将当前进程绑定到一个空闲的CPU上,以优化性能
|
|
|
|
|
bind_to_free_cpu();
|
|
|
|
|
#endif /* HAVE_AFFINITY */
|
|
|
|
|
|
|
|
|
|