diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 21129b0..46a216c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -34,7 +34,6 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE - #endif #define _FILE_OFFSET_BITS 64 @@ -1552,115 +1551,141 @@ static int compare_extras_use_d(const void* p1, const void* p2) { /* Read extras from a file, sort by size. */ -// 定义一个函数,用于加载额外的数据文件 -static void load_extras_file(u8* fname, u32* min_len, u32* max_len, u32 dict_level) { - FILE* f; // 文件指针 - u8 buf[MAX_LINE]; // 用于存储每行数据的缓冲区 - u8 *lptr; // 指向当前行数据的指针 - u32 cur_line = 0; // 当前行号 +static void load_extras_file(u8* fname, u32* min_len, u32* max_len, + u32 dict_level) { + + FILE* f; + u8 buf[MAX_LINE]; + u8 *lptr; + u32 cur_line = 0; - // 尝试打开文件,如果失败则打印错误信息并退出 f = fopen(fname, "r"); + if (!f) PFATAL("Unable to open '%s'", fname); - // 逐行读取文件内容 while ((lptr = fgets(buf, MAX_LINE, f))) { - u8 *rptr, *wptr; // 指向行数据的指针 - u32 klen = 0; // 关键词长度 - cur_line++; // 行号增加 + u8 *rptr, *wptr; + u32 klen = 0; + + cur_line++; + + /* Trim on left and right. */ - // 去除行首的空白字符 while (isspace(*lptr)) lptr++; - // 去除行尾的空白字符,并找到行尾的结束符 rptr = lptr + strlen(lptr) - 1; while (rptr >= lptr && isspace(*rptr)) rptr--; rptr++; *rptr = 0; - // 跳过空行和注释行 + /* Skip empty lines and comments. */ + if (!*lptr || *lptr == '#') continue; - // 检查行尾是否有双引号,如果没有则报错 + /* All other lines must end with '"', which we can consume. */ + rptr--; + if (rptr < lptr || *rptr != '"') FATAL("Malformed name=\"value\" pair in line %u.", cur_line); - *rptr = 0; // 去除行尾的双引号 + *rptr = 0; + + /* Skip alphanumerics and dashes (label). */ - // 跳过字母数字和下划线(标签) while (isalnum(*lptr) || *lptr == '_') lptr++; - // 如果标签后跟有@数字,则解析该数字 + /* If @number follows, parse that. */ + if (*lptr == '@') { + lptr++; - if (atoi(lptr) > dict_level) continue; // 如果数字大于字典级别,则跳过 - while (isdigit(*lptr)) lptr++; // 跳过数字 + if (atoi(lptr) > dict_level) continue; + while (isdigit(*lptr)) lptr++; + } - // 跳过空白字符和等号 + /* Skip whitespace and = signs. */ + while (isspace(*lptr) || *lptr == '=') lptr++; - // 检查等号后是否有双引号,如果没有则报错 + /* Consume opening '"'. */ + if (*lptr != '"') FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line); - lptr++; // 跳过双引号 + lptr++; - if (!*lptr) FATAL("Empty keyword in line %u.", cur_line); // 检查关键词是否为空 + if (!*lptr) FATAL("Empty keyword in line %u.", cur_line); + + /* Okay, let's allocate memory and copy data between "...", handling + \xNN escaping, \\, and \". */ + + extras = ck_realloc_block(extras, (extras_cnt + 1) * + sizeof(struct extra_data)); - // 分配内存并复制数据,处理转义字符 - extras = ck_realloc_block(extras, (extras_cnt + 1) * sizeof(struct extra_data)); wptr = extras[extras_cnt].data = ck_alloc(rptr - lptr); - // 复制数据,处理转义字符 while (*lptr) { + char* hexdigits = "0123456789abcdef"; switch (*lptr) { - case 1 ... 31: // 非打印字符 - case 128 ... 255: // 非ASCII字符 + + case 1 ... 31: + case 128 ... 255: FATAL("Non-printable characters in line %u.", cur_line); - break; - case '\\': // 处理转义字符 + case '\\': + lptr++; + if (*lptr == '\\' || *lptr == '"') { - *(wptr++) = *(lptr++); // 直接复制转义字符 + *(wptr++) = *(lptr++); klen++; break; } + if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2])) FATAL("Invalid escaping (not \\xNN) in line %u.", cur_line); - // 处理\xNN形式的转义字符 - *(wptr++) = ((strchr(hexdigits, tolower(lptr[1])) - hexdigits) << 4) | - (strchr(hexdigits, tolower(lptr[2])) - hexdigits); + + *(wptr++) = + ((strchr(hexdigits, tolower(lptr[1])) - hexdigits) << 4) | + (strchr(hexdigits, tolower(lptr[2])) - hexdigits); + lptr += 3; klen++; + break; - default: // 复制普通字符 + default: + *(wptr++) = *(lptr++); klen++; + } + } - extras[extras_cnt].len = klen; // 设置关键词长度 + extras[extras_cnt].len = klen; if (extras[extras_cnt].len > MAX_DICT_FILE) FATAL("Keyword too big in line %u (%s, limit is %s)", cur_line, - DMS(klen), DMS(MAX_DICT_FILE)); // 检查关键词长度是否超过限制 + DMS(klen), DMS(MAX_DICT_FILE)); - if (*min_len > klen) *min_len = klen; // 更新最小长度 - if (*max_len < klen) *max_len = klen; // 更新最大长度 + if (*min_len > klen) *min_len = klen; + if (*max_len < klen) *max_len = klen; + + extras_cnt++; - extras_cnt++; // 增加额外数据计数 } - fclose(f); // 关闭文件 + fclose(f); + } + /* Read extras from the extras directory and sort them by size. */ static void load_extras(u8* dir) { @@ -2413,16 +2438,16 @@ static u8 run_target(char** argv, u32 timeout) { } - if (!WIFSTOPPED(status)) child_pid = 0;//这行代码检查子进程的状态。如果子进程没有被停止(即没有收到停止信号),则将child_pid变量设置为0。这通常意味着子进程已经完成执行。 + if (!WIFSTOPPED(status)) child_pid = 0; - getitimer(ITIMER_REAL, &it);//这行代码获取当前的实时定时器(ITIMER_REAL)的剩余时间,并将其存储在it结构体中。 + getitimer(ITIMER_REAL, &it); exec_ms = (u64) timeout - (it.it_value.tv_sec * 1000 + it.it_value.tv_usec / 1000); -//这行代码计算被测试程序的实际执行时间。它从预设的超时时间timeout中减去定时器剩余的时间(转换成毫秒)。这个值被存储在exec_ms中,代表执行时间,单位是毫秒。 + it.it_value.tv_sec = 0; - it.it_value.tv_usec = 0;//定时器置零 + it.it_value.tv_usec = 0; - setitimer(ITIMER_REAL, &it, NULL);//设置实时定时器,使用上面设置的0值,这意味着定时器被禁用,不会再产生超时信号。 + setitimer(ITIMER_REAL, &it, NULL); total_execs++; @@ -2430,22 +2455,22 @@ static u8 run_target(char** argv, u32 timeout) { compiler below this point. Past this location, trace_bits[] behave very normally and do not have to be treated as volatile. */ - MEM_BARRIER();//这是一个内存屏障函数,确保前面的操作不会被编译器重排到后面的操作之后。这对于多线程程序中的内存访问顺序很重要。 + MEM_BARRIER(); - tb4 = *(u32*)trace_bits;//这行代码将trace_bits(一个用于记录被测试程序执行路径的位图数组)的前4个字节的内容读取到一个无符号32位整数tb4中。这个值可能用于后续的分析,以确定被测试程序的执行路径是否产生了新的覆盖 + tb4 = *(u32*)trace_bits; #ifdef WORD_SIZE_64 classify_counts((u64*)trace_bits); #else classify_counts((u32*)trace_bits); #endif /* ^WORD_SIZE_64 */ -//这部分代码使用宏WORD_SIZE_64来确定系统是64位还是32位。根据系统的字长,它将trace_bits转换为相应的指针类型(u64*或u32*),并调用classify_counts函数。这个函数可能用于对trace_bits中的计数数据进行分类或简化。 + prev_timed_out = child_timed_out; /* Report outcome to caller. */ if (WIFSIGNALED(status) && !stop_soon) { -//这部分代码检查子进程是否因信号而终止。如果子进程因超时被杀死(SIGKILL),则返回FAULT_TMOUT。否则,返回FAULT_CRASH表示子进程崩溃。 + kill_signal = WTERMSIG(status); if (child_timed_out && kill_signal == SIGKILL) return FAULT_TMOUT; @@ -2458,19 +2483,16 @@ static u8 run_target(char** argv, u32 timeout) { must use a special exit code. */ if (uses_asan && WEXITSTATUS(status) == MSAN_ERROR) { - //对于使用地址sanitizer(ASAN)的程序,如果退出状态表明发生了内存错误(MSAN_ERROR),则返回FAULT_CRASH kill_signal = 0; return FAULT_CRASH; } if ((dumb_mode == 1 || no_forkserver) && tb4 == EXEC_FAIL_SIG) - //如果运行在“dumb”模式或没有使用fork服务器,且tb4的值表明执行失败,则返回FAULT_ERROR。 return FAULT_ERROR; /* It makes sense to account for the slowest units only if the testcase was run under the user defined timeout. */ if (!(timeout > exec_tmout) && (slowest_exec_ms < exec_ms)) { - //如果测试用例在用户定义的超时时间内运行,并且其执行时间是目前为止最慢的,则更新slowest_exec_ms。 slowest_exec_ms = exec_ms; } @@ -2488,7 +2510,7 @@ static void write_to_testcase(void* mem, u32 len) { s32 fd = out_fd; if (out_file) { -//如果指定了输出文件名(out_file),则删除旧文件并创建一个新文件。否则,将文件描述符的位置设置为文件开头。 + unlink(out_file); /* Ignore errors. */ fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); @@ -2496,7 +2518,7 @@ static void write_to_testcase(void* mem, u32 len) { if (fd < 0) PFATAL("Unable to create '%s'", out_file); } else lseek(fd, 0, SEEK_SET); -//如果没有指定输出文件名,则截断文件到指定长度并重置文件位置。如果指定了输出文件名,则关闭文件描述符。 + ck_write(fd, mem, len, out_file); if (!out_file) { @@ -2512,12 +2534,12 @@ static void write_to_testcase(void* mem, u32 len) { /* The same, but with an adjustable gap. Used for trimming. */ static void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) { -//这个函数类似于write_to_testcase,但允许在文件中跳过一段数据(skip_at和skip_len)。 + s32 fd = out_fd; u32 tail_len = len - skip_at - skip_len; if (out_file) { -//如果指定了输出文件名,则删除旧文件并创建一个新文件。否则,将文件描述符的位置设置为文件开头。 + unlink(out_file); /* Ignore errors. */ fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); @@ -2528,10 +2550,10 @@ static void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) { if (skip_at) ck_write(fd, mem, skip_at, out_file); - if (tail_len) ck_write(fd, mem + skip_at + skip_len, tail_len, out_file);//写入跳过部分之后的数据 + if (tail_len) ck_write(fd, mem + skip_at + skip_len, tail_len, out_file); if (!out_file) { -//如果没有指定输出文件名,则截断文件到指定长度并重置文件位置。如果指定了输出文件名,则关闭文件描述符。 + if (ftruncate(fd, len - skip_len)) PFATAL("ftruncate() failed"); lseek(fd, 0, SEEK_SET); @@ -2545,257 +2567,164 @@ static void show_stats(void); /* 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. */ -//start_us, stop_us; -<<<<<<< HEAD -======= -//保存了一些旧的状态值,以便在函数执行完毕后恢复。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 - -//保存了一些旧的状态值,以便在函数执行完毕后恢复。 -// 定义一个用于校准测试用例的函数 static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, u32 handicap, u8 from_queue) { - // 定义一个静态数组,用于存储第一次执行时的trace bits static u8 first_trace[MAP_SIZE]; - // 定义一些局部变量 u8 fault = 0, new_bits = 0, var_detected = 0, hnb = 0, first_run = (q->exec_cksum == 0); - // 定义变量用于计时 u64 start_us, stop_us; - // 保存旧的阶段状态 s32 old_sc = stage_cur, old_sm = stage_max; u32 use_tmout = exec_tmout; u8* old_sn = stage_name; -<<<<<<< HEAD - /* 如果是在恢复会话或校准已经添加的测试用例时,会放宽超时限制。 - 这有助于避免由于间歇性延迟导致的问题。 */ -======= /* 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. */ -//如果是在恢复会话或校准已经添加的测试用例时,会放宽超时限制。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + if (!from_queue || resuming_fuzz) use_tmout = MAX(exec_tmout + CAL_TMOUT_ADD, exec_tmout * CAL_TMOUT_PERC / 100); - // 增加校准失败计数 q->cal_failed++; -<<<<<<< HEAD - - // 设置当前阶段为“calibration”,并根据是否快速校准设置阶段最大值 - stage_name = "calibration"; - stage_max = fast_cal ? 3 : CAL_CYCLES; - /* 确保在执行任何操作之前fork服务器已经启动,并且不将其启动时间计入二进制校准。 */ - // 如果没有运行在“dumb”模式,并且没有使用fork服务器,那么初始化fork服务器 -======= -//设置当前阶段为“calibration”,并根据是否快速校准设置阶段最大值。 stage_name = "calibration"; stage_max = fast_cal ? 3 : CAL_CYCLES; /* Make sure the forkserver is up before we do anything, and let's not count its spin-up time toward binary calibration. */ -//如果没有运行在“dumb”模式,并且没有使用fork服务器,那么初始化fork服务器。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + if (dumb_mode != 1 && !no_forkserver && !forksrv_pid) init_forkserver(argv); - // 如果队列条目已经有执行校验和,那么将第一次执行的trace bits复制到first_trace if (q->exec_cksum) { -<<<<<<< HEAD -======= -//如果队列条目已经有执行校验和,那么将第一次执行的trace bits复制到first_trace。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + memcpy(first_trace, trace_bits, MAP_SIZE); hnb = has_new_bits(virgin_bits); if (hnb > new_bits) new_bits = hnb; + } -<<<<<<< HEAD - // 计时开始 start_us = get_cur_time_us(); -======= - start_us = get_cur_time_us();//计时 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 - // 校准循环 for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { u32 cksum; - // 如果不是第一次运行并且到了更新频率,显示统计信息 if (!first_run && !(stage_cur % stats_update_freq)) show_stats(); -<<<<<<< HEAD - // 将测试用例数据写入文件 - write_to_testcase(use_mem, q->len); - // 运行目标程序并获取执行结果 - fault = run_target(argv, use_tmout); - /* 如果用户按下Ctrl+C,stop_soon会被设置,我们希望快速退出。 */ - if (stop_soon || fault != crash_mode) goto abort_calibration; -======= -//如果不是第一次运行并且到了更新频率,显示统计信息。 - write_to_testcase(use_mem, q->len);//将测试用例数据写入文件。 + write_to_testcase(use_mem, q->len); - fault = run_target(argv, use_tmout);//运行目标程序并获取执行结果。 + 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;//如果用户请求停止或者执行结果不是崩溃模式,则跳到校准中止。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + if (stop_soon || fault != crash_mode) goto abort_calibration; - // 如果不是“dumb”模式,并且是第一次校准,并且没有新的代码覆盖,则设置错误为FAULT_NOINST if (!dumb_mode && !stage_cur && !count_bytes(trace_bits)) { - //如果不是“dumb”模式,并且是第一次校准,并且没有新的代码覆盖,则设置错误为FAULT_NOINST并跳到校准中止。 fault = FAULT_NOINST; goto abort_calibration; } -<<<<<<< HEAD - // 计算当前trace bits的哈希值,并与队列条目的执行校验和比较 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); - if (q->exec_cksum != cksum) { - // 如果有新的bits被设置,则更新new_bits - hnb = has_new_bits(virgin_bits); - if (hnb > new_bits) new_bits = hnb; - - // 如果队列条目之前有执行校验和,则检查变量字节 - if (q->exec_cksum) { - u32 i; - // 对于每个不同的字节,将其标记为变量字节,并增加校准周期 -======= - cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);//计算当前trace bits的哈希值,并与队列条目的执行校验和比较。 if (q->exec_cksum != cksum) { -//如果有新的bits被设置,则更新new_bits。 + hnb = has_new_bits(virgin_bits); if (hnb > new_bits) new_bits = hnb; - if (q->exec_cksum) {//如果队列条目之前有执行校验和,则检查变量字节。 + if (q->exec_cksum) { u32 i; - //对于每个不同的字节,将其标记为变量字节,并增加校准周期。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + for (i = 0; i < MAP_SIZE; i++) { + if (!var_bytes[i] && first_trace[i] != trace_bits[i]) { + var_bytes[i] = 1; stage_max = CAL_CYCLES_LONG; + } + } + var_detected = 1; -<<<<<<< HEAD -======= - //如果队列条目之前没有执行校验和,则更新其执行校验和并复制当前trace bits。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + } else { - // 如果队列条目之前没有执行校验和,则更新其执行校验和并复制当前trace bits + q->exec_cksum = cksum; memcpy(first_trace, trace_bits, MAP_SIZE); + } + } + } - // 计时结束 stop_us = get_cur_time_us(); -<<<<<<< HEAD - // 更新总校准时间和周期 total_cal_us += stop_us - start_us; total_cal_cycles += stage_max; - /* 收集一些关于这个测试用例性能的统计数据。 - 这用于在calculate_score()中计算fuzzing的空中时间。 */ - // 更新队列条目的执行时间、位图大小和校准失败计数 -======= - 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(). */ -//更新队列条目的执行时间、位图大小和校准失败计数。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + q->exec_us = (stop_us - start_us) / stage_max; q->bitmap_size = count_bytes(trace_bits); q->handicap = handicap; q->cal_failed = 0; -<<<<<<< HEAD - // 更新总位图大小和条目数 total_bitmap_size += q->bitmap_size; -======= - total_bitmap_size += q->bitmap_size;//更新总位图大小和条目数 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 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: -<<<<<<< HEAD - // 如果有新的bits被设置并且队列条目之前没有新覆盖,则更新队列条目的新覆盖标志 -======= -//如果有新的bits被设置并且队列条目之前没有新覆盖,则更新队列条目的新覆盖标志。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + if (new_bits == 2 && !q->has_new_cov) { q->has_new_cov = 1; queued_with_cov++; } - /* 标记可变路径。 */ + /* Mark variable paths. */ + if (var_detected) { -<<<<<<< HEAD - // 如果检测到变量行为,则标记队列条目 -======= -//如果检测到变量行为,则标记队列条目。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + var_byte_count = count_bytes(var_bytes); + if (!q->var_behavior) { mark_as_variable(q); queued_variable++; } + } -<<<<<<< HEAD - // 恢复旧的状态值 -======= -//恢复旧的状态值。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 + stage_name = old_sn; stage_cur = old_sc; stage_max = old_sm; -<<<<<<< HEAD - // 如果校准中止,则跳到函数末尾 if (!first_run) show_stats(); -======= - if (!first_run) show_stats();//如果校准中止,则跳到函数末尾。 ->>>>>>> 78837268ef8db0dafd64b331e2b9f6380dbb1090 - // 返回错误代码 return fault; + } /* Examine map coverage. Called once, for first test case. */ static void check_map_coverage(void) { -/* -这个函数检查映射覆盖率是否足够。如果映射中被设置的字节数少于100,则函数直接返回。 -然后,它检查映射的后半部分是否有任何被设置的位。如果没有,这意味着测试用例只触发了 -映射的前半部分,这可能意味着二进制文件需要重新编译以获得更好的覆盖率。在这种情况下 -,它会打印一条警告消息。 -*/ + u32 i; if (count_bytes(trace_bits) < 100) return; @@ -2824,20 +2753,20 @@ static void perform_dry_run(char** argv) { 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);////分配内存并读取测试用例数据。 + use_mem = ck_alloc_nozero(q->len); if (read(fd, use_mem, q->len) != q->len) FATAL("Short read from '%s'", q->fname); close(fd); - res = calibrate_case(argv, q, use_mem, 0, 1);//调用calibrate_case函数来校准测试用例。 + res = calibrate_case(argv, q, use_mem, 0, 1); ck_free(use_mem); if (stop_soon) return; @@ -2920,15 +2849,6 @@ static void perform_dry_run(char** argv) { " it to die due to OOM when parsing valid files. To fix this, try\n" " bumping it up with the -m setting in the command line. If in doubt,\n" " try something along the lines of:\n\n" -/* -根据校准结果,执行不同的操作: - - 如果测试用例没有引起崩溃,并且不是在寻找崩溃的模式下运行,则检查映射覆盖率。 - 如果测试用例导致超时,根据timeout_given变量的值,可能跳过该测试用例或报告错误。 - 如果测试用例导致崩溃,根据crash_mode变量的值和环境变量AFL_SKIP_CRASHES,可能跳过该测试用例或报告错误。 - -*/ - #ifdef RLIMIT_AS " ( ulimit -Sv $[%llu << 10]; /path/to/binary [...] exec_us < min_us) min_us = q->exec_us; if (q->exec_us > max_us) max_us = q->exec_us; - + if (!min_bits || q->bitmap_size < min_bits) min_bits = q->bitmap_size; if (q->bitmap_size > max_bits) max_bits = q->bitmap_size; - + if (q->len > max_len) max_len = q->len; - - q = q->next; // 移动到下一个队列元素 - + + q = q->next; + } SAYF("\n"); - // 如果平均执行时间较长,给出警告并调整havoc_div的值 if (avg_us > (qemu_mode ? 50000 : 10000)) WARNF(cLRD "The target binary is pretty slow! See %s/perf_tips.txt.", doc_path); - // 如果平均执行时间超过特定阈值,调整havoc_div的值 + /* Let's keep things moving with slow binaries. */ + if (avg_us > 50000) havoc_div = 10; /* 0-19 execs/sec */ else if (avg_us > 20000) havoc_div = 5; /* 20-49 execs/sec */ else if (avg_us > 10000) havoc_div = 2; /* 50-100 execs/sec */ - // 如果不是从会话中恢复,给出一些警告 if (!resuming_fuzz) { if (max_len > 50 * 1024) @@ -4538,7 +4458,6 @@ static void show_init_stats(void) { } - // 显示统计信息 OKF("Here are some useful stats:\n\n" cGRA " Test case count : " cRST "%u favored, %u variable, %u total\n" @@ -4548,21 +4467,24 @@ static void show_init_stats(void) { ((double)total_bitmap_size) / (total_bitmap_entries ? total_bitmap_entries : 1), DI(min_us), DI(max_us), DI(avg_us)); - // 如果没有指定超时时间,计算一个合适的超时时间 if (!timeout_given) { /* Figure out the appropriate timeout. The basic idea is: 5x average or - 1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second. */ - + 1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second. + + If the program is slow, the multiplier is lowered to 2x or 3x, because + random scheduler jitter is less likely to have any impact, and because + our patience is wearing thin =) */ + if (avg_us > 50000) exec_tmout = avg_us * 2 / 1000; else if (avg_us > 10000) exec_tmout = avg_us * 3 / 1000; else exec_tmout = avg_us * 5 / 1000; - + exec_tmout = MAX(exec_tmout, max_us / 1000); exec_tmout = (exec_tmout + EXEC_TM_ROUND) / EXEC_TM_ROUND * EXEC_TM_ROUND; - + if (exec_tmout > EXEC_TIMEOUT) exec_tmout = EXEC_TIMEOUT; - + ACTF("No -t option specified, so I'll use exec timeout of %u ms.", exec_tmout); @@ -4576,7 +4498,7 @@ static void show_init_stats(void) { /* In dumb mode, re-running every timing out test case with a generous time limit is very expensive, so let's select a more conservative default. */ - + if (dumb_mode && !getenv("AFL_HANG_TMOUT")) hang_tmout = MIN(EXEC_TIMEOUT, exec_tmout * 2 + 100); @@ -4584,7 +4506,10 @@ static void show_init_stats(void) { } -/* 找到大于或等于val的最小的2的幂次方(假设val小于2^31) */ + +/* Find first power of two greater or equal to val (assuming val under + 2^31). */ + static u32 next_p2(u32 val) { u32 ret = 1; @@ -4594,203 +4519,239 @@ static u32 next_p2(u32 val) { } +/* 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; // 长度的下一个2的幂次 + 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. */ - // 如果输入长度小于5,直接返回0,不进行修剪 if (q->len < 5) return 0; - stage_name = tmp; // 设置当前阶段名称 - bytes_trim_in += q->len; // 更新修剪输入的总字节数 + stage_name = tmp; + bytes_trim_in += q->len; + + /* Select initial chunk len, starting with large steps. */ - // 选择初始块长度,从大步长开始 - len_p2 = next_p2(q->len); // 获取q->len的下一个2的幂次 + len_p2 = next_p2(q->len); - remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES); // 计算移除长度 + remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES); + + /* 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)) { - u32 remove_pos = remove_len; // 移除位置 - // 格式化修剪信息 + 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; // 最大阶段数 + stage_cur = 0; + stage_max = q->len / remove_len; - while (remove_pos < q->len) { // 循环移除每个位置的数据 - u32 trim_avail = MIN(remove_len, q->len - remove_pos); // 可修剪的可用长度 - u32 cksum; // 校验和 + while (remove_pos < q->len) { + + u32 trim_avail = MIN(remove_len, q->len - remove_pos); + u32 cksum; - // 写入带有间隔的输入数据 write_with_gap(in_buf, q->len, remove_pos, trim_avail); - fault = run_target(argv, exec_tmout); // 运行目标程序 - trim_execs++; // 更新修剪执行次数 + fault = run_target(argv, exec_tmout); + trim_execs++; - // 如果出现错误或停止请求,跳转到修剪中止 if (stop_soon || fault == FAULT_ERROR) goto 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) { - u32 move_tail = q->len - remove_pos - trim_avail; // 需要移动的尾部长度 - q->len -= trim_avail; // 更新长度 - len_p2 = next_p2(q->len); // 更新下一个2的幂次 + u32 move_tail = q->len - remove_pos - trim_avail; + + q->len -= trim_avail; + len_p2 = next_p2(q->len); - // 移动数据 - memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, move_tail); + memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, + move_tail); + + /* 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 = 1; - memcpy(clean_trace, trace_bits, MAP_SIZE); // 复制执行路径 + memcpy(clean_trace, trace_bits, MAP_SIZE); + } - } else { - remove_pos += remove_len; // 否则,移动到下一个位置 - } - // 定期更新屏幕显示 + } 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(); - stage_cur++; // 更新当前阶段 + stage_cur++; + } - remove_len >>= 1; // 减少移除长度 + remove_len >>= 1; + } - // 如果对in_buf进行了修改,需要更新磁盘上的测试用例 + /* If we have made changes to in_buf, we also need to update the on-disk + version of the test case. */ + if (needs_write) { - s32 fd; // 文件描述符 - // 删除旧的测试用例文件 - unlink(q->fname); // 忽略错误 + s32 fd; + + unlink(q->fname); /* ignore errors */ - // 创建新的测试用例文件 fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", q->fname); // 错误处理 - // 写入新的测试用例数据 + if (fd < 0) PFATAL("Unable to create '%s'", q->fname); + ck_write(fd, in_buf, q->len, q->fname); - close(fd); // 关闭文件描述符 + close(fd); - // 更新执行路径和分数 memcpy(trace_bits, clean_trace, MAP_SIZE); update_bitmap_score(q); + } -abort_trimming: // 修剪中止标签 - bytes_trim_out += q->len; // 更新修剪输出的总字节数 - return fault; // 返回故障状态 +abort_trimming: + + bytes_trim_out += q->len; + return fault; + } -// 写入修改后的测试用例,运行程序,处理结果。处理错误条件,如果需要中止,则返回1。 -// 这是fuzz_one()的辅助函数。 + +/* 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; // 故障状态 - // 如果存在后处理函数 + u8 fault; + if (post_handler) { - // 调用后处理函数 + out_buf = post_handler(out_buf, &len); - if (!out_buf || !len) return 0; // 如果返回为空或长度为0,则返回0 + if (!out_buf || !len) return 0; + } - // 写入测试用例 write_to_testcase(out_buf, len); - fault = run_target(argv, exec_tmout); // 运行目标程序 + fault = run_target(argv, exec_tmout); - // 如果需要停止,则返回1 if (stop_soon) return 1; - // 如果出现超时故障 if (fault == FAULT_TMOUT) { - // 如果连续超时次数超过限制,则跳过当前路径 + if (subseq_tmouts++ > TMOUT_LIMIT) { cur_skipped_paths++; return 1; } - } else { - subseq_tmouts = 0; // 重置连续超时次数 - } - // 用户可以通过SIGUSR1信号请求放弃当前输入 + } else subseq_tmouts = 0; + + /* Users can hit us with SIGUSR1 to request the current input + to be abandoned. */ + if (skip_requested) { - skip_requested = 0; // 重置跳过请求 - cur_skipped_paths++; // 增加跳过路径数 - return 1; // 返回1 + + skip_requested = 0; + cur_skipped_paths++; + return 1; + } - // 处理故障,更新发现的队列 + /* This handles FAULT_ERROR for us: */ + queued_discovered += save_if_interesting(argv, out_buf, len, fault); - // 定期更新统计信息 if (!(stage_cur % stats_update_freq) || stage_cur + 1 == stage_max) show_stats(); - return 0; // 返回0,表示继续执行 + return 0; + } -/* 辅助函数,用于在模糊测试中的块操作选择随机块长度。 - 只要max_len大于0,就不会返回零 */ + +/* Helper to choose random block len for block operations in fuzz_one(). + Doesn't return zero, provided that max_len is > 0. */ static u32 choose_block_len(u32 limit) { u32 min_value, max_value; - u32 rlim = MIN(queue_cycle, 3); // 限制随机数生成的范围 + u32 rlim = MIN(queue_cycle, 3); - if (!run_over10m) rlim = 1; // 如果没有超过10M的运行限制,则设置rlim为1 + if (!run_over10m) rlim = 1; - // 根据随机数选择不同的块长度范围 switch (UR(rlim)) { - case 0: - min_value = 1; // 最小长度设置为1 - max_value = HAVOC_BLK_SMALL; // 最大长度设置为小值 - break; - case 1: - min_value = HAVOC_BLK_SMALL; // 最小长度设置为小值 - max_value = HAVOC_BLK_MEDIUM; // 最大长度设置为中等值 - break; + + case 0: min_value = 1; + max_value = HAVOC_BLK_SMALL; + break; + + case 1: min_value = HAVOC_BLK_SMALL; + max_value = HAVOC_BLK_MEDIUM; + break; + default: - if (UR(10)) { - min_value = HAVOC_BLK_MEDIUM; // 随机选择中等或大长度 - max_value = HAVOC_BLK_LARGE; - } else { - min_value = HAVOC_BLK_LARGE; // 随机选择大或超大长度 - max_value = HAVOC_BLK_XL; - } + + if (UR(10)) { + + min_value = HAVOC_BLK_MEDIUM; + max_value = HAVOC_BLK_LARGE; + + } else { + + min_value = HAVOC_BLK_LARGE; + max_value = HAVOC_BLK_XL; + + } + } - // 如果最小值大于限制,则设置最小值为1 if (min_value >= limit) min_value = 1; - // 返回一个在指定范围内的随机块长度 return min_value + UR(MIN(max_value, limit) - min_value + 1); + } -/* 计算案例的期望分数,以调整havoc模糊测试的长度。 - 这是一个辅助函数,用于fuzz_one()。也许这些常数中的一些应该 - 进入config.h文件中 */ + +/* 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; // 计算平均位图大小 - u32 perf_score = 100; // 初始化性能分数 + u32 avg_exec_us = total_cal_us / total_cal_cycles; + u32 avg_bitmap_size = total_bitmap_size / total_bitmap_entries; + u32 perf_score = 100; - /* 根据此路径的执行速度与全局平均值相比,调整分数。 - 乘数范围从0.1x到3x。快速输入的成本较低,因此给予更多的运行时间。 */ + /* 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. */ 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; @@ -4800,8 +4761,8 @@ static u32 calculate_score(struct queue_entry* q) { else if (q->exec_us * 3 < avg_exec_us) perf_score = 200; else if (q->exec_us * 2 < avg_exec_us) perf_score = 150; - /* 根据位图大小调整分数。工作理论是更好的覆盖率转化为更好的目标。 - 乘数从0.25x到3x。 */ + /* Adjust score based on bitmap size. The working theory is that better + coverage translates to better targets. Multiplier from 0.25x to 3x. */ 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; @@ -4810,33 +4771,42 @@ static u32 calculate_score(struct queue_entry* q) { 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; - /* 根据handicap调整分数。Handicap与我们了解此路径的时间成正比。 - 后来者允许运行更长时间,直到它们赶上其他路径。 */ + /* 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; // 如果handicap大于等于4,则乘以4 - q->handicap -= 4; // 减少handicap值 + + perf_score *= 4; + q->handicap -= 4; + } else if (q->handicap) { - perf_score *= 2; // 如果handicap大于0,则乘以2 - q->handicap--; // 减少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. */ switch (q->depth) { - case 0 ... 3: break; // 如果深度在0到3之间,不调整分数 - case 4 ... 7: perf_score *= 2; break; // 如果深度在4到7之间,乘以2 - case 8 ... 13: perf_score *= 3; break; // 如果深度在8到13之间,乘以3 - case 14 ... 25: perf_score *= 4; break; // 如果深度在14到25之间,乘以4 - default: perf_score *= 5; // 默认情况下,乘以5 + + 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; // 如果分数超过最大限制,则设置为最大限制 + if (perf_score > HAVOC_MAX_MULT * 100) perf_score = HAVOC_MAX_MULT * 100; + + return perf_score; - return perf_score; // 返回计算出的分数 } @@ -4847,136 +4817,182 @@ static u32 calculate_score(struct queue_entry* q) { return 1 if xor_val is zero, which implies that the old and attempted new values are identical and the exec would be a waste of time. */ -// 检测一个值是否可能是通过位翻转操作得到的。 static u8 could_be_bitflip(u32 xor_val) { - u32 sh = 0; // 用于记录位移的变量 - // 如果xor_val为0,表示没有位被翻转,返回1。 + u32 sh = 0; + if (!xor_val) return 1; - // 将xor_val左移,直到最低位为1,记录移动的位数。 + /* Shift left until first bit set. */ + while (!(xor_val & 1)) { sh++; xor_val >>= 1; } - // 如果xor_val是1、3或15,表示只有1、2或4位被翻转,返回1。 + /* 1-, 2-, and 4-bit patterns are OK anywhere. */ + if (xor_val == 1 || xor_val == 3 || xor_val == 15) return 1; - // 如果sh不是8的倍数,那么8位、16位或32位的模式不可能通过位移得到,返回0。 + /* 8-, 16-, and 32-bit patterns are OK only if shift factor is + divisible by 8, since that's the stepover for these ops. */ + if (sh & 7) return 0; - // 如果xor_val是0xff、0xffff或0xffffffff,表示8位、16位或32位的所有位都被翻转,返回1。 if (xor_val == 0xff || xor_val == 0xffff || xor_val == 0xffffffff) return 1; - // 如果以上条件都不满足,返回0。 return 0; + } -// 检测一个值是否可能是通过算术操作从另一个值得到的。 + +/* Helper function to see if a particular value is reachable through + arithmetic operations. Used for similar purposes. */ + static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { - u32 i, ov = 0, nv = 0, diffs = 0; // 用于循环和比较的变量 - // 如果old_val和new_val相同,返回1。 + u32 i, ov = 0, nv = 0, diffs = 0; + if (old_val == new_val) return 1; - // 检查每个字节是否有差异,并尝试通过单字节的调整来生成new_val。 + /* See if one-byte adjustments to any byte could produce this result. */ + for (i = 0; i < blen; i++) { - u8 a = old_val >> (8 * i), // 获取old_val的第i个字节 - b = new_val >> (8 * i); // 获取new_val的第i个字节 - if (a != b) { diffs++; ov = a; nv = b; } // 如果字节不同,增加差异计数 + u8 a = old_val >> (8 * i), + b = new_val >> (8 * i); + + if (a != b) { diffs++; ov = a; nv = b; } + } - // 如果只有一个字节不同,并且差异在可接受的范围内,返回1。 + /* If only one byte differs and the values are within range, return 1. */ + if (diffs == 1) { + if ((u8)(ov - nv) <= ARITH_MAX || (u8)(nv - ov) <= ARITH_MAX) return 1; + } - // 如果blen为1,表示只有单字节,返回0。 if (blen == 1) return 0; - // 检查每两个字节是否有差异,并尝试通过双字节的调整来生成new_val。 + /* See if two-byte adjustments to any byte would produce this result. */ + diffs = 0; + for (i = 0; i < blen / 2; i++) { - u16 a = old_val >> (16 * i), // 获取old_val的第i个双字节 - b = new_val >> (16 * i); // 获取new_val的第i个双字节 - if (a != b) { diffs++; ov = a; nv = b; } // 如果双字节不同,增加差异计数 + u16 a = old_val >> (16 * i), + b = new_val >> (16 * i); + + if (a != b) { diffs++; ov = a; nv = b; } + } - // 如果只有一个双字节不同,并且差异在可接受的范围内,返回1。 + /* If only one word differs and the values are within range, return 1. */ + if (diffs == 1) { + if ((u16)(ov - nv) <= ARITH_MAX || (u16)(nv - ov) <= ARITH_MAX) return 1; - // 尝试交换字节顺序后再次检查 ov = SWAP16(ov); nv = SWAP16(nv); + if ((u16)(ov - nv) <= ARITH_MAX || (u16)(nv - ov) <= ARITH_MAX) return 1; + } - // 如果blen为4,表示有四个字节,检查整个四字节的差异。 + /* Finally, let's do the same thing for dwords. */ + if (blen == 4) { + if ((u32)(old_val - new_val) <= ARITH_MAX || (u32)(new_val - old_val) <= ARITH_MAX) return 1; - // 尝试交换字节顺序后再次检查 new_val = SWAP32(new_val); old_val = SWAP32(old_val); + if ((u32)(old_val - new_val) <= ARITH_MAX || (u32)(new_val - old_val) <= ARITH_MAX) return 1; + } - // 如果以上条件都不满足,返回0。 return 0; + } -// 检测一个值是否可能是通过插入特定的整数得到的,考虑到之前已经插入过的值。 + +/* Last but not least, a similar helper to see if insertion of an + interesting integer is redundant given the insertions done for + shorter blen. The last param (check_le) is set if the caller + already executed LE insertion for current blen and wants to see + if BE variant passed in new_val is unique. */ + static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { + u32 i, j; - // 如果old_val和new_val相同,返回1。 if (old_val == new_val) return 1; - // 检查是否可以通过在old_val中插入一个字节的值来得到new_val。 + /* See if one-byte insertions from interesting_8 over old_val could + produce new_val. */ + for (i = 0; i < blen; i++) { + for (j = 0; j < sizeof(interesting_8); j++) { - u32 tval = (old_val & ~(0xff << (i * 8))) | // 清除old_val的第i个字节 - (((u8)interesting_8[j]) << (i * 8)); // 插入新的字节值 - if (new_val == tval) return 1; // 如果得到的值与new_val相同,返回1 + u32 tval = (old_val & ~(0xff << (i * 8))) | + (((u8)interesting_8[j]) << (i * 8)); + + if (new_val == tval) return 1; + } + } - // 如果blen为2并且check_le为0,表示不需要检查小端字节序,返回0。 + /* Bail out unless we're also asked to examine two-byte LE insertions + as a preparation for BE attempts. */ + if (blen == 2 && !check_le) return 0; - // 检查是否可以通过在old_val中插入一个双字节的值来得到new_val。 + /* See if two-byte insertions over old_val could give us new_val. */ + for (i = 0; i < blen - 1; i++) { + for (j = 0; j < sizeof(interesting_16) / 2; j++) { - u32 tval = (old_val & ~(0xffff << (i * 8))) | // 清除old_val的第i个双字节 - (((u16)interesting_16[j]) << (i * 8)); // 插入新的双字节值 - if (new_val == tval) return 1; // 如果得到的值与new_val相同,返回1 + u32 tval = (old_val & ~(0xffff << (i * 8))) | + (((u16)interesting_16[j]) << (i * 8)); + + if (new_val == tval) return 1; + + /* Continue here only if blen > 2. */ - // 如果blen大于2,尝试交换字节顺序后再次检查 if (blen > 2) { + tval = (old_val & ~(0xffff << (i * 8))) | (SWAP16(interesting_16[j]) << (i * 8)); - if (new_val == tval) return 1; // 如果得到的值与new_val相同,返回1 + + if (new_val == tval) return 1; + } + } + } - // 如果blen为4并且check_le为1,表示需要检查大端字节序。 if (blen == 4 && check_le) { - // 检查是否可以通过插入一个四字节的值来得到new_val(只考虑小端)。 + + /* See if four-byte insertions could produce the same result + (LE only). */ + for (j = 0; j < sizeof(interesting_32) / 4; j++) if (new_val == (u32)interesting_32[j]) return 1; + } - // 如果以上条件都不满足,返回0。 return 0; + } @@ -4986,717 +5002,552 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { 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 - // 如果定义了IGNORE_FINDS宏,跳过不在初始数据集中的条目。 + + /* In IGNORE_FINDS mode, skip any entries that weren't in the + initial data set. */ + if (queue_cur->depth > 1) return 1; #else - // 如果有待处理的优选输入且不在IGNORE_FINDS模式。 + if (pending_favored) { - // 如果队列中有新的优选输入,可能跳过已模糊测试的或非优选的情况。 + /* If we have any favored, non-fuzzed new arrivals in the queue, + possibly skip to them at the expense of already-fuzzed or non-favored + cases. */ + if ((queue_cur->was_fuzzed || !queue_cur->favored) && UR(100) < SKIP_TO_NEW_PROB) return 1; } else if (!dumb_mode && !queue_cur->favored && queued_paths > 10) { - // 否则,对于非优选情况,也可能跳过,但概率较低。 - // 对于已经模糊测试的输入,跳过的概率更高;对于从未模糊测试的输入,概率更低。 + /* Otherwise, still possibly skip non-favored cases, albeit less often. + The odds of skipping stuff are higher for already-fuzzed inputs and + lower for never-fuzzed entries. */ if (queue_cycle > 1 && !queue_cur->was_fuzzed) { - // 如果是队列中的第二轮且当前输入未被模糊测试过,根据概率跳过。 if (UR(100) < SKIP_NFAV_NEW_PROB) return 1; } else { - // 对于已经模糊测试过的输入,根据概率跳过。 if (UR(100) < SKIP_NFAV_OLD_PROB) return 1; } } + #endif /* ^IGNORE_FINDS */ -// 如果当前环境不是终端(tty),则打印模糊测试的进度信息。 -if (not_on_tty) { + if (not_on_tty) { ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found)...", current_entry, queued_paths, unique_crashes); fflush(stdout); -} + } + + /* Map the test case into memory. */ -// 打开当前测试用例文件并将其映射到内存中。 -fd = open(queue_cur->fname, O_RDONLY); + fd = open(queue_cur->fname, O_RDONLY); -// 如果打开文件失败,则打印错误信息并终止程序。 -if (fd < 0) PFATAL("Unable to open '%s'", queue_cur->fname); + if (fd < 0) PFATAL("Unable to open '%s'", queue_cur->fname); -// 获取文件长度。 -len = queue_cur->len; + len = queue_cur->len; -// 将文件内容映射到内存中的in_buf变量。 -orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + 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); + if (orig_in == MAP_FAILED) PFATAL("Unable to mmap '%s'", queue_cur->fname); -// 关闭文件描述符。 -close(fd); + close(fd); + + /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every + single byte anyway, so it wouldn't give us any performance or memory usage + benefits. */ -// 分配内存用于输出缓冲区,这里使用ck_alloc_nozero来分配非零内存。 -out_buf = ck_alloc_nozero(len); + out_buf = ck_alloc_nozero(len); -// 初始化连续超时计数器。 -subseq_tmouts = 0; + subseq_tmouts = 0; -// 设置当前深度。 -cur_depth = queue_cur->depth; + cur_depth = queue_cur->depth; -/******************************************* -* CALIBRATION (only if failed earlier on) * -*******************************************/ + /******************************************* + * CALIBRATION (only if failed earlier on) * + *******************************************/ -// 如果当前测试用例之前校准失败,则尝试重新校准。 -if (queue_cur->cal_failed) { + if (queue_cur->cal_failed) { u8 res = FAULT_TMOUT; - // 如果校准失败次数小于允许的最大次数,则尝试重新校准。 if (queue_cur->cal_failed < CAL_CHANCES) { - // 重置exec_cksum,以便重新执行测试用例。 - queue_cur->exec_cksum = 0; + /* 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 */ - // 执行校准函数。 - res = calibrate_case(argv, queue_cur, in_buf, queue_cycle - 1, 0); + queue_cur->exec_cksum = 0; - // 如果校准失败,则终止程序。 - if (res == FAULT_ERROR) - FATAL("Unable to execute target application"); + res = calibrate_case(argv, queue_cur, in_buf, queue_cycle - 1, 0); - } + if (res == FAULT_ERROR) + FATAL("Unable to execute target application"); + + } - // 如果需要停止或者校准结果不是预期的崩溃模式,则跳过当前测试用例。 if (stop_soon || res != crash_mode) { - cur_skipped_paths++; - goto abandon_entry; + cur_skipped_paths++; + goto abandon_entry; } -} + } -/************ -* TRIMMING * -************/ + /************ + * TRIMMING * + ************/ -// 如果没有启用dumb_mode并且当前测试用例尚未修剪,则执行修剪操作。 -if (!dumb_mode && !queue_cur->trim_done) { + if (!dumb_mode && !queue_cur->trim_done) { u8 res = trim_case(argv, queue_cur, in_buf); - // 如果修剪失败,则终止程序。 if (res == FAULT_ERROR) - FATAL("Unable to execute target application"); + FATAL("Unable to execute target application"); - // 如果需要停止,则跳过当前测试用例。 if (stop_soon) { - cur_skipped_paths++; - goto abandon_entry; + cur_skipped_paths++; + goto abandon_entry; } - // 标记为已修剪,不再尝试修剪。 + /* Don't retry trimming, even if it failed. */ + queue_cur->trim_done = 1; - // 更新文件长度。 if (len != queue_cur->len) len = queue_cur->len; -} + } -// 将输入缓冲区的内容复制到输出缓冲区。 -memcpy(out_buf, in_buf, len); + memcpy(out_buf, in_buf, len); + + /********************* + * PERFORMANCE SCORE * + *********************/ -/********************* -* PERFORMANCE SCORE * -*********************/ + orig_perf = perf_score = calculate_score(queue_cur); -// 计算性能得分。 -orig_perf = perf_score = calculate_score(queue_cur); + /* Skip right away if -d is given, if we have done deterministic fuzzing on + this entry ourselves (was_fuzzed), or if it has gone through deterministic + testing in earlier, resumed runs (passed_det). */ -// 如果启用了-d选项,或者已经对当前测试用例执行过确定性模糊测试,或者它已经通过了早期的确定性测试,则跳过确定性测试阶段。 -if (skip_deterministic || queue_cur->was_fuzzed || queue_cur->passed_det) + if (skip_deterministic || queue_cur->was_fuzzed || queue_cur->passed_det) goto havoc_stage; -// 如果执行路径校验和将当前测试用例排除在当前主实例的范围之外,则跳过确定性测试。 -if (master_max && (queue_cur->exec_cksum % master_max) != master_id - 1) + /* Skip deterministic fuzzing if exec path checksum puts this out of scope + for this master instance. */ + + if (master_max && (queue_cur->exec_cksum % master_max) != master_id - 1) goto havoc_stage; -// 标记为正在执行确定性测试。 -doing_det = 1; + doing_det = 1; -/********************************************* -* SIMPLE BITFLIP (+dictionary construction) * -********************************************/ + /********************************************* + * SIMPLE BITFLIP (+dictionary construction) * + *********************************************/ -// 定义一个宏,用于翻转位。 #define FLIP_BIT(_ar, _b) do { \ u8* _arf = (u8*)(_ar); \ u32 _bf = (_b); \ _arf[(_bf) >> 3] ^= (128 >> ((_bf) & 7)); \ } while (0) -// 执行简单的位翻转测试。 -stage_short = "flip1"; -stage_max = len << 3; -stage_name = "bitflip 1/1"; + /* Single walking bit. */ + + stage_short = "flip1"; + stage_max = len << 3; + stage_name = "bitflip 1/1"; -stage_val_type = STAGE_VAL_NONE; + stage_val_type = STAGE_VAL_NONE; -// 初始化原始命中计数器。 -orig_hit_cnt = queued_paths + unique_crashes; + orig_hit_cnt = queued_paths + unique_crashes; -// 保存之前的校验和。 -prev_cksum = queue_cur->exec_cksum; + prev_cksum = queue_cur->exec_cksum; -// 遍历每个位并执行位翻转测试。 -for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { + for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { - // 计算当前位所在的字节。 stage_cur_byte = stage_cur >> 3; - // 翻转当前位。 FLIP_BIT(out_buf, stage_cur); - // 执行模糊测试。 if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - // 恢复原始位状态。 FLIP_BIT(out_buf, stage_cur); - // 在翻转最低有效位时,执行额外的检测以识别可能的语法标记。 - if (!dumb_mode && (stage_cur & 7) == 7) { + /* 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: - // 计算新的校验和。 - u32 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + xxxxxxxxIHDRxxxxxxxx - // 如果到达文件末尾并且我们仍在收集字符串,则获取最终字符并强制输出。 - if (stage_cur == stage_max - 1 && cksum == prev_cksum) { + ...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. - if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3]; - a_len++; + 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. - if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) - maybe_add_auto(a_collect, a_len); + The caveat is that we won't generate dictionaries in the -d mode or -S + mode - but that's probably a fair trade-off. - } else if (cksum != prev_cksum) { + This won't work particularly well with paths that exhibit variable + behavior, but fails gracefully, so we'll carry out the checks anyway. - // 否则,如果校验和已更改,则查看是否有值得收集的内容,并在是的情况下收集。 - if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) - maybe_add_auto(a_collect, a_len); + */ - a_len = 0; - prev_cksum = cksum; + if (!dumb_mode && (stage_cur & 7) == 7) { - } + u32 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); - // 继续收集字符串,但只有在位翻转实际上产生了影响时,我们才不希望无操作标记。 + if (stage_cur == stage_max - 1 && cksum == prev_cksum) { - if (cksum != queue_cur->exec_cksum) { + /* If at end of file and we are still collecting a string, grab the + final character and force output. */ - if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3]; - a_len++; + if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3]; + a_len++; - } + if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) + maybe_add_auto(a_collect, a_len); - } + } else if (cksum != prev_cksum) { -} -// 初始化新的命中计数,这是待处理路径数和唯一崩溃数的总和。 -new_hit_cnt = queued_paths + unique_crashes; + /* Otherwise, if the checksum has changed, see if we have something + worthwhile queued up, and collect that if the answer is yes. */ -// 更新STAGE_FLIP1阶段的发现计数和周期数。 -stage_finds[STAGE_FLIP1] += new_hit_cnt - orig_hit_cnt; // 发现计数增加新的命中数减去原始的命中数。 -stage_cycles[STAGE_FLIP1] += stage_max; // 增加STAGE_FLIP1的最大周期数。 + if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) + maybe_add_auto(a_collect, a_len); -/* 两个行走的位。 */ + a_len = 0; + prev_cksum = cksum; -// 设置当前阶段的名称和简称。 -stage_name = "bitflip 2/1"; -stage_short = "flip2"; -// 计算STAGE_FLIP1阶段的最大值,即输入数据长度的8倍减1。 -stage_max = (len << 3) - 1; + } -// 将新的命中计数赋值给原始命中计数,以便下一次迭代使用。 -orig_hit_cnt = new_hit_cnt; + /* Continue collecting string, but only if the bit flip actually made + any difference - we don't want no-op tokens. */ -// 循环遍历STAGE_FLIP1阶段的最大值。 -for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { + if (cksum != queue_cur->exec_cksum) { - // 计算当前位操作的字节索引。 - stage_cur_byte = stage_cur >> 3; + if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3]; + a_len++; - // 翻转输出缓冲区中当前位置和下一个位置的位。 - FLIP_BIT(out_buf, stage_cur); - FLIP_BIT(out_buf, stage_cur + 1); + } - // 如果common_fuzz_stuff函数返回失败,跳转到abandon_entry标签处。 - 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; -// 计算新的命中次数,这是当前队列中的路径数加上唯一的崩溃次数。 -new_hit_cnt = queued_paths + unique_crashes; - -// 更新FLIP2阶段的发现次数和周期数。 -stage_finds[STAGE_FLIP2] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_FLIP2] += stage_max; - -// 定义FLIP4阶段的名称和简称。 -stage_name = "bitflip 4/1"; -stage_short = "flip4"; -// 计算FLIP4阶段的最大值,这是输入长度的三倍减去3。 -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); - - // 如果common_fuzz_stuff函数返回真,则跳转到abandon_entry标签。 - 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); -} + stage_finds[STAGE_FLIP1] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_FLIP1] += stage_max; -// 更新FLIP4阶段的发现次数和周期数。 -new_hit_cnt = queued_paths + unique_crashes; -stage_finds[STAGE_FLIP4] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_FLIP4] += stage_max; + /* Two walking bits. */ -// 定义一些宏,用于计算effector map中的位置和长度。 -#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2) -#define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1)) -#define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l)) -#define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1) + stage_name = "bitflip 2/1"; + stage_short = "flip2"; + stage_max = (len << 3) - 1; -// 初始化下一个步骤的effector map,并标记第一个和最后一个字节为活跃的。 -eff_map = ck_alloc(EFF_ALEN(len)); -eff_map[0] = 1; + orig_hit_cnt = new_hit_cnt; -// 如果最后一个字节的位置不等于0,则标记它为活跃的,并增加eff_cnt计数器。 -if (EFF_APOS(len - 1) != 0) { - eff_map[EFF_APOS(len - 1)] = 1; - eff_cnt++; -} + for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { -// 定义FLIP8阶段的名称和简称。 -stage_name = "bitflip 8/8"; -stage_short = "flip8"; -// 设置FLIP8阶段的最大值为输入长度。 -stage_max = len; + stage_cur_byte = stage_cur >> 3; -// 将新的命中次数保存为原始命中次数,以便在下一个阶段使用。 -orig_hit_cnt = new_hit_cnt; + FLIP_BIT(out_buf, stage_cur); + FLIP_BIT(out_buf, stage_cur + 1); -// 循环,对每个字节进行操作。 -for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { - // 计算当前操作的字节位置。 - stage_cur_byte = stage_cur; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - // 翻转当前字节的所有位。 - out_buf[stage_cur] ^= 0xFF; + FLIP_BIT(out_buf, stage_cur); + FLIP_BIT(out_buf, stage_cur + 1); - // 如果common_fuzz_stuff函数返回真,则跳转到abandon_entry标签。 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + } - // 如果当前字节在effector map中未被标记,则进行进一步的检查。 - if (!eff_map[EFF_APOS(stage_cur)]) { - u32 cksum; + new_hit_cnt = queued_paths + unique_crashes; - // 如果处于dumb模式或文件非常短,则不进行checksum计算。 - if (!dumb_mode && len >= EFF_MIN_LEN) - cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); - else - cksum = ~queue_cur->exec_cksum; + stage_finds[STAGE_FLIP2] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_FLIP2] += stage_max; - // 如果checksum与预期不符,则标记该字节为活跃的,并增加eff_cnt计数器。 - if (cksum != queue_cur->exec_cksum) { - eff_map[EFF_APOS(stage_cur)] = 1; - eff_cnt++; - } - } + /* Four walking bits. */ - // 恢复原始字节状态。 - out_buf[stage_cur] ^= 0xFF; -} + stage_name = "bitflip 4/1"; + stage_short = "flip4"; + stage_max = (len << 3) - 3; -/* 如果效应器(effector)映射的密度超过EFF_MAX_PERC指定的百分比, - 则标记整个映射为值得fuzzing(模糊测试),因为我们不会节省太多时间。 */ + orig_hit_cnt = new_hit_cnt; -if (eff_cnt != EFF_ALEN(len) && - eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { + for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { - // 如果超过阈值,则将整个效应器映射标记为1(即,所有位都值得测试) - memset(eff_map, 1, EFF_ALEN(len)); + stage_cur_byte = stage_cur >> 3; - // 更新被选中进行模糊测试的块的数量 - blocks_eff_select += EFF_ALEN(len); + 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); -} else { + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - // 如果没有超过阈值,则只增加实际有效应器的计数 - blocks_eff_select += eff_cnt; + 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); -} + } -// 更新总共被考虑进行模糊测试的块的数量 -blocks_eff_total += EFF_ALEN(len); + new_hit_cnt = queued_paths + unique_crashes; -// 更新在当前阶段发现的新问题(如路径或崩溃)的数量 -new_hit_cnt = queued_paths + unique_crashes; + stage_finds[STAGE_FLIP4] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_FLIP4] += stage_max; -// 更新当前阶段(STAGE_FLIP8)的发现和周期计数 -stage_finds[STAGE_FLIP8] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_FLIP8] += 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. -// 如果长度小于2,则跳过位翻转 -if (len < 2) goto skip_bitflip; + */ -// 设置当前阶段的名称和简称 -stage_name = "bitflip 16/8"; -stage_short = "flip16"; -stage_cur = 0; // 当前阶段的当前进度 -stage_max = len - 1; // 当前阶段的最大进度 +#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2) +#define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1)) +#define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l)) +#define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1) -// 记录原始的命中计数 -orig_hit_cnt = new_hit_cnt; + /* Initialize effector map for the next step (see comments below). Always + flag first and last byte as doing something. */ -// 遍历输入数据,每次处理两个字节 -for (i = 0; i < len - 1; i++) { + eff_map = ck_alloc(EFF_ALEN(len)); + eff_map[0] = 1; - // 检查效应器映射,如果当前和下一个字节都不是效应器,则跳过 - if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { - stage_max--; - continue; + if (EFF_APOS(len - 1) != 0) { + eff_map[EFF_APOS(len - 1)] = 1; + eff_cnt++; } - // 设置当前处理的字节位置 - stage_cur_byte = i; + /* Walking byte. */ - // 翻转当前两个字节的所有位 - *(u16*)(out_buf + i) ^= 0xFFFF; + stage_name = "bitflip 8/8"; + stage_short = "flip8"; + stage_max = len; - // 执行模糊测试的常见操作,如果失败则跳转到abandon_entry - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; + orig_hit_cnt = new_hit_cnt; - // 恢复原始数据 - *(u16*)(out_buf + i) ^= 0xFFFF; + for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { -} + stage_cur_byte = stage_cur; -// 更新新发现的问题数量和周期计数 -new_hit_cnt = queued_paths + unique_crashes; -stage_finds[STAGE_FLIP16] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_FLIP16] += stage_max; + out_buf[stage_cur] ^= 0xFF; -// 如果长度小于4,则跳过四字节位翻转 -if (len < 4) goto skip_bitflip; + 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. */ -// 设置当前阶段的名称和简称 -stage_name = "bitflip 32/8"; -stage_short = "flip32"; -stage_cur = 0; -stage_max = len - 3; + if (!eff_map[EFF_APOS(stage_cur)]) { -// 记录原始的命中计数 -orig_hit_cnt = new_hit_cnt; + u32 cksum; -// 遍历输入数据,每次处理四个字节 -for (i = 0; i < len - 3; i++) { + /* If in dumb mode or if the file is very short, just flag everything + without wasting time on checksums. */ - // 检查效应器映射,如果当前和接下来三个字节都不是效应器,则跳过 - if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && - !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) { - stage_max--; - continue; - } + if (!dumb_mode && len >= EFF_MIN_LEN) + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + else + cksum = ~queue_cur->exec_cksum; - // 设置当前处理的字节位置 - stage_cur_byte = i; + if (cksum != queue_cur->exec_cksum) { + eff_map[EFF_APOS(stage_cur)] = 1; + eff_cnt++; + } - // 翻转当前四个字节的所有位 - *(u32*)(out_buf + i) ^= 0xFFFFFFFF; + } - // 执行模糊测试的常见操作,如果失败则跳转到abandon_entry - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; + out_buf[stage_cur] ^= 0xFF; - // 恢复原始数据 - *(u32*)(out_buf + i) ^= 0xFFFFFFFF; + } -} + /* 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. */ -// 更新新发现的问题数量和周期计数 -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 (eff_cnt != EFF_ALEN(len) && + eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { -// 如果设置了no_arith标志,则跳过算术操作 -if (no_arith) goto skip_arith; + memset(eff_map, 1, EFF_ALEN(len)); -/************************** - * ARITHMETIC INC/DEC * - *************************/ + blocks_eff_select += EFF_ALEN(len); -// 8位算术操作 -stage_name = "arith 8/8"; -stage_short = "arith8"; -stage_cur = 0; // 当前阶段的当前值 -stage_max = 2 * len * ARITH_MAX; // 最大可能的值 + } else { -stage_val_type = STAGE_VAL_LE; // 值的类型,这里设置为小端 + blocks_eff_select += eff_cnt; -orig_hit_cnt = new_hit_cnt; // 原始的命中次数 + } -// 对输入缓冲区中的每个字节进行操作 -for (i = 0; i < len; i++) { - u8 orig = out_buf[i]; // 原始字节值 + blocks_eff_total += EFF_ALEN(len); - // 如果当前位置不在影响映射中,则跳过 - if (!eff_map[EFF_APOS(i)]) { - stage_max -= 2 * ARITH_MAX; - continue; - } + new_hit_cnt = queued_paths + unique_crashes; - stage_cur_byte = i; // 当前处理的字节位置 + stage_finds[STAGE_FLIP8] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_FLIP8] += stage_max; - // 对每个可能的增量进行操作 - for (j = 1; j <= ARITH_MAX; j++) { - u8 r = orig ^ (orig + j); // 计算增加j后的值 + /* Two walking bytes. */ - // 如果结果不可能是位翻转的结果,则进行算术操作 - if (!could_be_bitflip(r)) { - stage_cur_val = j; - out_buf[i] = orig + j; // 应用增加操作 + if (len < 2) goto skip_bitflip; - // 如果公共模糊测试函数返回成功,则跳转到abandon_entry标签 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + stage_name = "bitflip 16/8"; + stage_short = "flip16"; + stage_cur = 0; + stage_max = len - 1; - r = orig ^ (orig - j); // 计算减少j后的值 + orig_hit_cnt = new_hit_cnt; - // 如果结果不可能是位翻转的结果,则进行算术操作 - if (!could_be_bitflip(r)) { - stage_cur_val = -j; - out_buf[i] = orig - j; // 应用减少操作 + for (i = 0; i < len - 1; i++) { - // 如果公共模糊测试函数返回成功,则跳转到abandon_entry标签 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + /* Let's consult the effector map... */ - out_buf[i] = orig; // 恢复原始值 - } -} + if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { + stage_max--; + continue; + } -// 更新命中次数和阶段统计信息 -new_hit_cnt = queued_paths + unique_crashes; -stage_finds[STAGE_ARITH8] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_ARITH8] += stage_max; + stage_cur_byte = i; -// 16位算术操作,包括小端和大端 -if (len < 2) goto skip_arith; + *(u16*)(out_buf + i) ^= 0xFFFF; -stage_name = "arith 16/8"; -stage_short = "arith16"; -stage_cur = 0; -stage_max = 4 * (len - 1) * ARITH_MAX; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + stage_cur++; -orig_hit_cnt = new_hit_cnt; + *(u16*)(out_buf + i) ^= 0xFFFF; -// 对输入缓冲区中每两个字节进行操作 -for (i = 0; i < len - 1; i++) { - u16 orig = *(u16*)(out_buf + i); // 原始的16位值 - // 如果当前位置和下一个位置都不在影响映射中,则跳过 - if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { - stage_max -= 4 * ARITH_MAX; - continue; } - stage_cur_byte = i; + new_hit_cnt = queued_paths + unique_crashes; - // 对每个可能的增量进行操作 - 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); // 大端减少 + stage_finds[STAGE_FLIP16] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_FLIP16] += stage_max; - // 尝试小端增加和减少操作 - stage_val_type = STAGE_VAL_LE; + if (len < 4) goto skip_bitflip; - if ((orig & 0xff) + j > 0xff && !could_be_bitflip(r1)) { - stage_cur_val = j; - *(u16*)(out_buf + i) = orig + j; + /* Four walking bytes. */ - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + stage_name = "bitflip 32/8"; + stage_short = "flip32"; + stage_cur = 0; + stage_max = len - 3; - if ((orig & 0xff) < j && !could_be_bitflip(r2)) { - stage_cur_val = -j; - *(u16*)(out_buf + i) = orig - j; + orig_hit_cnt = new_hit_cnt; - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + for (i = 0; i < len - 3; i++) { - // 尝试大端增加和减少操作 - stage_val_type = STAGE_VAL_BE; + /* 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--; + continue; + } - if ((orig >> 8) + j > 0xff && !could_be_bitflip(r3)) { - stage_cur_val = j; - *(u16*)(out_buf + i) = SWAP16(SWAP16(orig) + j); + stage_cur_byte = i; - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + *(u32*)(out_buf + i) ^= 0xFFFFFFFF; - if ((orig >> 8) < j && !could_be_bitflip(r4)) { - 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++; - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + *(u32*)(out_buf + i) ^= 0xFFFFFFFF; - *(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; + new_hit_cnt = queued_paths + unique_crashes; -// 32位算术操作,包括小端和大端 -if (len < 4) goto skip_arith; + stage_finds[STAGE_FLIP32] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_FLIP32] += stage_max; -stage_name = "arith 32/8"; -stage_short = "arith32"; -stage_cur = 0; -stage_max = 4 * (len - 3) * ARITH_MAX; +skip_bitflip: -orig_hit_cnt = new_hit_cnt; + if (no_arith) goto skip_arith; -// 对输入缓冲区中每四个字节进行操作 -for (i = 0; i < len - 3; i++) { - u32 orig = *(u32*)(out_buf + i); // 原始的32位值 + /********************** + * ARITHMETIC INC/DEC * + **********************/ - // 如果当前位置和接下来的三个位置都不在影响映射中,则跳过 - 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; - } + /* 8-bit arithmetics. */ - stage_cur_byte = i; + stage_name = "arith 8/8"; + stage_short = "arith8"; + stage_cur = 0; + stage_max = 2 * len * ARITH_MAX; - // 对每个可能的增量进行操作 - 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); // 大端减少 + stage_val_type = STAGE_VAL_LE; - // 尝试小端增加和减少操作 - stage_val_type = STAGE_VAL_LE; + orig_hit_cnt = new_hit_cnt; - if ((orig & 0xffff) + j > 0xffff && !could_be_bitflip(r1)) { - stage_cur_val = j; - *(u32*)(out_buf + i) = orig + j; + for (i = 0; i < len; i++) { - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + u8 orig = out_buf[i]; - if ((orig & 0xffff) < j && !could_be_bitflip(r2)) { - stage_cur_val = -j; - *(u32*)(out_buf + i) = orig - j; + /* Let's consult the effector map... */ - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + if (!eff_map[EFF_APOS(i)]) { + stage_max -= 2 * ARITH_MAX; + continue; + } + + stage_cur_byte = i; - // 尝试大端增加和减少操作 - stage_val_type = STAGE_VAL_BE; + for (j = 1; j <= ARITH_MAX; j++) { - if ((SWAP32(orig) & 0xffff) + j > 0xffff && !could_be_bitflip(r3)) { - stage_cur_val = j; - *(u32*)(out_buf + i) = SWAP32(SWAP32(orig) + j); + u8 r = orig ^ (orig + j); - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; - } else stage_max--; + /* Do arithmetic operations only if the result couldn't be a product + of a 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)) { - // 如果原始值的大端格式的低16位小于增量j,并且结果不可能是位翻转的结果 - if ((SWAP32(orig) & 0xffff) < j && !could_be_bitflip(r4)) { - // 设置当前阶段的值为负的增量j stage_cur_val = -j; - // 对当前的32位值进行大端格式的减法操作,并更新输出缓冲区 - *(u32*)(out_buf + i) = SWAP32(SWAP32(orig) - j); + out_buf[i] = orig - j; - // 执行公共的模糊测试操作,如果测试中断,则跳转到abandon_entry标签 if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - // 如果测试没有中断,增加当前阶段的计数器 stage_cur++; - } else { - // 如果操作不可能产生新的命中,减少最大阶段计数 - stage_max--; - } - // 恢复原始的32位值,以便下一次迭代使用 - *(u32*)(out_buf + i) = orig; + } else stage_max--; + + out_buf[i] = orig; } @@ -5704,1091 +5555,1303 @@ for (i = 0; i < len - 3; i++) { new_hit_cnt = queued_paths + unique_crashes; - stage_finds[STAGE_ARITH32] += new_hit_cnt - orig_hit_cnt; - stage_cycles[STAGE_ARITH32] += stage_max; + stage_finds[STAGE_ARITH8] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_ARITH8] += stage_max; -// 如果设置了跳过算术处理的标志,则跳到下一个阶段。 -skip_arith: + /* 16-bit arithmetics, both endians. */ -/********************** - * INTERESTING VALUES * -**********************/ + if (len < 2) goto skip_arith; -// 设置当前阶段的名称和简称。 -stage_name = "interest 8/8"; -stage_short = "int8"; -stage_cur = 0; // 初始化当前阶段的计数器。 -stage_max = len * sizeof(interesting_8); // 最大次数为输入数据长度与有趣8位整数数组大小的乘积。 + stage_name = "arith 16/8"; + stage_short = "arith16"; + stage_cur = 0; + stage_max = 4 * (len - 1) * ARITH_MAX; -stage_val_type = STAGE_VAL_LE; // 设置阶段值类型为小端。 + orig_hit_cnt = new_hit_cnt; -orig_hit_cnt = new_hit_cnt; // 记录原始的命中次数。 + for (i = 0; i < len - 1; i++) { -// 设置8位整数。 -for (i = 0; i < len; i++) { - u8 orig = out_buf[i]; // 保存原始字节。 + u16 orig = *(u16*)(out_buf + i); - // 如果当前位置在效应器映射中没有设置,则跳过。 - if (!eff_map[EFF_APOS(i)]) { - stage_max -= sizeof(interesting_8); - continue; + /* Let's consult the effector map... */ + + if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { + stage_max -= 4 * ARITH_MAX; + continue; } - stage_cur_byte = i; // 设置当前处理的字节位置。 + stage_cur_byte = i; - // 遍历有趣的8位整数数组。 - for (j = 0; j < sizeof(interesting_8); j++) { - // 如果该值可能是位翻转或算术操作的结果,则跳过。 - if (could_be_bitflip(orig ^ (u8)interesting_8[j]) || - could_be_arith(orig, (u8)interesting_8[j], 1)) { - stage_max--; - continue; - } + 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); - stage_cur_val = interesting_8[j]; // 设置当前阶段的值。 - out_buf[i] = interesting_8[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)) { + + 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--; - out_buf[i] = orig; // 恢复原始值。 - stage_cur++; // 增加当前阶段的计数器。 - } -} + if ((orig & 0xff) < j && !could_be_bitflip(r2)) { + + stage_cur_val = -j; + *(u16*)(out_buf + i) = orig - j; + + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + stage_cur++; -// 更新新的命中次数。 -new_hit_cnt = queued_paths + unique_crashes; + } else stage_max--; -// 记录在当前阶段发现的新问题数量和周期计数。 -stage_finds[STAGE_INTEREST8] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_INTEREST8] += stage_max; + /* Big endian comes next. Same deal. */ -// 如果设置了不进行算术处理或输入数据长度小于2,则跳过16位整数的处理。 -if (no_arith || len < 2) goto skip_interest; + stage_val_type = STAGE_VAL_BE; -// 设置当前阶段的名称和简称。 -stage_name = "interest 16/8"; -stage_short = "int16"; -stage_cur = 0; // 初始化当前阶段的计数器。 -stage_max = 2 * (len - 1) * (sizeof(interesting_16) >> 1); // 最大次数为输入数据长度减1与有趣16位整数数组大小的乘积。 -orig_hit_cnt = new_hit_cnt; // 记录原始的命中次数。 + if ((orig >> 8) + j > 0xff && !could_be_bitflip(r3)) { -// 设置16位整数,包括小端和大端。 -for (i = 0; i < len - 1; i++) { - u16 orig = *(u16*)(out_buf + i); // 保存原始的16位整数。 + stage_cur_val = j; + *(u16*)(out_buf + i) = SWAP16(SWAP16(orig) + j); - // 如果当前位置和下一位在效应器映射中没有设置,则跳过。 - if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { - stage_max -= sizeof(interesting_16); - continue; - } + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + stage_cur++; - stage_cur_byte = i; // 设置当前处理的字节位置。 + } else stage_max--; - // 遍历有趣的16位整数数组。 - for (j = 0; j < sizeof(interesting_16) / 2; j++) { - stage_cur_val = interesting_16[j]; // 设置当前阶段的值。 + if ((orig >> 8) < j && !could_be_bitflip(r4)) { - // 如果该值不可能是位翻转、算术操作或单字节有趣值插入的结果,则进行处理。 - 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)) { - stage_val_type = STAGE_VAL_LE; // 设置阶段值类型为小端。 + stage_cur_val = -j; + *(u16*)(out_buf + i) = SWAP16(SWAP16(orig) - j); - *(u16*)(out_buf + i) = interesting_16[j]; // 设置有趣的值。 + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + stage_cur++; - // 执行模糊测试操作,如果需要则放弃当前输入。 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; // 增加当前阶段的计数器。 - } else { - stage_max--; // 减少最大次数。 - } + } 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) = orig; - *(u16*)(out_buf + i) = SWAP16(interesting_16[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; -// 更新新的命中次数。 -new_hit_cnt = queued_paths + unique_crashes; + /* 32-bit arithmetics, both endians. */ -// 记录在当前阶段发现的新问题数量和周期计数。 -stage_finds[STAGE_INTEREST16] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_INTEREST16] += stage_max; + if (len < 4) goto skip_arith; -// 如果输入数据长度小于4,则跳过32位整数的处理。 -if (len < 4) goto skip_interest; + stage_name = "arith 32/8"; + stage_short = "arith32"; + stage_cur = 0; + stage_max = 4 * (len - 3) * ARITH_MAX; -// 设置当前阶段的名称和简称。 -stage_name = "interest 32/8"; -stage_short = "int32"; -stage_cur = 0; // 初始化当前阶段的计数器。 -stage_max = 2 * (len - 3) * (sizeof(interesting_32) >> 2); // 最大次数为输入数据长度减3与有趣32位整数数组大小的乘积。 + orig_hit_cnt = new_hit_cnt; -orig_hit_cnt = new_hit_cnt; // 记录原始的命中次数。 + for (i = 0; i < len - 3; i++) { -// 设置32位整数,包括小端和大端。 -for (i = 0; i < len - 3; i++) { - u32 orig = *(u32*)(out_buf + i); // 保存原始的32位整数。 + 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 -= sizeof(interesting_32) >> 1; - continue; + stage_max -= 4 * ARITH_MAX; + continue; } - stage_cur_byte = i; // 设置当前处理的字节位置。 + stage_cur_byte = i; - // 遍历有趣的32位整数数组。 - for (j = 0; j < sizeof(interesting_32) / 4; j++) { - stage_cur_val = interesting_32[j]; // 设置当前阶段的值。 + for (j = 1; j <= ARITH_MAX; j++) { - // 如果该值不可能是位翻转、算术操作或词有趣值插入的结果,则进行处理。 - 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 r1 = orig ^ (orig + j), + r2 = orig ^ (orig - j), + r3 = orig ^ SWAP32(SWAP32(orig) + j), + r4 = orig ^ SWAP32(SWAP32(orig) - j); - *(u32*)(out_buf + i) = interesting_32[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. */ - // 执行模糊测试操作,如果需要则放弃当前输入。 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; - stage_cur++; // 增加当前阶段的计数器。 - } else stage_max--; // 减少最大次数。 + stage_val_type = STAGE_VAL_LE; - // 对于大端情况也进行相同的处理。 - 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)) { + if ((orig & 0xffff) + j > 0xffff && !could_be_bitflip(r1)) { + + stage_cur_val = j; + *(u32*)(out_buf + i) = orig + j; -// 设置阶段值类型为大端。 - 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--; - } -// 恢复原始的32位整数值。 -*(u32*)(out_buf + i) = orig; + if ((orig & 0xffff) < j && !could_be_bitflip(r2)) { + + stage_cur_val = -j; + *(u32*)(out_buf + i) = orig - j; -// 完成对当前输入数据的所有有趣值设置后,更新新的命中次数。 -new_hit_cnt = queued_paths + unique_crashes; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + stage_cur++; -// 记录在当前阶段发现的新问题数量和周期计数。 -stage_finds[STAGE_INTEREST32] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_INTEREST32] += stage_max; + } else stage_max--; -// 如果没有用户定义的额外数据,则跳过此段代码。 -skip_interest: + /* Big endian next. */ -/******************** - * DICTIONARY STUFF * - ********************/ -if (!extras_cnt) goto skip_user_extras; + stage_val_type = STAGE_VAL_BE; -// 使用用户提供的额外数据进行覆盖操作。 -stage_name = "user extras (over)"; -stage_short = "ext_UO"; -stage_cur = 0; // 初始化当前阶段的计数器。 -stage_max = extras_cnt * len; // 最大次数为额外数据的数量乘以输入数据的长度。 + if ((SWAP32(orig) & 0xffff) + j > 0xffff && !could_be_bitflip(r3)) { -stage_val_type = STAGE_VAL_NONE; // 设置阶段值类型为无。 + stage_cur_val = j; + *(u32*)(out_buf + i) = SWAP32(SWAP32(orig) + j); -orig_hit_cnt = new_hit_cnt; // 记录原始的命中次数。 + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + stage_cur++; -// 对输入数据的每个字节位置进行操作。 -for (i = 0; i < len; i++) { - u32 last_len = 0; - stage_cur_byte = i; // 设置当前处理的字节位置。 + } else stage_max--; - // 遍历每个额外数据。 - for (j = 0; j < extras_cnt; j++) { - // 如果额外数据的数量大于最大确定性额外数据,或者没有足够的空间插入数据, - // 或者该数据已经存在于输出缓冲区中,或者有效性映射显示该位置不适合插入,则跳过此额外数据。 - 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; - } + 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++; - last_len = extras[j].len; - memcpy(out_buf + i, extras[j].data, last_len); // 将额外数据复制到输出缓冲区的当前位置。 + } else stage_max--; - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; // 对修改后的输出缓冲区执行通用模糊测试操作。 + *(u32*)(out_buf + i) = orig; - stage_cur++; // 增加当前阶段的计数器。 } - // 恢复所有被覆盖的内存。 - memcpy(out_buf + i, in_buf + i, last_len); -} + } + + new_hit_cnt = queued_paths + unique_crashes; -// 更新新的命中次数。 -new_hit_cnt = queued_paths + unique_crashes; + stage_finds[STAGE_ARITH32] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_ARITH32] += stage_max; -// 记录在当前阶段发现的新问题数量和周期计数。 -stage_finds[STAGE_EXTRAS_UO] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_EXTRAS_UO] += stage_max; +skip_arith: -// 用户提供的额外数据进行插入操作。 -stage_name = "user extras (insert)"; -stage_short = "ext_UI"; -stage_cur = 0; // 初始化当前阶段的计数器。 -stage_max = extras_cnt * (len + 1); // 最大次数为额外数据的数量乘以(输入数据的长度 + 1)。 + /********************** + * INTERESTING VALUES * + **********************/ -orig_hit_cnt = new_hit_cnt; // 记录原始的命中次数。 + stage_name = "interest 8/8"; + stage_short = "int8"; + stage_cur = 0; + stage_max = len * sizeof(interesting_8); -// 分配临时缓冲区,大小为输入数据的长度加上最大字典文件的大小。 -ex_tmp = ck_alloc(len + MAX_DICT_FILE); + stage_val_type = STAGE_VAL_LE; -// 遍历输入数据的每个位置以及末尾。 -for (i = 0; i <= len; i++) { - stage_cur_byte = i; // 设置当前处理的字节位置。 + orig_hit_cnt = new_hit_cnt; - // 遍历每个额外数据。 - for (j = 0; j < extras_cnt; j++) { - if (len + extras[j].len > MAX_FILE) { - stage_max--; - continue; - } + /* Setting 8-bit integers. */ - // 插入额外数据。 - memcpy(ex_tmp + i, extras[j].data, extras[j].len); + for (i = 0; i < len; i++) { - // 复制原始数据的剩余部分。 - memcpy(ex_tmp + i + extras[j].len, out_buf + i, len - i); + u8 orig = out_buf[i]; - if (common_fuzz_stuff(argv, ex_tmp, len + extras[j].len)) { - ck_free(ex_tmp); - goto abandon_entry; - } + /* Let's consult the effector map... */ - stage_cur++; // 增加当前阶段的计数器。 + if (!eff_map[EFF_APOS(i)]) { + stage_max -= sizeof(interesting_8); + continue; } - // 复制原始数据的当前部分。 - ex_tmp[i] = out_buf[i]; -} + stage_cur_byte = i; -// 释放临时缓冲区。 -ck_free(ex_tmp); + for (j = 0; j < sizeof(interesting_8); j++) { -// 更新新的命中次数。 -new_hit_cnt = queued_paths + unique_crashes; + /* Skip if the value could be a product of bitflips or arithmetics. */ -// 记录在当前阶段发现的新问题数量和周期计数。 -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; + if (could_be_bitflip(orig ^ (u8)interesting_8[j]) || + could_be_arith(orig, (u8)interesting_8[j], 1)) { + stage_max--; + continue; + } -// 设置当前阶段的名称和简短名称。 -stage_name = "auto extras (over)"; -stage_short = "ext_AO"; -// 初始化当前阶段的计数器和最大值,最大值是额外数据的数量乘以输入数据的长度。 -stage_cur = 0; -stage_max = MIN(a_extras_cnt, USE_AUTO_EXTRAS) * len; + stage_cur_val = interesting_8[j]; + out_buf[i] = interesting_8[j]; -// 设置阶段值类型为无。 -stage_val_type = STAGE_VAL_NONE; + if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; -// 记录原始的命中次数。 -orig_hit_cnt = new_hit_cnt; + out_buf[i] = orig; + stage_cur++; -// 遍历输入数据的每个字节位置。 -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++) { - // 如果额外数据的大小超过了剩余长度,或者该数据已经存在于输出缓冲区中, - // 或者有效性映射显示该位置不适合插入,则跳过此额外数据。 - 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; - } + new_hit_cnt = queued_paths + unique_crashes; - // 记录额外数据的长度,并将其复制到输出缓冲区的当前位置。 - last_len = a_extras[j].len; - memcpy(out_buf + i, a_extras[j].data, last_len); + stage_finds[STAGE_INTEREST8] += new_hit_cnt - orig_hit_cnt; + stage_cycles[STAGE_INTEREST8] += stage_max; - // 对修改后的输出缓冲区执行通用模糊测试操作,如果需要则放弃当前输入。 - if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; + /* Setting 16-bit integers, both endians. */ - // 增加当前阶段的计数器。 - stage_cur++; - } + if (no_arith || len < 2) goto skip_interest; - // 恢复所有被覆盖的内存。 - memcpy(out_buf + i, in_buf + i, last_len); -} + stage_name = "interest 16/8"; + stage_short = "int16"; + stage_cur = 0; + stage_max = 2 * (len - 1) * (sizeof(interesting_16) >> 1); -// 更新新的命中次数。 -new_hit_cnt = queued_paths + unique_crashes; + orig_hit_cnt = new_hit_cnt; -// 记录在当前阶段发现的新问题数量和周期计数。 -stage_finds[STAGE_EXTRAS_AO] += new_hit_cnt - orig_hit_cnt; -stage_cycles[STAGE_EXTRAS_AO] += stage_max; + for (i = 0; i < len - 1; i++) { -// 跳过额外数据的处理。 -skip_extras: + u16 orig = *(u16*)(out_buf + i); -// 如果我们到达这里而没有跳转到havoc_stage或abandon_entry, -// 则我们已经完成了确定性的步骤,并可以在.state目录中标记为完成。 -if (!queue_cur->passed_det) mark_as_det_done(queue_cur); + /* Let's consult the effector map... */ -//**************** -//* RANDOM HAVOC * -//**************** + if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) { + stage_max -= sizeof(interesting_16); + continue; + } -// 到达随机混沌(havoc)阶段。 -havoc_stage: -stage_cur_byte = -1; + stage_cur_byte = i; -// 如果当前是拼接周期,则生成不同的描述。 -if (!splice_cycle) { - // 设置混沌阶段的名称和简短名称。 - stage_name = "havoc"; - stage_short = "havoc"; - // 计算混沌阶段的最大运行次数,基于性能得分和一些常数。 - stage_max = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * + for (j = 0; j < sizeof(interesting_16) / 2; j++) { + + 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)) { + + stage_val_type = STAGE_VAL_LE; + + *(u16*)(out_buf + i) = 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]); + 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"; + 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)]) { + 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 * + ********************/ + + if (!extras_cnt) goto skip_user_extras; + + /* Overwrite with user-supplied extras. */ + + stage_name = "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++) { + + /* 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. */ + + 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); + + 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)"; + stage_short = "ext_UI"; + stage_cur = 0; + stage_max = extras_cnt * (len + 1); + + 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) { + stage_max--; + continue; + } + + /* Insert token */ + memcpy(ex_tmp + i, extras[j].data, extras[j].len); + + /* Copy tail */ + memcpy(ex_tmp + i + extras[j].len, out_buf + i, len - i); + + 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)"; + 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); + + 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); + + /**************** + * 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) { + + stage_name = "havoc"; + stage_short = "havoc"; + stage_max = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * perf_score / havoc_div / 100; -} else { - // 如果是拼接周期,设置特定的名称和最大运行次数。 + + } else { + 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; + if (stage_max < HAVOC_MIN) stage_max = HAVOC_MIN; -// 记录原始的命中次数。 -orig_hit_cnt = queued_paths + unique_crashes; + temp_len = len; -// 记录当前队列中的路径数量。 -havoc_queued = queued_paths; + 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++) { -/* - 我们基本上执行几千次运行(取决于perf_score),在这些运行中我们获取输入文件并进行随机的叠加修改。 -*/ -for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { - // 随机选择一个叠加因子,范围从1到2^(HAVOC_STACK_POW2)。 u32 use_stacking = 1 << (1 + UR(HAVOC_STACK_POW2)); - // 将当前阶段的值设置为所选的叠加因子。 stage_cur_val = use_stacking; - - // 根据叠加因子重复多次修改操作。 + for (i = 0; i < use_stacking; i++) { - // 随机选择一个操作,如果存在额外的数据,则选项会更多。 - switch (UR(15 + ((extras_cnt + a_extras_cnt) ? 2 : 0))) { - /* - 在某个位置翻转单个位。这是一项基本的位操作,可以用来测试程序对位错误的敏感性。 - */ - case 0: - FLIP_BIT(out_buf, UR(temp_len << 3)); - break; - - /* - 将某个字节设置为一个有趣的值。这些值通常是那些在程序逻辑中具有特殊意义的值,比如0, 1, -1等。 - */ - case 1: - out_buf[UR(temp_len)] = interesting_8[UR(sizeof(interesting_8))]; - break; - - /* - 将某个字(word)设置为一个有趣的值,并随机选择字节序。 - 字节序的选择可以帮助测试程序对不同字节序的处理能力。 - */ - case 2: - 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; - - /* - 将某个双字(dword)设置为一个有趣的值,并随机选择字节序。 - 类似于字操作,但是针对更大的数据单元。 - */ - case 3: - 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[UR(temp_len)] -= 1 + UR(ARITH_MAX); - break; - - /* - 向某个字节随机添加一个值。 - */ - case 5: - out_buf[UR(temp_len)] += 1 + UR(ARITH_MAX); - break; - - /* - 从一个词(word)随机减去一个值,并随机选择字节序。 - */ - case 6: - 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; - - /* - 向一个词(word)随机添加一个值,并随机选择字节序。 - */ - case 7: - 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; - - /* - 从一个双字(dword)随机减去一个值,并随机选择字节序。 - */ - case 8: - 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: 开始处理第9种情况,即随机增加一个双字节(dword),可以是随机字节序。 -case 9: - /* 如果临时数据长度小于4字节,则无法进行操作,因此跳出。 */ - if (temp_len < 4) break; - - // 以50%的概率选择是增加一个双字节还是进行字节序交换后增加。 - if (UR(2)) { - u32 pos = UR(temp_len - 3); - // 直接在指定位置增加一个随机值,范围从1到ARITH_MAX。 - *(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: 开始处理第10种情况,即随机设置一个字节为随机值。 -case 10: - /* 随机选择一个字节,然后使用XOR操作将其设置为1到255之间的随机值,以避免无操作(no-op)。 */ - out_buf[UR(temp_len)] ^= 1 + UR(255); - break; + switch (UR(15 + ((extras_cnt + a_extras_cnt) ? 2 : 0))) { -// case 11和case 12: 开始处理删除字节的操作。 -case 11 ... 12: { - /* 删除字节。我们使得这个操作比插入(下一个选项)更有可能,以希望保持文件的合理大小。 */ - u32 del_from, del_len; + case 0: - // 如果临时数据长度小于2字节,则无法进行删除操作,因此跳出。 - if (temp_len < 2) break; + /* Flip a single bit somewhere. Spooky! */ - /* 不要删除太多数据。 */ - del_len = choose_block_len(temp_len - 1); - del_from = UR(temp_len - del_len + 1); + FLIP_BIT(out_buf, UR(temp_len << 3)); + break; - // 将删除位置之后的数据向前移动。 - memmove(out_buf + del_from, out_buf + del_from + del_len, - temp_len - del_from - del_len); + case 1: - temp_len -= del_len; // 更新临时数据长度。 - break; -} + /* Set byte to interesting value. */ -// case 13: 开始处理插入或克隆字节的操作。 -case 13: - if (temp_len + HAVOC_BLK_XL < MAX_FILE) { - /* 克隆字节(75%概率)或插入一个常数块(25%概率)。 */ - u8 actually_clone = UR(4); - u32 clone_from, clone_to, clone_len; - u8* new_buf; - - if (actually_clone) { - clone_len = choose_block_len(temp_len); - clone_from = UR(temp_len - clone_len + 1); - } else { - clone_len = choose_block_len(HAVOC_BLK_XL); - clone_from = 0; - } + out_buf[UR(temp_len)] = interesting_8[UR(sizeof(interesting_8))]; + break; - clone_to = UR(temp_len); + case 2: - new_buf = ck_alloc_nozero(temp_len + clone_len); + /* Set word to interesting value, randomly choosing endian. */ - /* 头部 */ - memcpy(new_buf, out_buf, clone_to); + if (temp_len < 2) break; - /* 插入部分 */ - 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); + if (UR(2)) { - /* 尾部 */ - memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, - temp_len - clone_to); + *(u16*)(out_buf + UR(temp_len - 1)) = + interesting_16[UR(sizeof(interesting_16) >> 1)]; - ck_free(out_buf); - out_buf = new_buf; - temp_len += clone_len; // 更新临时数据长度。 - } - break; + } else { -// case 14: 开始处理用随机选择的数据块或固定数据覆盖字节的操作。 -case 14: { - u32 copy_from, copy_to, copy_len; + *(u16*)(out_buf + UR(temp_len - 1)) = SWAP16( + interesting_16[UR(sizeof(interesting_16) >> 1)]); - if (temp_len < 2) break; + } - copy_len = choose_block_len(temp_len - 1); + break; - copy_from = UR(temp_len - copy_len + 1); - copy_to = UR(temp_len - copy_len + 1); + case 3: - if (UR(4)) { - 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; -} + /* Set dword to interesting value, randomly choosing endian. */ -/* 只有当字典中有额外数据时,才能选择值15和16。 */ + if (temp_len < 4) break; -// case 15: 开始处理用额外数据覆盖字节的操作。 -case 15: { - if (!extras_cnt || (a_extras_cnt && UR(2))) { - /* 没有用户指定的额外数据,或者随机数倾向于使用自动检测到的数据。 */ - u32 use_extra = UR(a_extras_cnt); - u32 extra_len = a_extras[use_extra].len; - u32 insert_at; + if (UR(2)) { + + *(u32*)(out_buf + UR(temp_len - 3)) = + interesting_32[UR(sizeof(interesting_32) >> 2)]; - if (extra_len > temp_len) break; + } else { - insert_at = UR(temp_len - extra_len + 1); - memcpy(out_buf + insert_at, a_extras[use_extra].data, extra_len); - } else { - /* 没有自动检测到的额外数据,或者随机数倾向于使用字典中的数据。 */ - u32 use_extra = UR(extras_cnt); - u32 extra_len = extras[use_extra].len; - u32 insert_at; + *(u32*)(out_buf + UR(temp_len - 3)) = SWAP32( + interesting_32[UR(sizeof(interesting_32) >> 2)]); - 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; -} + break; -// case 16: 开始处理插入额外数据的操作。 -case 16: { - u32 use_extra, extra_len, insert_at = UR(temp_len + 1); - u8* new_buf; + case 4: - if (!extras_cnt || (a_extras_cnt && UR(2))) { - use_extra = UR(a_extras_cnt); - extra_len = a_extras[use_extra].len; + /* Randomly subtract from byte. */ - if (temp_len + extra_len >= MAX_FILE) break; + out_buf[UR(temp_len)] -= 1 + UR(ARITH_MAX); + break; - new_buf = ck_alloc_nozero(temp_len + extra_len); + case 5: - /* 头部 */ - memcpy(new_buf, out_buf, insert_at); + /* Randomly add to byte. */ - /* 插入部分 */ - memcpy(new_buf + insert_at, a_extras[use_extra].data, extra_len); - } else { - use_extra = UR(extras_cnt); - extra_len = extras[use_extra].len; + out_buf[UR(temp_len)] += 1 + UR(ARITH_MAX); + break; - if (temp_len + extra_len >= MAX_FILE) break; + case 6: - new_buf = ck_alloc_nozero(temp_len + extra_len); + /* Randomly subtract from word, random endian. */ - /* 头部 */ - memcpy(new_buf, out_buf, insert_at); + if (temp_len < 2) break; - /* 插入部分 */ - memcpy(new_buf + insert_at, extras[use_extra].data, extra_len); - } + if (UR(2)) { - /* 尾部 */ - memcpy(new_buf + insert_at + extra_len, out_buf + insert_at, - temp_len - insert_at); + 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: + + /* 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: + + /* 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: + + /* 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: + + /* 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) { + + /* 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) { + + clone_len = choose_block_len(temp_len); + clone_from = UR(temp_len - clone_len + 1); + + } else { + + 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)) { + + 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: { + + /* 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: { + + 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; + + } - ck_free(out_buf); - out_buf = new_buf; - temp_len += extra_len; // 更新临时数据长度。 - break; - } } + } - } -} -// 检查通用模糊测试函数是否指示我们应该放弃当前的输入。 -// 如果common_fuzz_stuff函数返回真(非零值),则跳转到标签abandon_entry。 -if (common_fuzz_stuff(argv, out_buf, temp_len)) - goto abandon_entry; - -// 如果out_buf在之前的处理中被破坏了,我们需要将其恢复到原始大小和形状。 -// 如果临时长度temp_len小于原始长度len,我们需要重新分配out_buf的大小为len。 -if (temp_len < len) - out_buf = ck_realloc(out_buf, len); -// 将temp_len设置回原始长度len。 -temp_len = len; -// 将原始输入数据in_buf复制回out_buf,以恢复其原始内容。 -memcpy(out_buf, in_buf, len); - -// 如果我们发现了新的问题或崩溃,我们应该在限制范围内继续运行更长时间。 -if (queued_paths != havoc_queued) { - // 如果性能得分perf_score小于或等于最大乘数HAVOC_MAX_MULT乘以100, - // 我们将当前阶段的最大尝试次数stage_max翻倍,并将性能得分perf_score翻倍。 - if (perf_score <= HAVOC_MAX_MULT * 100) { + + 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); + + /* 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; + } - // 更新havoc_queued为当前的queued_paths值。 - havoc_queued = queued_paths; -} -// 计算新的发现数量,包括新加入队列的路径数queued_paths和独特的崩溃数unique_crashes。 -new_hit_cnt = queued_paths + unique_crashes; + } + + new_hit_cnt = queued_paths + unique_crashes; -// 如果当前不是拼接周期splice_cycle,则更新HAVOC阶段的发现和周期计数。 -if (!splice_cycle) { + if (!splice_cycle) { stage_finds[STAGE_HAVOC] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_HAVOC] += stage_max; -} else { - // 如果当前是拼接周期,则更新SPLICE阶段的发现和周期计数。 + } else { stage_finds[STAGE_SPLICE] += new_hit_cnt - orig_hit_cnt; stage_cycles[STAGE_SPLICE] += stage_max; -} + } -// 如果没有定义IGNORE_FINDS宏,则执行以下代码。 #ifndef IGNORE_FINDS /************ * SPLICING * ************/ - /* 这是一种最后的手段策略,当一轮完整的测试没有发现任何问题时触发。 - 它获取当前的输入文件,随机选择另一个输入,并在某个偏移量处将它们拼接在一起, - 然后依赖havoc代码来变异这个新拼接的数据块。*/ - + /* 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: -// 如果启用了拼接,并且拼接周期小于最大拼接周期数,并且队列中有多个测试用例,且当前测试用例长度大于1,则尝试拼接操作。 -if (use_splicing && splice_cycle++ < SPLICE_CYCLES && - queued_paths > 1 && queue_cur->len > 1) { + if (use_splicing && splice_cycle++ < SPLICE_CYCLES && + queued_paths > 1 && queue_cur->len > 1) { - struct queue_entry* target; // 指向目标队列条目的指针 - u32 tid, split_at; // 目标ID和分割点 - u8* new_buf; // 新的缓冲区 - s32 f_diff, l_diff; // 第一个和最后一个不同字节的位置 + struct queue_entry* target; + u32 tid, split_at; + u8* new_buf; + s32 f_diff, l_diff; - /* 首先,如果我们对in_buf进行了havoc操作的修改,我们需要清理它... */ + /* First of all, if we've modified in_buf for havoc, let's clean that + up... */ - // 如果in_buf不是原始输入缓冲区,释放它并将in_buf重置为原始输入缓冲区 - if (in_buf != orig_in) { - ck_free(in_buf); - in_buf = orig_in; - len = queue_cur->len; - } + if (in_buf != orig_in) { + 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. */ - // 随机选择一个目标ID,确保它不是当前条目 - do { tid = UR(queued_paths); } while (tid == current_entry); + do { tid = UR(queued_paths); } while (tid == current_entry); - splicing_with = tid; // 记录当前拼接的目标ID - target = queue; // 初始化目标指向队列头部 + splicing_with = tid; + target = queue; - // 定位到目标队列条目 - while (tid >= 100) { target = target->next_100; tid -= 100; } - while (tid--) target = target->next; + while (tid >= 100) { target = target->next_100; tid -= 100; } + while (tid--) target = target->next; - /* 确保目标有合理的长度。 */ + /* Make sure that the target has a reasonable length. */ - // 确保目标条目长度足够,并且不是当前条目 - while (target && (target->len < 2 || target == queue_cur)) { - target = target->next; - splicing_with++; - } + while (target && (target->len < 2 || target == queue_cur)) { + target = target->next; + splicing_with++; + } - // 如果没有合适的目标,重试拼接 - if (!target) goto retry_splicing; + if (!target) goto retry_splicing; - /* 将测试用例读入新缓冲区。 */ + /* Read the testcase into a new buffer. */ - // 打开目标文件 - fd = open(target->fname, O_RDONLY); + fd = open(target->fname, O_RDONLY); - // 如果打开失败,输出错误信息并退出 - if (fd < 0) PFATAL("Unable to open '%s'", target->fname); + if (fd < 0) PFATAL("Unable to open '%s'", target->fname); - // 分配新缓冲区 - new_buf = ck_alloc_nozero(target->len); + new_buf = ck_alloc_nozero(target->len); - // 读取目标文件内容到新缓冲区 - ck_read(fd, new_buf, target->len, target->fname); + ck_read(fd, new_buf, target->len, target->fname); - // 关闭文件描述符 - close(fd); + 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); + 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) { - ck_free(new_buf); - goto retry_splicing; - } + if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { + 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); + split_at = f_diff + UR(l_diff - f_diff); - /* 执行拼接操作。 */ + /* Do the thing. */ - // 更新长度为目标长度 - len = target->len; - // 将分割点之前的数据复制到新缓冲区 - memcpy(new_buf, in_buf, split_at); - // 更新输入缓冲区为新缓冲区 - in_buf = new_buf; + 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); + ck_free(out_buf); + out_buf = ck_alloc_nozero(len); + memcpy(out_buf, in_buf, len); - // 跳转到havoc阶段 - goto havoc_stage; + goto havoc_stage; -} + } #endif /* !IGNORE_FINDS */ -// 设置返回值为0 -ret_val = 0; + ret_val = 0; -// 放弃当前条目 abandon_entry: -// 重置拼接目标ID -splicing_with = -1; + splicing_with = -1; - /* 如果我们通过了校准周期并且之前没有见过这个条目,更新待处理未测试计数。 */ + /* Update pending_not_fuzzed count if we made it through the calibration + cycle and have not seen this entry before. */ - // 如果没有停止信号,当前条目没有校准失败,且之前未被测试过 if (!stop_soon && !queue_cur->cal_failed && !queue_cur->was_fuzzed) { - // 标记当前条目为已测试 queue_cur->was_fuzzed = 1; - // 减少待处理未测试计数 pending_not_fuzzed--; - // 如果当前条目是优选的,减少优选计数 if (queue_cur->favored) pending_favored--; } - // 取消映射原始输入缓冲区 munmap(orig_in, queue_cur->len); - // 如果in_buf不是原始输入缓冲区,释放它 if (in_buf != orig_in) ck_free(in_buf); - // 释放输出缓冲区 ck_free(out_buf); - // 释放效果映射缓冲区 ck_free(eff_map); - // 返回结果 return ret_val; #undef FLIP_BIT } -/* 从其他模糊测试器中获取有趣的测试用例。 */ -// 这个函数用于在分布式模糊测试环境中,从其他模糊测试器中同步测试用例。 + +/* Grab interesting test cases from other fuzzers. */ + static void sync_fuzzers(char** argv) { - DIR* sd; // 指向同步目录的目录流 - struct dirent* sd_ent; // 目录流中的当前条目 - u32 sync_cnt = 0; // 同步的模糊测试器数量 + DIR* sd; + struct dirent* sd_ent; + u32 sync_cnt = 0; - // 打开同步目录 sd = opendir(sync_dir); if (!sd) PFATAL("Unable to open '%s'", sync_dir); - // 重置阶段最大值和当前值,以及当前深度 stage_max = stage_cur = 0; cur_depth = 0; - /* 查看同步目录中为每个其他模糊测试器创建的条目。 */ + /* Look at the entries created for every other fuzzer in the sync directory. */ - // 遍历同步目录中的每个条目 while ((sd_ent = readdir(sd))) { - static u8 stage_tmp[128]; // 临时阶段名称 + static u8 stage_tmp[128]; - DIR* qd; // 指向队列目录的目录流 - struct dirent* qd_ent; // 队列目录中的当前条目 - u8 *qd_path, *qd_synced_path; // 队列目录和同步目录的路径 - u32 min_accept = 0, next_min_accept; // 最小接受的测试用例ID和下一个最小接受的测试用例ID + DIR* qd; + struct dirent* qd_ent; + u8 *qd_path, *qd_synced_path; + u32 min_accept = 0, next_min_accept; - s32 id_fd; // 用于存储最后看到的测试用例ID的文件的文件描述符 + s32 id_fd; - /* 跳过隐藏文件和我们自己的输出目录。 */ + /* Skip dot files and our own output directory. */ - // 如果条目是隐藏文件或与我们自己的同步ID相同,则跳过 if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue; - /* 跳过任何没有queue/子目录的东西。 */ + /* Skip anything that doesn't have a queue/ subdirectory. */ - // 构造队列目录的路径 qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name); - // 打开队列目录 if (!(qd = opendir(qd_path))) { ck_free(qd_path); continue; } - /* 检索最后看到的测试用例的ID。 */ + /* Retrieve the ID of the last seen test case. */ - // 构造同步目录中用于存储最后看到的测试用例ID的文件的路径 qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name); - // 打开或创建用于存储最后看到的测试用例ID的文件 id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600); - // 如果打开文件失败,则输出错误信息并退出 if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path); - // 如果文件中已经有数据,则读取最小接受的测试用例ID if (read(id_fd, &min_accept, sizeof(u32)) > 0) lseek(id_fd, 0, SEEK_SET); - // 更新下一个最小接受的测试用例ID next_min_accept = min_accept; - /* 显示统计信息 */ + /* Show stats */ - // 设置阶段名称和当前阶段值 sprintf(stage_tmp, "sync %u", ++sync_cnt); stage_name = stage_tmp; stage_cur = 0; stage_max = 0; - /* 对于这个模糊测试器排队的每个文件,解析ID并查看我们是否之前已经看过它; - 如果没有,执行测试用例。 */ + /* For every file queued by this fuzzer, parse ID and see if we have looked at + it before; exec a test case if not. */ - // 遍历队列目录中的每个条目 while ((qd_ent = readdir(qd))) { - u8* path; // 文件的路径 - s32 fd; // 文件描述符 - struct stat st; // 文件状态 + u8* path; + s32 fd; + struct stat st; - // 如果条目是隐藏文件或测试用例ID小于最小接受的测试用例ID,则跳过 if (qd_ent->d_name[0] == '.' || sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || syncing_case < min_accept) continue; - /* 好的,听起来像是一个新测试用例。让我们试试它。 */ + /* OK, sounds like a new one. Let's give it a try. */ - // 如果测试用例ID大于或等于下一个最小接受的测试用例ID if (syncing_case >= next_min_accept) next_min_accept = syncing_case + 1; - // 构造文件的路径 path = alloc_printf("%s/%s", qd_path, qd_ent->d_name); - /* 允许在其他模糊测试器正在恢复等情况下失败... */ + /* Allow this to fail in case the other fuzzer is resuming or so... */ - // 打开文件 fd = open(path, O_RDONLY); - // 如果打开文件失败,则释放路径内存并继续 if (fd < 0) { ck_free(path); continue; } - // 获取文件状态 if (fstat(fd, &st)) PFATAL("fstat() failed"); - /* 忽略大小为零或过大的文件。 */ + /* Ignore zero-sized or oversized files. */ - // 如果文件大小在允许范围内 if (st.st_size && st.st_size <= MAX_FILE) { - u8 fault; // 故障标志 - u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); // 将文件内容映射到内存 + u8 fault; + u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - // 如果映射失败,则输出错误信息并退出 if (mem == MAP_FAILED) PFATAL("Unable to mmap '%s'", path); - /* 看看会发生什么。我们依赖save_if_interesting()来捕获主要 - 错误并保存测试用例。 */ + /* See what happens. We rely on save_if_interesting() to catch major + errors and save the test case. */ - // 将测试用例写入测试用例缓冲区 write_to_testcase(mem, st.st_size); - // 运行目标程序并获取故障标志 fault = run_target(argv, exec_tmout); - // 如果收到停止信号,则返回 if (stop_soon) return; - // 设置当前正在同步的模糊测试器的名称 syncing_party = sd_ent->d_name; - // 将测试用例添加到队列中 queued_imported += save_if_interesting(argv, mem, st.st_size, fault); - // 重置当前正在同步的模糊测试器的名称 + syncing_party = 0; - // 取消内存映射并释放内存 munmap(mem, st.st_size); - // 如果需要,则显示统计信息 if (!(stage_cur++ % stats_update_freq)) show_stats(); } - // 释放路径内存并关闭文件描述符 ck_free(path); close(fd); } - // 将下一个最小接受的测试用例ID写入文件 ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); - // 关闭文件描述符 close(id_fd); - // 关闭队列目录流 closedir(qd); - // 释放队列目录路径内存 ck_free(qd_path); - // 释放同步目录路径内存 ck_free(qd_synced_path); } - // 关闭同步目录流 closedir(sd); } -/* 处理停止信号(Ctrl-C等)。 */ -// 这个函数用于处理停止信号,例如用户按下Ctrl-C。 +/* Handle stop signal (Ctrl-C, etc). */ + static void handle_stop_sig(int sig) { - // 设置停止标志 stop_soon = 1; - // 如果子进程存在,则杀死它 if (child_pid > 0) kill(child_pid, SIGKILL); - // 如果fork服务器进程存在,则杀死它 if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); } -/* 处理跳过请求(SIGUSR1)。 */ -// 这个函数用于处理跳过请求信号。 +/* Handle skip request (SIGUSR1). */ + static void handle_skipreq(int sig) { - // 设置跳过请求标志 skip_requested = 1; } -/* 处理超时(SIGALRM)。 */ +/* Handle timeout (SIGALRM). */ -// 这个函数用于处理超时信号。 static void handle_timeout(int sig) { - // 如果子进程存在,则标记它为超时并杀死它 if (child_pid > 0) { child_timed_out = 1; @@ -6796,7 +6859,6 @@ static void handle_timeout(int sig) { } else if (child_pid == -1 && forksrv_pid > 0) { - // 如果子进程不存在但fork服务器进程存在,则标记它为超时并杀死它 child_timed_out = 1; kill(forksrv_pid, SIGKILL); @@ -6805,170 +6867,217 @@ static void handle_timeout(int sig) { } -// 检查目标二进制文件是否存在、是否可执行等属性的函数 -void check_binary(u8* fname) { +/* Do a PATH search and find target binary to see that it exists and + isn't a shell script - a common and painful mistake. We also check for + a valid ELF header and for evidence of AFL instrumentation. */ - u8* env_path = 0; // 环境变量PATH - struct stat st; // 文件状态结构体 +EXP_ST void check_binary(u8* fname) { - s32 fd; // 文件描述符 - u8* f_data; // 文件数据 - u32 f_len = 0; // 文件长度 + u8* env_path = 0; + struct stat st; + + s32 fd; + u8* f_data; + u32 f_len = 0; - ACTF("Validating target binary..."); // 动作提示:验证目标二进制文件 + ACTF("Validating target binary..."); - // 如果文件名中包含路径分隔符'/'或者环境变量PATH未设置,则直接使用文件名 if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { - target_path = ck_strdup(fname); // 复制文件名 - // 检查文件是否存在、是否为普通文件、是否可执行、文件长度是否至少为4字节 + target_path = ck_strdup(fname); if (stat(target_path, &st) || !S_ISREG(st.st_mode) || !(st.st_mode & 0111) || (f_len = st.st_size) < 4) - FATAL("Program '%s' not found or not executable", fname); // 如果检查失败,输出错误信息并退出 + FATAL("Program '%s' not found or not executable", fname); } else { - // 如果环境变量PATH已设置,则遍历PATH中的每个目录 while (env_path) { - u8 *cur_elem, *delim = strchr(env_path, ':'); // 查找路径分隔符':' + u8 *cur_elem, *delim = strchr(env_path, ':'); if (delim) { - // 如果找到分隔符,则复制当前目录到新分配的内存 + cur_elem = ck_alloc(delim - env_path + 1); memcpy(cur_elem, env_path, delim - env_path); delim++; - } else cur_elem = ck_strdup(env_path); // 如果没有分隔符,复制剩余的路径 - env_path = delim; // 更新env_path指针 + } else cur_elem = ck_strdup(env_path); + + env_path = delim; if (cur_elem[0]) - target_path = alloc_printf("%s/%s", cur_elem, fname); // 构造完整的文件路径 + target_path = alloc_printf("%s/%s", cur_elem, fname); else - target_path = ck_strdup(fname); // 如果当前目录为空,则直接使用文件名 + target_path = ck_strdup(fname); - ck_free(cur_elem); // 释放临时内存 + ck_free(cur_elem); - // 如果找到文件并且文件属性符合要求,则跳出循环 if (!stat(target_path, &st) && S_ISREG(st.st_mode) && (st.st_mode & 0111) && (f_len = st.st_size) >= 4) break; - ck_free(target_path); // 释放之前分配的内存 - target_path = 0; // 重置target_path + ck_free(target_path); + target_path = 0; } - if (!target_path) FATAL("Program '%s' not found or not executable", fname); // 如果未找到文件,输出错误信息并退出 + if (!target_path) FATAL("Program '%s' not found or not executable", fname); } - // 如果环境变量AFL_SKIP_BIN_CHECK被设置,则跳过后续检查 if (getenv("AFL_SKIP_BIN_CHECK")) return; - /* 检查用户是否犯了一些明显的错误,比如将二进制文件放在/tmp或/var/tmp目录下 */ + /* Check for blatant user errors. */ + if ((!strncmp(target_path, "/tmp/", 5) && !strchr(target_path + 5, '/')) || (!strncmp(target_path, "/var/tmp/", 9) && !strchr(target_path + 9, '/'))) FATAL("Please don't keep binaries in /tmp or /var/tmp"); - // 打开目标文件 fd = open(target_path, O_RDONLY); - if (fd < 0) PFATAL("Unable to open '%s'", target_path); // 如果打开失败,输出错误信息 - // 将文件内容映射到内存 + if (fd < 0) PFATAL("Unable to open '%s'", target_path); + f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); - if (f_data == MAP_FAILED) PFATAL("Unable to mmap file '%s'", target_path); // 如果映射失败,输出错误信息 - close(fd); // 关闭文件描述符 + if (f_data == MAP_FAILED) PFATAL("Unable to mmap file '%s'", target_path); + + close(fd); - // 检查文件是否为脚本文件 if (f_data[0] == '#' && f_data[1] == '!') { - // 如果是脚本文件,输出错误信息并退出 - SAYF("\n" cLRD "[-] " cRST "Oops, the target binary looks like a shell script..."); + + SAYF("\n" cLRD "[-] " cRST + "Oops, the target binary looks like a shell script. Some build systems will\n" + " sometimes generate shell stubs for dynamically linked programs; try static\n" + " library mode (./configure --disable-shared) if that's the case.\n\n" + + " Another possible cause is that you are actually trying to use a shell\n" + " wrapper around the fuzzed component. Invoking shell can slow down the\n" + " fuzzing process by a factor of 20x or more; it's best to write the wrapper\n" + " in a compiled language instead.\n"); + FATAL("Program '%s' is a shell script", target_path); + } #ifndef __APPLE__ - // 检查文件是否为ELF格式 + if (f_data[0] != 0x7f || memcmp(f_data + 1, "ELF", 3)) FATAL("Program '%s' is not an ELF binary", target_path); + #else - // 在苹果系统上,检查文件是否为Mach-O格式 + if (f_data[0] != 0xCF || f_data[1] != 0xFA || f_data[2] != 0xED) FATAL("Program '%s' is not a 64-bit Mach-O binary", target_path); + #endif /* ^!__APPLE__ */ - // 如果没有使用QEMU模式且没有使用dumb模式,检查文件是否被AFL插桩 if (!qemu_mode && !dumb_mode && !memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { - // 如果没有插桩,输出错误信息并退出 - SAYF("\n" cLRD "[-] " cRST "Looks like the target binary is not instrumented!..."); + + SAYF("\n" cLRD "[-] " cRST + "Looks like the target binary is not instrumented! The fuzzer depends on\n" + " compile-time instrumentation to isolate interesting test cases while\n" + " mutating the input data. For more information, and for tips on how to\n" + " instrument binaries, please see %s/README.\n\n" + + " When source code is not available, you may be able to leverage QEMU\n" + " mode support. Consult the README for tips on how to enable this.\n" + + " (It is also possible to use afl-fuzz as a traditional, \"dumb\" fuzzer.\n" + " For that, you can use the -n option - but expect much worse results.)\n", + doc_path); + FATAL("No instrumentation detected"); + } if (qemu_mode && memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { - // 如果在QEMU模式下检测到插桩,输出错误信息并退出 - SAYF("\n" cLRD "[-] " cRST "This program appears to be instrumented with afl-gcc..."); + + SAYF("\n" cLRD "[-] " cRST + "This program appears to be instrumented with afl-gcc, but is being run in\n" + " QEMU mode (-Q). This is probably not what you want - this setup will be\n" + " slow and offer no practical benefits.\n"); + FATAL("Instrumentation found in -Q mode"); + } - // 检查文件是否使用了AddressSanitizer if (memmem(f_data, f_len, "libasan.so", 10) || memmem(f_data, f_len, "__msan_init", 11)) uses_asan = 1; - /* 检测二进制文件中的持久模式和延迟初始化签名 */ + /* Detect persistent & deferred init signatures in the binary. */ + if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { + OKF(cPIN "Persistent mode binary detected."); setenv(PERSIST_ENV_VAR, "1", 1); persistent_mode = 1; + } else if (getenv("AFL_PERSISTENT")) { + WARNF("AFL_PERSISTENT is no longer supported and may misbehave!"); + } if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { + OKF(cPIN "Deferred forkserver binary detected."); setenv(DEFER_ENV_VAR, "1", 1); deferred_mode = 1; + } else if (getenv("AFL_DEFER_FORKSRV")) { + WARNF("AFL_DEFER_FORKSRV is no longer supported and may misbehave!"); + } - if (munmap(f_data, f_len)) PFATAL("unmap() failed"); // 取消内存映射 + if (munmap(f_data, f_len)) PFATAL("unmap() failed"); } -// 修剪并可能为运行创建一个横幅 + +/* Trim and possibly create a banner for the run. */ + static void fix_up_banner(u8* name) { - // 如果没有设置横幅,则根据同步ID或文件名来设置 + if (!use_banner) { + if (sync_id) { + use_banner = sync_id; + } else { - u8* trim = strrchr(name, '/'); // 查找文件名中的路径分隔符 + + u8* trim = strrchr(name, '/'); if (!trim) use_banner = name; else use_banner = trim + 1; + } + } - // 如果横幅字符串过长,则截断它 if (strlen(use_banner) > 40) { + u8* tmp = ck_alloc(44); sprintf(tmp, "%.40s...", use_banner); use_banner = tmp; + } + } -// 检查是否在TTY上运行 + +/* Check if we're on TTY. */ + static void check_if_tty(void) { - struct winsize ws; // 窗口大小结构体 - // 如果设置了环境变量AFL_NO_UI,则禁用UI + struct winsize ws; + if (getenv("AFL_NO_UI")) { OKF("Disabling the UI because AFL_NO_UI is set."); not_on_tty = 1; return; } - // 如果无法获取窗口大小,则认为不在TTY上运行 if (ioctl(1, TIOCGWINSZ, &ws)) { if (errno == ENOTTY) { @@ -6982,30 +7091,27 @@ static void check_if_tty(void) { } -/* 在终端尺寸变化后检查终端尺寸。 */ +/* Check terminal dimensions after resize. */ -// 这个函数检查终端的尺寸,以确保它不是太小,从而无法适当地显示程序的输出。 static void check_term_size(void) { - struct winsize ws; // winsize结构体用于存储终端的尺寸信息 + struct winsize ws; - term_too_small = 0; // 假设终端不是太小 + term_too_small = 0; - // 使用ioctl系统调用来获取终端的尺寸信息 - if (ioctl(1, TIOCGWINSZ, &ws)) return; // 如果ioctl调用失败,则返回 + if (ioctl(1, TIOCGWINSZ, &ws)) return; - // 如果窗口尺寸的行数或列数为0,或者小于某个阈值,则认为终端太小 - if (ws.ws_row == 0 && ws.ws_col == 0) return; // 如果行数和列数都为0,则返回 - if (ws.ws_row < 25 || ws.ws_col < 80) term_too_small = 1; // 设置终端太小的标志 + if (ws.ws_row == 0 && ws.ws_col == 0) return; + if (ws.ws_row < 25 || ws.ws_col < 80) term_too_small = 1; } -/* 显示使用提示。 */ -// 这个函数在用户请求帮助或者使用了错误的命令行参数时显示程序的使用提示。 + +/* Display usage hints. */ + static void usage(u8* argv0) { - // 使用SAYF宏来格式化并输出使用提示信息 SAYF("\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" "Required parameters:\n\n" @@ -7018,8 +7124,8 @@ static void usage(u8* argv0) { " -f file - location read by the fuzzed program (stdin)\n" " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" " -m megs - memory limit for child process (%u MB)\n" - " -Q - use binary-only instrumentation (QEMU mode)\n\n" - + " -Q - use binary-only instrumentation (QEMU mode)\n\n" + "Fuzzing behavior settings:\n\n" " -d - quick & dirty mode (skips deterministic steps)\n" @@ -7036,9 +7142,8 @@ static void usage(u8* argv0) { "For additional tips, please consult %s/README.\n\n", - argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); // 使用宏替换标记来插入特定的值 + argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); - // 显示使用提示后退出程序 exit(1); } @@ -7046,135 +7151,158 @@ static void usage(u8* argv0) { /* Prepare output directories and fds. */ -/* 准备输出目录和文件描述符。 */ - EXP_ST void setup_dirs_fds(void) { - u8* tmp; // 临时字符串指针 - s32 fd; // 文件描述符 - ACTF("Setting up output directories..."); // 动作提示:设置输出目录 + u8* tmp; + s32 fd; + + ACTF("Setting up output directories..."); - // 如果设置了同步ID,尝试创建同步目录,如果失败且不是因为已存在,则输出错误信息并终止 if (sync_id && mkdir(sync_dir, 0700) && errno != EEXIST) PFATAL("Unable to create '%s'", sync_dir); - // 尝试创建输出目录,如果失败且不是因为已存在,则输出错误信息并终止 if (mkdir(out_dir, 0700)) { + if (errno != EEXIST) PFATAL("Unable to create '%s'", out_dir); - maybe_delete_out_dir(); // 可能删除已存在的输出目录 + maybe_delete_out_dir(); + } else { + if (in_place_resume) - FATAL("Resume attempted but old output directory not found"); // 如果尝试在地恢复,但未找到旧的输出目录,则终止 + FATAL("Resume attempted but old output directory not found"); - out_dir_fd = open(out_dir, O_RDONLY); // 打开输出目录 + out_dir_fd = open(out_dir, O_RDONLY); #ifndef __sun - // 如果无法锁定输出目录,则输出错误信息并终止 + if (out_dir_fd < 0 || flock(out_dir_fd, LOCK_EX | LOCK_NB)) PFATAL("Unable to flock() output directory."); + #endif /* !__sun */ + } - // 创建队列目录,用于存放起始和发现的路径 + /* Queue directory for any starting & discovered paths. */ + tmp = alloc_printf("%s/queue", out_dir); if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); ck_free(tmp); - // 创建队列元数据的顶级目录,用于会话恢复等任务 + /* Top-level directory for queue metadata used for session + resume and related tasks. */ + tmp = alloc_printf("%s/queue/.state/", out_dir); 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); - // 如果设置了同步ID,创建同步目录,用于跟踪合作的模糊测试器 + /* 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); - // 创建一般有用的文件描述符 - dev_null_fd = open("/dev/null", O_RDWR); // 打开null设备文件描述符 + /* 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); // 打开随机设备文件描述符 + dev_urandom_fd = open("/dev/urandom", O_RDONLY); if (dev_urandom_fd < 0) PFATAL("Unable to open /dev/urandom"); - // 创建Gnuplot输出文件 + /* Gnuplot output file. */ + tmp = alloc_printf("%s/plot_data", out_dir); - fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); // 打开或创建plot_data文件 + 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"); // 将文件描述符与FILE*关联 - if (!plot_file) PFATAL("fdopen() failed"); // 如果失败,则输出错误信息并终止 + plot_file = fdopen(fd, "w"); + if (!plot_file) PFATAL("fdopen() failed"); - // 写入Gnuplot输出文件的标题行 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 */ + } -/* 如果没有使用-f选项,则设置模糊测试数据的输出文件。 */ + +/* Setup the output file for fuzzed data, if not using -f. */ EXP_ST void setup_stdio_file(void) { - u8* fn = alloc_printf("%s/.cur_input", out_dir); // 构造当前输入文件的路径 - unlink(fn); // 删除已存在的当前输入文件,忽略错误 + u8* fn = alloc_printf("%s/.cur_input", out_dir); + + unlink(fn); /* Ignore errors */ - out_fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600); // 打开或创建当前输入文件 + out_fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600); - if (out_fd < 0) PFATAL("Unable to create '%s'", fn); // 如果失败,则输出错误信息并终止 + if (out_fd < 0) PFATAL("Unable to create '%s'", fn); + + ck_free(fn); - ck_free(fn); // 释放路径字符串 } -/* 确保核心转储(core dumps)不会发送到外部程序。 */ +/* Make sure that core dumps don't go to a program. */ static void check_crash_handling(void) { #ifdef __APPLE__ - /* 在Mac OS X上,似乎没有简单的C API可以查询已加载的守护进程状态,我也不愿意在没有测试环境的情况下 - 做一些更复杂的操作,比如通过Mach端口禁用崩溃报告。因此,目前我们用一种不太优雅的方式来检查崩溃报告。 */ - - // 尝试执行系统命令来检查是否有崩溃报告被配置为发送到外部程序 + /* Yuck! There appears to be no simple C API to query for the state of + loaded daemons on MacOS X, and I'm a bit hesitant to do something + more sophisticated, such as disabling crash reporting via Mach ports, + until I get a box to test the code. So, for now, we check for crash + reporting the awful way. */ + if (system("launchctl list 2>/dev/null | grep -q '\\.ReportCrash$'")) return; - // 如果系统配置了发送崩溃通知到外部程序,输出警告信息 SAYF("\n" cLRD "[-] " cRST "Whoops, your system is configured to forward crash notifications to an\n" " external crash reporting utility. This will cause issues due to the\n" @@ -7187,28 +7315,23 @@ static void check_crash_handling(void) { " launchctl unload -w ${SL}/LaunchAgents/${PL}.plist\n" " sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist\n"); - // 如果没有设置环境变量来忽略缺失的崩溃报告,则终止程序 if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) FATAL("Crash reporter detected"); #else - /* 这是Linux特定的代码,我不认为*BSD上有等效的设置,所以我们暂时可以忽略这个问题。 */ + /* This is Linux specific, but I don't think there's anything equivalent on + *BSD, so we can just let it slide for now. */ - // 打开核心转储模式文件 s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY); u8 fchar; - // 如果打开文件失败,则直接返回 if (fd < 0) return; - // 输出动作信息:正在检查核心转储模式 ACTF("Checking core_pattern..."); - // 读取核心转储模式文件的第一个字符 if (read(fd, &fchar, 1) == 1 && fchar == '|') { - // 如果第一个字符是管道符号(|),说明系统配置了将核心转储发送到外部程序 SAYF("\n" cLRD "[-] " cRST "Hmm, your system is configured to send core dump notifications to an\n" " external utility. This will cause issues: there will be an extended delay\n" @@ -7220,63 +7343,55 @@ static void check_crash_handling(void) { " echo core >/proc/sys/kernel/core_pattern\n"); - // 如果没有设置环境变量来忽略缺失的崩溃报告,则终止程序 if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) FATAL("Pipe at the beginning of 'core_pattern'"); + } - // 关闭核心转储模式文件 close(fd); #endif /* ^__APPLE__ */ + } -/* 检查CPU调速器(scaling governor)。 */ +/* Check CPU governor. */ static void check_cpu_governor(void) { - FILE* f; // 文件指针 - u8 tmp[128]; // 临时缓冲区 - u64 min = 0, max = 0; // 最小和最大CPU频率 + FILE* f; + u8 tmp[128]; + u64 min = 0, max = 0; - // 如果设置了环境变量AFL_SKIP_CPUFREQ,则跳过此检查 if (getenv("AFL_SKIP_CPUFREQ")) return; - // 尝试打开CPU调速器配置文件 f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "r"); - if (!f) return; // 如果打开失败,则跳过此检查 + if (!f) return; - ACTF("Checking CPU scaling governor..."); // 输出动作信息 + ACTF("Checking CPU scaling governor..."); - // 读取CPU调速器配置 - if (!fgets(tmp, 128, f)) PFATAL("fgets() failed"); // 如果读取失败,则输出错误信息并终止 + if (!fgets(tmp, 128, f)) PFATAL("fgets() failed"); - fclose(f); // 关闭文件 + fclose(f); - // 如果CPU调速器设置为performance,则不需要调整 if (!strncmp(tmp, "perf", 4)) return; - // 尝试打开最小CPU频率配置文件 f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", "r"); if (f) { - if (fscanf(f, "%llu", &min) != 1) min = 0; // 读取最小频率 + if (fscanf(f, "%llu", &min) != 1) min = 0; fclose(f); } - // 尝试打开最大CPU频率配置文件 f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "r"); if (f) { - if (fscanf(f, "%llu", &max) != 1) max = 0; // 读取最大频率 + if (fscanf(f, "%llu", &max) != 1) max = 0; fclose(f); } - // 如果最小和最大频率相同,则不需要调整 if (min == max) return; - // 输出警告信息,提示用户调整CPU调速器设置 SAYF("\n" cLRD "[-] " cRST "Whoops, your system uses on-demand CPU frequency scaling, adjusted\n" " between %llu and %llu MHz. Unfortunately, the scaling algorithm in the\n" @@ -7291,32 +7406,32 @@ static void check_cpu_governor(void) { " to make afl-fuzz skip this check - but expect some performance drop.\n", min / 1024, max / 1024); - FATAL("Suboptimal CPU scaling governor"); // 如果CPU调速器设置不合理,则终止程序 + FATAL("Suboptimal CPU scaling governor"); + } -/* 计算逻辑CPU核心数。 */ + +/* Count the number of logical CPU cores. */ static void get_core_count(void) { - u32 cur_runnable = 0; // 当前可运行的任务数 + u32 cur_runnable = 0; #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) - size_t s = sizeof(cpu_core_count); // 核心数的大小 + size_t s = sizeof(cpu_core_count); - /* 在*BSD系统上,我们可以使用sysctl来获取CPU数量。 */ + /* On *BSD systems, we can just use a sysctl to get the number of CPUs. */ #ifdef __APPLE__ - // 在Mac OS X上获取逻辑CPU核心数 if (sysctlbyname("hw.logicalcpu", &cpu_core_count, &s, NULL, 0) < 0) return; #else - int s_name[2] = { CTL_HW, HW_NCPU }; // sysctl的名称 + int s_name[2] = { CTL_HW, HW_NCPU }; - // 在其他*BSD系统上获取逻辑CPU核心数 if (sysctl(s_name, 2, &cpu_core_count, &s, NULL, 0) < 0) return; #endif /* ^__APPLE__ */ @@ -7325,53 +7440,48 @@ static void get_core_count(void) { #ifdef HAVE_AFFINITY - // 如果支持CPU亲和性,则使用sysconf获取逻辑CPU核心数 cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN); #else - FILE* f = fopen("/proc/stat", "r"); // 尝试打开/proc/stat文件 - u8 tmp[1024]; // 临时缓冲区 + FILE* f = fopen("/proc/stat", "r"); + u8 tmp[1024]; - if (!f) return; // 如果打开失败,则跳过此检查 + if (!f) return; - // 读取/proc/stat文件,计算逻辑CPU核心数 while (fgets(tmp, sizeof(tmp), f)) if (!strncmp(tmp, "cpu", 3) && isdigit(tmp[3])) cpu_core_count++; - fclose(f); // 关闭文件 + fclose(f); #endif /* ^HAVE_AFFINITY */ #endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */ - // 如果成功获取逻辑CPU核心数 if (cpu_core_count > 0) { - cur_runnable = (u32)get_runnable_processes(); // 获取当前可运行的任务数 + cur_runnable = (u32)get_runnable_processes(); #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) - // 添加当前进程,因为1分钟平均值尚未包括它 + /* Add ourselves, since the 1-minute average doesn't include that yet. */ + cur_runnable++; #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ - // 输出CPU核心数和当前可运行的任务数 OKF("You have %u CPU core%s and %u runnable tasks (utilization: %0.0f%%).", cpu_core_count, cpu_core_count > 1 ? "s" : "", cur_runnable, cur_runnable * 100.0 / cpu_core_count); if (cpu_core_count > 1) { - // 如果当前可运行的任务数超过CPU核心数的1.5倍,则输出警告信息 if (cur_runnable > cpu_core_count * 1.5) { WARNF("System under apparent load, performance may be spotty."); } else if (cur_runnable + 1 <= cpu_core_count) { - // 如果当前可运行的任务数加1小于或等于CPU核心数,则输出提示信息 OKF("Try parallel jobs - see %s/parallel_fuzzing.txt.", doc_path); } @@ -7380,30 +7490,32 @@ static void get_core_count(void) { } else { - cpu_core_count = 0; // 如果无法获取逻辑CPU核心数,则设置为0 - WARNF("Unable to figure out the number of CPU cores."); // 输出警告信息 + cpu_core_count = 0; + WARNF("Unable to figure out the number of CPU cores."); + } } -/* 验证并修正使用-S时的out_dir和sync_dir。 */ + +/* Validate and fix up out_dir and sync_dir when using -S. */ static void fix_up_sync(void) { - u8* x = sync_id; // 同步ID + u8* x = sync_id; if (dumb_mode) - FATAL("-S / -M and -n are mutually exclusive"); // 如果同时使用-S/-M和-n,则终止程序 + FATAL("-S / -M and -n are mutually exclusive"); if (skip_deterministic) { if (force_deterministic) - FATAL("use -S instead of -M -d"); // 如果同时使用-M -d和-S,则终止程序 + FATAL("use -S instead of -M -d"); else - FATAL("-S already implies -d"); // 如果使用-S,则隐含-d,不需要再次指定 + FATAL("-S already implies -d"); + } - // 检查同步ID是否只包含字母数字、下划线或破折号 while (*x) { if (!isalnum(*x) && *x != '_' && *x != '-') @@ -7413,237 +7525,236 @@ static void fix_up_sync(void) { } - // 如果同步ID太长,则终止程序 if (strlen(sync_id) > 32) FATAL("Fuzzer ID too long"); - // 构造同步目录路径 x = alloc_printf("%s/%s", out_dir, sync_id); - sync_dir = out_dir; // 设置同步目录 - out_dir = x; // 设置输出目录 + sync_dir = out_dir; + out_dir = x; if (!force_deterministic) { - skip_deterministic = 1; // 跳过确定性测试 - use_splicing = 1; // 使用拼接技术 + skip_deterministic = 1; + use_splicing = 1; } } -/* 处理屏幕大小变化(SIGWINCH)。 */ + +/* Handle screen resize (SIGWINCH). */ static void handle_resize(int sig) { - clear_screen = 1; // 设置清除屏幕的标志 + clear_screen = 1; } -/* 检查ASAN选项。 */ + +/* Check ASAN options. */ static void check_asan_opts(void) { - u8* x = getenv("ASAN_OPTIONS"); // 获取ASAN_OPTIONS环境变量 + u8* x = getenv("ASAN_OPTIONS"); if (x) { - // 如果ASAN_OPTIONS没有设置abort_on_error=1,则终止程序 if (!strstr(x, "abort_on_error=1")) FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); - // 如果ASAN_OPTIONS没有设置symbolize=0,则终止程序 if (!strstr(x, "symbolize=0")) FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } - x = getenv("MSAN_OPTIONS"); // 获取MSAN_OPTIONS环境 - + x = getenv("MSAN_OPTIONS"); if (x) { - // 如果MSAN_OPTIONS环境变量被设置了,检查是否包含了特定的exit_code值 + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(MSAN_ERROR) " - please fix!"); - // 检查MSAN_OPTIONS是否设置了symbolize=0,这通常用于防止asan_symbolize.py运行 if (!strstr(x, "symbolize=0")) FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + } + } -/* 检测命令行参数中的'@@'符号并替换为文件路径。 */ + +/* Detect @@ in args. */ EXP_ST void detect_file_args(char** argv) { - u32 i = 0; // 初始化索引变量 - u8* cwd = getcwd(NULL, 0); // 获取当前工作目录 - // 如果无法获取当前工作目录,输出错误信息并终止程序 + u32 i = 0; + u8* cwd = getcwd(NULL, 0); + if (!cwd) PFATAL("getcwd() failed"); - // 遍历命令行参数 while (argv[i]) { - u8* aa_loc = strstr(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); // 使用默认输出文件名 + 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; // 如果out_file是绝对路径,则直接使用 - else - aa_subst = alloc_printf("%s/%s", cwd, out_file); // 否则,构造绝对路径 + if (out_file[0] == '/') aa_subst = out_file; + else aa_subst = alloc_printf("%s/%s", cwd, out_file); - /* 构造替换后的argv值。 */ - *aa_loc = 0; // 临时终止字符串以构造新值 - n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); // 构造新参数值 - argv[i] = n_arg; // 更新argv - *aa_loc = '@'; // 恢复'@@'符号 + /* Construct a replacement argv value. */ - if (out_file[0] != '/') ck_free(aa_subst); // 如果out_file不是绝对路径,则释放构造的绝对路径 + *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++; // 移动到下一个参数 + i++; + } - free(cwd); // 释放当前工作目录字符串 + free(cwd); /* not tracked */ + } -/* 设置信号处理器。Solaris上的libc比较复杂,因为它在中断的read()调用时不会恢复, - 当你调用siginterrupt()时会设置SA_RESETHAND,并且会做一些不必要的事情。 */ +/* Set up signal handlers. More complicated that needs to be, because libc on + Solaris doesn't resume interrupted reads(), sets SA_RESETHAND when you call + siginterrupt(), and does other unnecessary things. */ EXP_ST void setup_signal_handlers(void) { - struct sigaction sa; // 定义信号动作结构体 + struct sigaction sa; - // 初始化信号动作结构体 - sa.sa_handler = NULL; // 没有指定信号处理函数 - sa.sa_flags = SA_RESTART; // 设置信号处理时自动重启被中断的系统调用 - sa.sa_sigaction = NULL; // 没有指定信号的特定处理函数 + sa.sa_handler = NULL; + sa.sa_flags = SA_RESTART; + sa.sa_sigaction = NULL; - sigemptyset(&sa.sa_mask); // 清空信号集 + sigemptyset(&sa.sa_mask); - /* 各种表示“停止”的信号。 */ + /* Various ways of saying "stop". */ - // 设置信号处理函数为handle_stop_sig sa.sa_handler = handle_stop_sig; - sigaction(SIGHUP, &sa, NULL); // 对SIGHUP信号进行设置 - sigaction(SIGINT, &sa, NULL); // 对SIGINT信号进行设置 - sigaction(SIGTERM, &sa, NULL); // 对SIGTERM信号进行设置 + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); - /* 执行超时通知。 */ + /* Exec timeout notifications. */ - // 设置信号处理函数为handle_timeout sa.sa_handler = handle_timeout; - sigaction(SIGALRM, &sa, NULL); // 对SIGALRM信号进行设置 + sigaction(SIGALRM, &sa, NULL); - /* 窗口大小改变通知 */ + /* Window resize */ - // 设置信号处理函数为handle_resize sa.sa_handler = handle_resize; - sigaction(SIGWINCH, &sa, NULL); // 对SIGWINCH信号进行设置 + sigaction(SIGWINCH, &sa, NULL); - /* SIGUSR1: 跳过条目 */ + /* SIGUSR1: skip entry */ - // 设置信号处理函数为handle_skipreq sa.sa_handler = handle_skipreq; - sigaction(SIGUSR1, &sa, NULL); // 对SIGUSR1信号进行设置 + sigaction(SIGUSR1, &sa, NULL); - /* 我们不关心的信号。 */ + /* Things we don't care about. */ - // 设置信号处理函数为SIG_IGN,忽略这些信号 sa.sa_handler = SIG_IGN; - sigaction(SIGTSTP, &sa, NULL); // 对SIGTSTP信号进行设置 - sigaction(SIGPIPE, &sa, NULL); // 对SIGPIPE信号进行设置 + sigaction(SIGTSTP, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + } -/* 为QEMU重写argv。 */ + +/* Rewrite argv for QEMU. */ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { - char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); // 分配新的argv数组 + char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); u8 *tmp, *cp, *rsl, *own_copy; - /* 针对QEMU稳定性问题的工作区。 */ + /* Workaround for a QEMU stability glitch. */ - setenv("QEMU_LOG", "nochain", 1); // 设置环境变量QEMU_LOG + setenv("QEMU_LOG", "nochain", 1); - // 将原始argv的参数复制到新数组中,从第三个参数开始 memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); - new_argv[2] = target_path; // 设置目标路径 - new_argv[1] = "--"; // 设置参数分隔符 + new_argv[2] = target_path; + new_argv[1] = "--"; - /* 现在我们需要找到QEMU二进制文件并放入argv[0]。 */ + /* Now we need to actually find the QEMU binary to put in argv[0]. */ - tmp = getenv("AFL_PATH"); // 获取环境变量AFL_PATH + tmp = getenv("AFL_PATH"); if (tmp) { - cp = alloc_printf("%s/afl-qemu-trace", tmp); // 构造QEMU路径 + cp = alloc_printf("%s/afl-qemu-trace", tmp); - if (access(cp, X_OK)) // 检查文件是否存在且可执行 - FATAL("Unable to find '%s'", tmp); // 如果找不到,输出错误信息并终止 + if (access(cp, X_OK)) + FATAL("Unable to find '%s'", tmp); - target_path = new_argv[0] = cp; // 设置目标路径和argv[0] - return new_argv; // 返回新的argv数组 + target_path = new_argv[0] = cp; + return new_argv; } - own_copy = ck_strdup(own_loc); // 复制原始位置信息 - rsl = strrchr(own_copy, '/'); // 查找路径分隔符 + own_copy = ck_strdup(own_loc); + rsl = strrchr(own_copy, '/'); if (rsl) { - *rsl = 0; // 将路径分隔符替换为字符串结束符 + *rsl = 0; - cp = alloc_printf("%s/afl-qemu-trace", own_copy); // 构造QEMU路径 - ck_free(own_copy); // 释放原始位置信息 + cp = alloc_printf("%s/afl-qemu-trace", own_copy); + ck_free(own_copy); - if (!access(cp, X_OK)) { // 检查文件是否存在且可执行 + if (!access(cp, X_OK)) { - target_path = new_argv[0] = cp; // 设置目标路径和argv[0] - return new_argv; // 返回新的argv数组 + target_path = new_argv[0] = cp; + return new_argv; } - } else ck_free(own_copy); // 如果没有找到路径分隔符,释放原始位置信息 + } else ck_free(own_copy); - if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { // 检查默认路径下的QEMU是否存在且可执行 + if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { - target_path = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace"); // 设置目标路径和argv[0] - return new_argv; // 返回新的argv数组 + target_path = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace"); + return new_argv; } - SAYF("\n" cLRD "[-] " cRST // 输出错误信息 + SAYF("\n" cLRD "[-] " cRST "Oops, unable to find the 'afl-qemu-trace' binary. The binary must be built\n" " separately by following the instructions in qemu_mode/README.qemu. If you\n" " already have the binary installed, you may need to specify AFL_PATH in the\n" " environment.\n\n" + " Of course, even without QEMU, afl-fuzz can still work with binaries that are\n" " instrumented at compile time with afl-gcc. It is also possible to use it as a\n" " traditional \"dumb\" fuzzer by specifying '-n' in the command line.\n"); - FATAL("Failed to locate 'afl-qemu-trace'."); // 输出错误信息并终止 + FATAL("Failed to locate 'afl-qemu-trace'."); + } -/* 保存当前命令行参数。 */ + +/* Make a copy of the current command line. */ static void save_cmdline(u32 argc, char** argv) { - u32 len = 1, i; // 初始化长度变量 - u8* buf; // 定义缓冲区指针 + u32 len = 1, i; + u8* buf; - // 计算命令行参数的总长度 for (i = 0; i < argc; i++) len += strlen(argv[i]) + 1; - buf = orig_cmdline = ck_alloc(len); // 分配缓冲区 + buf = orig_cmdline = ck_alloc(len); - // 将命令行参数复制到缓冲区 for (i = 0; i < argc; i++) { u32 l = strlen(argv[i]); @@ -7651,11 +7762,12 @@ static void save_cmdline(u32 argc, char** argv) { memcpy(buf, argv[i], l); buf += l; - if (i != argc - 1) *(buf++) = ' '; // 在参数之间添加空格 + if (i != argc - 1) *(buf++) = ' '; } - *buf = 0; // 设置字符串结束符 + *buf = 0; + } @@ -7665,7 +7777,6 @@ static void save_cmdline(u32 argc, char** argv) { int main(int argc, char** argv) { - // 定义了一些变量,用于存储命令行参数和状态 s32 opt; u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to; @@ -7674,406 +7785,413 @@ int main(int argc, char** argv) { u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); char** use_argv; - // 定义时间相关的结构体 struct timeval tv; struct timezone tz; - // 打印欢迎信息和版本号 SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by \n"); - // 设置文档路径 doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; - // 获取当前时间,用于随机数种子 gettimeofday(&tv, &tz); srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); - // 解析命令行参数 - 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) + switch (opt) { - // 处理不同的命令行选项 + case 'i': /* input dir */ - // 设置输入目录 + if (in_dir) FATAL("Multiple -i options not supported"); in_dir = optarg; + 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 */ - // 设置主同步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"); + + 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"); + + } + + force_deterministic = 1; + } - force_deterministic = 1; - } + break; case 'S': - // 设置同步ID + 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; + + 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'; - if (mem_limit_given) FATAL("Multiple -m options not supported"); - mem_limit_given = 1; - if (!strcmp(optarg, "none")) { - 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; - case 'G': mem_limit *= 1024; break; - case 'k': mem_limit /= 1024; break; - case 'M': break; - default: FATAL("Unsupported suffix or bad syntax for -m"); + + u8 suffix = 'M'; + + if (mem_limit_given) FATAL("Multiple -m options not supported"); + mem_limit_given = 1; + + if (!strcmp(optarg, "none")) { + + 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; + case 'G': mem_limit *= 1024; break; + case 'k': mem_limit /= 1024; break; + case 'M': break; + + default: FATAL("Unsupported suffix or bad syntax for -m"); + + } + + if (mem_limit < 5) FATAL("Dangerously low value of -m"); + + if (sizeof(rlim_t) == 4 && mem_limit > 2000) + FATAL("Value of -m out of range on 32-bit systems"); + } - if (mem_limit < 5) FATAL("Dangerously low value of -m"); - if (sizeof(rlim_t) == 4 && mem_limit > 2000) - FATAL("Value of -m out of range on 32-bit systems"); - } + break; case 'b': { /* bind CPU core */ - // 绑定CPU核心 - if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); - cpu_to_bind_given = 1; - if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -b"); - break; + + if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); + cpu_to_bind_given = 1; + + if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || + optarg[0] == '-') FATAL("Bad syntax used for -b"); + + break; + } case 'd': /* skip deterministic */ - // 跳过确定性测试 + if (skip_deterministic) FATAL("Multiple -d options not supported"); skip_deterministic = 1; use_splicing = 1; 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. */ + if (in_bitmap) FATAL("Multiple -B options not supported"); + in_bitmap = optarg; read_bitmap(in_bitmap); break; case 'C': /* crash mode */ - // 设置为崩溃模式 + if (crash_mode) FATAL("Multiple -C options not supported"); crash_mode = FAULT_CRASH; break; case 'n': /* dumb mode */ - // 设置为简单模式 + 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 */ - // 设置横幅 + if (use_banner) FATAL("Multiple -T options not supported"); use_banner = optarg; break; case 'Q': /* QEMU mode */ - // 设置为QEMU模式 + if (qemu_mode) FATAL("Multiple -Q options not supported"); qemu_mode = 1; + if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; + break; case 'V': /* Show version number */ - // 显示版本号并退出 + + /* Version number has been printed already, just quit. */ exit(0); default: - // 默认行为:显示使用说明 + usage(argv[0]); + } - } - // 检查必要的参数是否已设置 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_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 (getenv("AFL_PRELOAD")) { setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); } -// 如果设置了环境变量AFL_LD_PRELOAD,则输出错误信息并终止程序。 -if (getenv("AFL_LD_PRELOAD")) + if (getenv("AFL_LD_PRELOAD")) FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); -// 保存命令行参数,以便后续使用。 -save_cmdline(argc, argv); + save_cmdline(argc, argv); -// 修复banner信息,这通常是为了显示程序的版本号或其他信息。 -fix_up_banner(argv[optind]); + fix_up_banner(argv[optind]); -// 检查当前是否是TTY环境,这可能影响程序的输出方式。 -check_if_tty(); + check_if_tty(); -// 获取CPU核心数,这有助于后续的并行处理。 -get_core_count(); + get_core_count(); #ifdef HAVE_AFFINITY -// 如果支持CPU亲和性设置,则将程序绑定到一个空闲的CPU核心上。 -bind_to_free_cpu(); + bind_to_free_cpu(); #endif /* HAVE_AFFINITY */ -// 检查崩溃处理设置,确保程序在崩溃时能够正确处理。 -check_crash_handling(); -check_cpu_governor(); + check_crash_handling(); + check_cpu_governor(); -// 设置后续操作,这可能包括日志文件的设置等。 -setup_post(); -setup_shm(); -init_count_class16(); + setup_post(); + setup_shm(); + init_count_class16(); -// 设置目录和文件描述符,为后续的文件操作做准备。 -setup_dirs_fds(); -read_testcases(); -load_auto(); + setup_dirs_fds(); + read_testcases(); + load_auto(); -// 调整输入,这可能涉及到对测试用例的预处理。 -pivot_inputs(); + pivot_inputs(); -// 如果指定了extras目录,则加载额外的测试用例。 -if (extras_dir) load_extras(extras_dir); + if (extras_dir) load_extras(extras_dir); -// 如果没有给定超时时间,则自动寻找一个合适的超时时间。 -if (!timeout_given) find_timeout(); + if (!timeout_given) find_timeout(); -// 检测文件参数,这可能涉及到对命令行参数的处理。 -detect_file_args(argv + optind + 1); + detect_file_args(argv + optind + 1); -// 如果没有指定输出文件,则设置标准输出文件。 -if (!out_file) setup_stdio_file(); + if (!out_file) setup_stdio_file(); -// 检查二进制文件,这可能涉及到对目标程序的验证。 -check_binary(argv[optind]); + check_binary(argv[optind]); -// 获取当前时间,用于后续的时间统计。 -start_time = get_cur_time(); + start_time = get_cur_time(); -// 如果启用了QEMU模式,则获取QEMU的命令行参数,否则使用原始参数。 -if (qemu_mode) + if (qemu_mode) use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); -else + else use_argv = argv + optind; -// 执行一次干运行,以检查程序的行为。 -perform_dry_run(use_argv); + perform_dry_run(use_argv); -// 修剪队列,移除无效或重复的测试用例。 -cull_queue(); + cull_queue(); -// 显示初始化统计信息。 -show_init_stats(); + show_init_stats(); -// 寻找开始位置,这可能涉及到对测试用例的排序或选择。 -seek_to = find_start_position(); + seek_to = find_start_position(); -// 写入统计文件,保存当前的状态。 -write_stats_file(0, 0, 0); -save_auto(); + write_stats_file(0, 0, 0); + save_auto(); -// 如果程序即将停止,则跳转到停止处理部分。 -if (stop_soon) goto stop_fuzzing; + if (stop_soon) goto stop_fuzzing; -// 如果不是在TTY环境下,则等待一段时间再开始测试。 -if (!not_on_tty) { + /* Woop woop woop */ + + if (!not_on_tty) { sleep(4); start_time += 4000; if (stop_soon) goto stop_fuzzing; -} + } + + while (1) { -// 主循环开始,这里会不断执行模糊测试,直到程序停止。 -while (1) { u8 skipped_fuzz; - // 修剪队列,移除无效或重复的测试用例。 cull_queue(); - // 如果队列中没有当前的测试用例,则进入下一个循环周期。 if (!queue_cur) { - queue_cycle++; - current_entry = 0; - cur_skipped_paths = 0; - queue_cur = queue; - - // 如果需要seek到特定位置,则移动队列指针。 - while (seek_to) { - current_entry++; - seek_to--; - queue_cur = queue_cur->next; - } - // 显示统计信息。 - show_stats(); + queue_cycle++; + current_entry = 0; + cur_skipped_paths = 0; + queue_cur = queue; - // 如果不是在TTY环境下,则输出当前的循环周期。 - if (not_on_tty) { - ACTF("Entering queue cycle %llu.", queue_cycle); - fflush(stdout); - } + 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; - // 如果在当前循环周期中没有新的发现,则尝试重组策略。 - if (queued_paths == prev_queued) { - if (use_splicing) cycles_wo_finds++; else use_splicing = 1; - } else cycles_wo_finds = 0; + prev_queued = queued_paths; - // 更新之前的队列路径数。 - prev_queued = queued_paths; + if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) + sync_fuzzers(use_argv); - // 如果设置了同步ID并且是第一个循环周期,并且设置了AFL_IMPORT_FIRST环境变量,则同步模糊测试者。 - if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) - sync_fuzzers(use_argv); } - // 执行一次模糊测试。 skipped_fuzz = fuzz_one(use_argv); - // 如果程序没有停止并且设置了同步ID并且没有跳过模糊测试,则尝试同步模糊测试者。 if (!stop_soon && sync_id && !skipped_fuzz) { - if (!(sync_interval_cnt++ % SYNC_INTERVAL)) - sync_fuzzers(use_argv); + + if (!(sync_interval_cnt++ % SYNC_INTERVAL)) + sync_fuzzers(use_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(); + } + + if (queue_cur) show_stats(); -// 如果程序是被程序性地停止的,那么杀死forkserver和当前的运行者。 -// 如果是手动停止的,那么这部分由信号处理程序完成。 -if (stop_soon == 2) { - if (child_pid > 0) kill(child_pid, SIGKILL); - if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); -} -// 现在我们已经杀死了forkserver,我们可以等待它以便获取资源使用统计信息。 -if (waitpid(forksrv_pid, NULL, 0) <= 0) { + /* 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"); -} + } -// 写入bitmap,保存当前的测试覆盖情况。 -write_bitmap(); -// 写入统计文件,保存最终的状态。 -write_stats_file(0, 0, 0); -// 保存自动保存的信息。 -save_auto(); + 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"); + 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? */ + + if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) { -// 如果运行超过30分钟但仍然在第一个循环周期,输出警告信息。 -if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) { SAYF("\n" cYEL "[!] " cRST - "Stopped during the first cycle, results may be incomplete.\n" - " (For info on resuming, see %s/README.)\n", doc_path); -} + "Stopped during the first cycle, results may be incomplete.\n" + " (For info on resuming, see %s/README.)\n", doc_path); -// 关闭绘图文件,销毁队列和额外的测试用例,释放内存。 -fclose(plot_file); -destroy_queue(); -destroy_extras(); -ck_free(target_path); -ck_free(sync_id); + } + + fclose(plot_file); + destroy_queue(); + destroy_extras(); + ck_free(target_path); + ck_free(sync_id); -// 生成报告。 -alloc_report(); + alloc_report(); -// 输出结束信息,表示测试完成。 -OKF("We're done here. Have a nice day!\n"); + OKF("We're done here. Have a nice day!\n"); + + exit(0); -// 退出程序。 -exit(0); } + #endif /* !AFL_LIB */ diff --git a/src/afl-gcc.c b/src/afl-gcc.c index 20de974..f0e4d85 100644 --- a/src/afl-gcc.c +++ b/src/afl-gcc.c @@ -14,8 +14,6 @@ limitations under the License. */ -// 这部分是版权声明和许可证信息,说明这个文件是在Apache License 2.0下发布的。 - /* american fuzzy lop - wrapper for GCC and clang ---------------------------------------------- @@ -40,7 +38,7 @@ specify its location via AFL_CC or AFL_CXX. */ -// 这部分是注释,提供了关于这个程序的概述和使用说明。 + #define AFL_MAIN #include "config.h" @@ -53,8 +51,6 @@ #include #include -// 这些是包含的头文件,其中一些是AFL自己的头文件,其他的是C标准库的头文件 - static u8* as_path; /* Path to the AFL 'as' wrapper */ static u8** cc_params; /* Parameters passed to the real CC */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */ @@ -62,14 +58,11 @@ static u8 be_quiet, /* Quiet mode */ clang_mode; /* Invoked as afl-clang*? */ -// 这些是全局变量声明。`as_path`存储AFL汇编器的路径,`cc_params`存储传递给实际编译器的参数,`cc_par_cnt`是参数计数器,`be_quiet`用于控制 -// 是否静默模式,`clang_mode`指示是否以`afl-clang`或`afl-clang++`模式调用。 - /* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived from argv[0]. If that fails, abort. */ static void find_as(u8* argv0) { -// 这个函数尝试在AFL_PATH环境变量指定的路径或从argv[0]派生的路径中找到AFL的“假”GNU汇编器。如果找不到,程序将终止。 + u8 *afl_path = getenv("AFL_PATH"); u8 *slash, *tmp; @@ -123,32 +116,31 @@ static void find_as(u8* argv0) { /* Copy argv to cc_params, making the necessary edits. */ static void edit_params(u32 argc, char** argv) { - //定义了一个函数edit_params,它接受两个参数:argc是参数的数量,argv是参数的数组。 - u8 fortify_set = 0, asan_set = 0;//声明两个变量fortify_set和asan_set,用于跟踪是否已经设置了FORTIFY_SOURCE和address sanitizer(ASan)标志 - u8 *name;//用于存储程序的名称 + u8 fortify_set = 0, asan_set = 0; + u8 *name; #if defined(__FreeBSD__) && defined(__x86_64__) u8 m32_set = 0; #endif - cc_params = ck_alloc((argc + 128) * sizeof(u8*));//分配内存以存储修改后的参数列表,大小为argc + 128个u8*类型的指针。 + cc_params = ck_alloc((argc + 128) * sizeof(u8*)); - name = strrchr(argv[0], '/');//找到argv[0](程序的路径)中最后一个'/'字符,这通常用于获取程序的名称。 - if (!name) name = argv[0]; else name++;//如果name为NULL(即argv[0]中没有'/'),则name指向argv[0]的开始。否则,name向前移动一个字符,跳过'/'。 + name = strrchr(argv[0], '/'); + if (!name) name = argv[0]; else name++; if (!strncmp(name, "afl-clang", 9)) { - clang_mode = 1;//检查程序名称是否以"afl-clang"开头,如果是,设置clang_mode标志为1 + clang_mode = 1; - setenv(CLANG_ENV_VAR, "1", 1);//设置环境变量CLANG_ENV_VAR为"1",这可能用于通知其他部分的AFL工具链正在使用Clang。 + setenv(CLANG_ENV_VAR, "1", 1); if (!strcmp(name, "afl-clang++")) { u8* alt_cxx = getenv("AFL_CXX"); - cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";//如果AFL_CXX设置,将其值作为第一个参数;否则,使用"clang++"。 + cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++"; } else { u8* alt_cc = getenv("AFL_CC"); - cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";//否则尝试获取环境变量AFL_CC的值。 + cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; } } else { @@ -160,13 +152,13 @@ static void edit_params(u32 argc, char** argv) { binaries. Meh. */ #ifdef __APPLE__ - //在Apple系统上,根据程序名称设置不同的编译器。如果AFL_CXX、AFL_GCJ或AFL_CC环境变量设置,使用它们的值;否则,使用默认的编译器名称 + if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX"); else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ"); else cc_params[0] = getenv("AFL_CC"); if (!cc_params[0]) { - //输出错误信息,指出在MacOS X上需要设置AFL_CC或AFL_CXX环境变量。 + SAYF("\n" cLRD "[-] " cRST "On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n" " 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n" @@ -177,7 +169,7 @@ static void edit_params(u32 argc, char** argv) { } #else - //对于非Apple系统,根据程序名称设置不同的编译器。如果相应的环境变量设置,使用它们的值;否则,使用默认的编译器名称。 + if (!strcmp(name, "afl-g++")) { u8* alt_cxx = getenv("AFL_CXX"); cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++"; @@ -196,27 +188,27 @@ static void edit_params(u32 argc, char** argv) { while (--argc) { u8* cur = *(++argv); - if (!strncmp(cur, "-B", 2)) {//如果当前参数以"-B"开头,输出警告信息,并跳过后续参数(如果当前参数后面紧跟着的是编译器的路径)。 + if (!strncmp(cur, "-B", 2)) { - if (!be_quiet) WARNF("-B is already set, overriding");//如果程序不在静默模式,输出警告信息。 + if (!be_quiet) WARNF("-B is already set, overriding"); - if (!cur[2] && argc > 1) { argc--; argv++; }//如果-B后面紧跟着的是编译器的路径,跳过这个路径。 + if (!cur[2] && argc > 1) { argc--; argv++; } continue; } - if (!strcmp(cur, "-integrated-as")) continue;//如果参数是"-integrated-as",跳过它。 + if (!strcmp(cur, "-integrated-as")) continue; - if (!strcmp(cur, "-pipe")) continue;//如果参数是"-pipe",跳过它。 + if (!strcmp(cur, "-pipe")) continue; #if defined(__FreeBSD__) && defined(__x86_64__) if (!strcmp(cur, "-m32")) m32_set = 1; #endif if (!strcmp(cur, "-fsanitize=address") || - !strcmp(cur, "-fsanitize=memory")) asan_set = 1;//如果参数是"-fsanitize=address"或"-fsanitize=memory",设置asan_set标志。 + !strcmp(cur, "-fsanitize=memory")) asan_set = 1; - if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;//如果参数包含"FORTIFY_SOURCE",设置fortify_set标志。 + if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; cc_params[cc_par_cnt++] = cur; @@ -224,11 +216,9 @@ static void edit_params(u32 argc, char** argv) { cc_params[cc_par_cnt++] = "-B"; cc_params[cc_par_cnt++] = as_path; - //向参数列表中添加"-B"和AFL汇编器的路径。 - if (clang_mode) - cc_params[cc_par_cnt++] = "-no-integrated-as";//如果clang_mode标志设置,向参数列表中添加`"-no-integrated-as" + cc_params[cc_par_cnt++] = "-no-integrated-as"; if (getenv("AFL_HARDEN")) { @@ -239,38 +229,38 @@ static void edit_params(u32 argc, char** argv) { } - if (asan_set) {//检查是否设置了asan_set标志。 + if (asan_set) { /* Pass this on to afl-as to adjust map density. */ - setenv("AFL_USE_ASAN", "1", 1);//如果设置,设置环境变量AFL_USE_ASAN为"1" + setenv("AFL_USE_ASAN", "1", 1); - } else if (getenv("AFL_USE_ASAN")) {//如果asan_set标志未设置,但设置了环境变量AFL_USE_ASAN。 + } else if (getenv("AFL_USE_ASAN")) { if (getenv("AFL_USE_MSAN")) - FATAL("ASAN and MSAN are mutually exclusive");//如果同时设置了AFL_USE_MSAN,输出错误信息并终止程序。 + FATAL("ASAN and MSAN are mutually exclusive"); if (getenv("AFL_HARDEN")) - FATAL("ASAN and AFL_HARDEN are mutually exclusive");//如果同时设置了AFL_HARDEN,输出错误信息并终止程序。 + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=address";//向参数列表中添加"-U_FORTIFY_SOURCE"和"-fsanitize=address"。 + cc_params[cc_par_cnt++] = "-fsanitize=address"; } else if (getenv("AFL_USE_MSAN")) { if (getenv("AFL_USE_ASAN")) - FATAL("ASAN and MSAN are mutually exclusive");//如果同时设置了AFL_USE_ASAN,输出错误信息并终止程序。 + FATAL("ASAN and MSAN are mutually exclusive"); if (getenv("AFL_HARDEN")) - FATAL("MSAN and AFL_HARDEN are mutually exclusive");//如果同时设置了AFL_HARDEN,输出错误信息并终止程序。 + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=memory";//向参数列表中添加"-U_FORTIFY_SOURCE"和"-fsanitize=memory"。 + cc_params[cc_par_cnt++] = "-fsanitize=memory"; } - if (!getenv("AFL_DONT_OPTIMIZE")) {//检查是否设置了环境变量AFL_DONT_OPTIMIZE。 + if (!getenv("AFL_DONT_OPTIMIZE")) { #if defined(__FreeBSD__) && defined(__x86_64__) @@ -279,23 +269,22 @@ static void edit_params(u32 argc, char** argv) { that bug. */ if (!clang_mode || !m32_set) - cc_params[cc_par_cnt++] = "-g";//如果不是Clang模式或没有设置m32_set标志,向参数列表中添加"-g"。 + cc_params[cc_par_cnt++] = "-g"; #else cc_params[cc_par_cnt++] = "-g"; -#endif//结束#if defined(__FreeBSD__) && defined(__x86_64__)条件编译块。 +#endif cc_params[cc_par_cnt++] = "-O3"; - cc_params[cc_par_cnt++] = "-funroll-loops";//向参数列表中添加"-O3"和"-funroll-loops",这些是优化选项。 + cc_params[cc_par_cnt++] = "-funroll-loops"; /* Two indicators that you're building for fuzzing; one of them is AFL-specific, the other is shared with libfuzzer. */ cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; - //向参数列表中添加两个宏定义,这些宏定义指示编译器代码将用于模糊测试。 } @@ -308,7 +297,7 @@ static void edit_params(u32 argc, char** argv) { cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; cc_params[cc_par_cnt++] = "-fno-builtin-strstr"; cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr"; - //如果设置,向参数列表中添加一系列"-fno-builtin-*"选项,这些选项禁用编译器的内置函数。 + } cc_params[cc_par_cnt] = NULL; @@ -317,7 +306,7 @@ static void edit_params(u32 argc, char** argv) { /* Main entry point */ -//最后是函数结束语,结束函数定义。 + int main(int argc, char** argv) { if (isatty(2) && !getenv("AFL_QUIET")) { diff --git a/src/afl-gotcpu.c b/src/afl-gotcpu.c index eaa58e1..630b7ff 100644 --- a/src/afl-gotcpu.c +++ b/src/afl-gotcpu.c @@ -1,31 +1,21 @@ /* Copyright 2015 Google LLC All rights reserved. - - /* - code -*/ - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. // code + you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software // code + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -/* // code +/* american fuzzy lop - free CPU gizmo - - /* - code -*/ - ----------------------------------- Written and maintained by Michal Zalewski @@ -35,12 +25,7 @@ in the afl-fuzz UI. See docs/parallel_fuzzing.txt for more info. For some work loads, the tool may actually suggest running more instances - - /* - code -*/ - - than you have CPU cores. This can happen if the tested program is spending // code + than you have CPU cores. This can happen if the tested program is spending a portion of its run time waiting for I/O, rather than being 100% CPU-bound. @@ -49,10 +34,10 @@ #define AFL_MAIN #include "android-ashmem.h" -#define _GNU_SOURCE // code +#define _GNU_SOURCE #include -#include // code +#include #include #include #include @@ -60,47 +45,27 @@ #include #include #include -#include // code +#include #include "types.h" #include "debug.h" #ifdef __linux__ # define HAVE_AFFINITY 1 - - /* - code -*/ - #endif /* __linux__ */ /* Get unix time in microseconds. */ - /* - code -*/ - - -static u64 get_cur_time_us(void) { // code +static u64 get_cur_time_us(void) { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); - - /* - code -*/ - return (tv.tv_sec * 1000000ULL) + tv.tv_usec; - - /* - code -*/ - } @@ -112,16 +77,11 @@ static u64 get_cpu_usage_us(void) { getrusage(RUSAGE_SELF, &u); - /* - code -*/ - - return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec + (u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec; - // code + } - // code + /* Measure preemption rate. */ @@ -133,67 +93,37 @@ static u32 measure_preemption(u32 target_ms) { s32 loop_repeats = 0; st_t = get_cur_time_us(); - - /* - code -*/ - - st_c = get_cpu_usage_us(); // code + st_c = get_cpu_usage_us(); repeat_loop: v1 = CTEST_BUSY_CYCLES; - - /* - code -*/ - while (v1--) v2++; sched_yield(); - // code - en_t = get_cur_time_us(); - - /* - code -*/ + en_t = get_cur_time_us(); if (en_t - st_t < target_ms * 1000) { loop_repeats++; - - /* - code -*/ - - goto repeat_loop; // code + goto repeat_loop; } /* Let's see what percentage of this time we actually had a chance to - - /* - code -*/ - run, and how much time was spent in the penalty box. */ - en_c = get_cpu_usage_us(); // code + en_c = get_cpu_usage_us(); real_delta = (en_t - st_t) / 1000; slice_delta = (en_c - st_c) / 1000; - /* - code -*/ - - // code return real_delta * 100 / slice_delta; -} // code +} + - // code /* Do the benchmark thing. */ - // code + int main(int argc, char** argv) { #ifdef HAVE_AFFINITY @@ -202,57 +132,42 @@ int main(int argc, char** argv) { idle_cpus = 0, maybe_cpus = 0, i; SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by \n"); - // code + ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...", - ((double)CTEST_CORE_TRG_MS) / 1000); // code + ((double)CTEST_CORE_TRG_MS) / 1000); for (i = 0; i < cpu_cnt; i++) { - s32 fr = fork(); // code + s32 fr = fork(); - if (fr < 0) PFATAL("fork failed"); // code + if (fr < 0) PFATAL("fork failed"); if (!fr) { - - /* - code -*/ - cpu_set_t c; u32 util_perc; - - /* - code -*/ - - CPU_ZERO(&c); // code + CPU_ZERO(&c); CPU_SET(i, &c); - if (sched_setaffinity(0, sizeof(c), &c)) // code + if (sched_setaffinity(0, sizeof(c), &c)) PFATAL("sched_setaffinity failed for cpu %d", i); util_perc = measure_preemption(CTEST_CORE_TRG_MS); - if (util_perc < 110) { // code + if (util_perc < 110) { SAYF(" Core #%u: " cLGN "AVAILABLE " cRST "(%u%%)\n", i, util_perc); - exit(0); // code + exit(0); - } else if (util_perc < 250) { // code + } else if (util_perc < 250) { SAYF(" Core #%u: " cYEL "CAUTION " cRST "(%u%%)\n", i, util_perc); - - /* - code -*/ - exit(1); } - SAYF(" Core #%u: " cLRD "OVERBOOKED " cRST "(%u%%)\n" cRST, i, // code + SAYF(" Core #%u: " cLRD "OVERBOOKED " cRST "(%u%%)\n" cRST, i, util_perc); exit(2); @@ -265,49 +180,39 @@ int main(int argc, char** argv) { int ret; if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed"); - if (WEXITSTATUS(ret) == 0) idle_cpus++; // code + if (WEXITSTATUS(ret) == 0) idle_cpus++; if (WEXITSTATUS(ret) <= 1) maybe_cpus++; } SAYF(cGRA "\n>>> "); - if (idle_cpus) { // code + if (idle_cpus) { if (maybe_cpus == idle_cpus) { - - /* - code -*/ - SAYF(cLGN "PASS: " cRST "You can run more processes on %u core%s.", idle_cpus, idle_cpus > 1 ? "s" : ""); - // code + } else { - SAYF(cLGN "PASS: " cRST "You can run more processes on %u to %u core%s.", // code + SAYF(cLGN "PASS: " cRST "You can run more processes on %u to %u core%s.", idle_cpus, maybe_cpus, maybe_cpus > 1 ? "s" : ""); } - /* - code -*/ - - // code - SAYF(cGRA " <<<" cRST "\n\n"); // code + SAYF(cGRA " <<<" cRST "\n\n"); return 0; - // code + } - // code + if (maybe_cpus) { SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.", - maybe_cpus, maybe_cpus > 1 ? "s" : ""); // code + maybe_cpus, maybe_cpus > 1 ? "s" : ""); SAYF(cGRA " <<<" cRST "\n\n"); return 1; - // code + } SAYF(cLRD "FAIL: " cRST "All cores are overbooked."); @@ -316,13 +221,8 @@ int main(int argc, char** argv) { #else - - /* - code -*/ - u32 util_perc; - // code + SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by \n"); /* Run a busy loop for CTEST_TARGET_MS. */ @@ -332,35 +232,25 @@ int main(int argc, char** argv) { util_perc = measure_preemption(CTEST_TARGET_MS); - /* - code -*/ - - // code /* Deliver the final verdict. */ - // code + SAYF(cGRA "\n>>> "); if (util_perc < 105) { SAYF(cLGN "PASS: " cRST "You can probably run additional processes."); - } else if (util_perc < 130) { // code + } else if (util_perc < 130) { SAYF(cYEL "CAUTION: " cRST "Your CPU may be somewhat overbooked (%u%%).", util_perc); - - /* - code -*/ - } else { - SAYF(cLRD "FAIL: " cRST "Your CPU is overbooked (%u%%).", util_perc); // code + SAYF(cLRD "FAIL: " cRST "Your CPU is overbooked (%u%%).", util_perc); } - // code + SAYF(cGRA " <<<" cRST "\n\n"); return (util_perc > 105) + (util_perc > 130);