diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 3d4a5a7..d400125 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1551,141 +1551,115 @@ 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; // 关键词长度 - u8 *rptr, *wptr; - u32 klen = 0; - - cur_line++; - - /* Trim on left and right. */ + cur_line++; // 行号增加 + // 去除行首的空白字符 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; - - /* Skip alphanumerics and dashes (label). */ + *rptr = 0; // 去除行尾的双引号 + // 跳过字母数字和下划线(标签) 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++; - - if (!*lptr) FATAL("Empty keyword in line %u.", cur_line); + lptr++; // 跳过双引号 - /* 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)); + if (!*lptr) FATAL("Empty keyword in line %u.", cur_line); // 检查关键词是否为空 + // 分配内存并复制数据,处理转义字符 + 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: + case 1 ... 31: // 非打印字符 + case 128 ... 255: // 非ASCII字符 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); - - *(wptr++) = - ((strchr(hexdigits, tolower(lptr[1])) - hexdigits) << 4) | - (strchr(hexdigits, tolower(lptr[2])) - hexdigits); - + // 处理\xNN形式的转义字符 + *(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; - - extras_cnt++; + if (*min_len > klen) *min_len = klen; // 更新最小长度 + if (*max_len < klen) *max_len = klen; // 更新最大长度 + extras_cnt++; // 增加额外数据计数 } - fclose(f); - + fclose(f); // 关闭文件 } - /* Read extras from the extras directory and sort them by size. */ static void load_extras(u8* dir) { @@ -2573,158 +2547,152 @@ static void show_stats(void); //start_us, stop_us; //保存了一些旧的状态值,以便在函数执行完毕后恢复。 - +// 定义一个用于校准测试用例的函数 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; - /* Be a bit more generous about timeouts when resuming sessions, or when - trying to calibrate already-added finds. This helps avoid trouble due - to intermittent latency. */ -//如果是在恢复会话或校准已经添加的测试用例时,会放宽超时限制。 + /* 如果是在恢复会话或校准已经添加的测试用例时,会放宽超时限制。 + 这有助于避免由于间歇性延迟导致的问题。 */ if (!from_queue || resuming_fuzz) use_tmout = MAX(exec_tmout + CAL_TMOUT_ADD, exec_tmout * CAL_TMOUT_PERC / 100); + // 增加校准失败计数 q->cal_failed++; -//设置当前阶段为“calibration”,并根据是否快速校准设置阶段最大值。 + + // 设置当前阶段为“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服务器。 + /* 确保在执行任何操作之前fork服务器已经启动,并且不将其启动时间计入二进制校准。 */ + // 如果没有运行在“dumb”模式,并且没有使用fork服务器,那么初始化fork服务器 if (dumb_mode != 1 && !no_forkserver && !forksrv_pid) init_forkserver(argv); + // 如果队列条目已经有执行校验和,那么将第一次执行的trace bits复制到first_trace if (q->exec_cksum) { -//如果队列条目已经有执行校验和,那么将第一次执行的trace bits复制到first_trace。 memcpy(first_trace, trace_bits, MAP_SIZE); hnb = has_new_bits(virgin_bits); if (hnb > new_bits) new_bits = hnb; - } - start_us = get_cur_time_us();//计时 + // 计时开始 + start_us = get_cur_time_us(); + // 校准循环 for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { u32 cksum; + // 如果不是第一次运行并且到了更新频率,显示统计信息 if (!first_run && !(stage_cur % stats_update_freq)) show_stats(); -//如果不是第一次运行并且到了更新频率,显示统计信息。 - write_to_testcase(use_mem, q->len);//将测试用例数据写入文件。 - - fault = run_target(argv, use_tmout);//运行目标程序并获取执行结果。 + // 将测试用例数据写入文件 + write_to_testcase(use_mem, q->len); + // 运行目标程序并获取执行结果 + fault = run_target(argv, use_tmout); - /* stop_soon is set by the handler for Ctrl+C. When it's pressed, - we want to bail out quickly. */ - - if (stop_soon || fault != crash_mode) goto abort_calibration;//如果用户请求停止或者执行结果不是崩溃模式,则跳到校准中止。 + /* 如果用户按下Ctrl+C,stop_soon会被设置,我们希望快速退出。 */ + 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; } - cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);//计算当前trace bits的哈希值,并与队列条目的执行校验和比较。 - + // 计算当前trace bits的哈希值,并与队列条目的执行校验和比较 + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); if (q->exec_cksum != cksum) { -//如果有新的bits被设置,则更新new_bits。 + // 如果有新的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; - //对于每个不同的字节,将其标记为变量字节,并增加校准周期。 + // 对于每个不同的字节,将其标记为变量字节,并增加校准周期 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; - //如果队列条目之前没有执行校验和,则更新其执行校验和并复制当前trace bits。 } else { - + // 如果队列条目之前没有执行校验和,则更新其执行校验和并复制当前trace bits q->exec_cksum = cksum; memcpy(first_trace, trace_bits, MAP_SIZE); - } - } - } + // 计时结束 stop_us = get_cur_time_us(); - total_cal_us += stop_us - start_us;//更新总校准时间和周期。 + // 更新总校准时间和周期 + total_cal_us += stop_us - start_us; total_cal_cycles += stage_max; - /* OK, let's collect some stats about the performance of this test case. - This is used for fuzzing air time calculations in calculate_score(). */ -//更新队列条目的执行时间、位图大小和校准失败计数。 + /* 收集一些关于这个测试用例性能的统计数据。 + 这用于在calculate_score()中计算fuzzing的空中时间。 */ + // 更新队列条目的执行时间、位图大小和校准失败计数 q->exec_us = (stop_us - start_us) / stage_max; q->bitmap_size = count_bytes(trace_bits); q->handicap = handicap; q->cal_failed = 0; - total_bitmap_size += q->bitmap_size;//更新总位图大小和条目数 + // 更新总位图大小和条目数 + total_bitmap_size += q->bitmap_size; 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: -//如果有新的bits被设置并且队列条目之前没有新覆盖,则更新队列条目的新覆盖标志。 + // 如果有新的bits被设置并且队列条目之前没有新覆盖,则更新队列条目的新覆盖标志 if (new_bits == 2 && !q->has_new_cov) { q->has_new_cov = 1; queued_with_cov++; } - /* Mark variable paths. */ - + /* 标记可变路径。 */ if (var_detected) { -//如果检测到变量行为,则标记队列条目。 + // 如果检测到变量行为,则标记队列条目 var_byte_count = count_bytes(var_bytes); - if (!q->var_behavior) { mark_as_variable(q); queued_variable++; } - } -//恢复旧的状态值。 + // 恢复旧的状态值 stage_name = old_sn; stage_cur = old_sc; stage_max = old_sm; - if (!first_run) show_stats();//如果校准中止,则跳到函数末尾。 + // 如果校准中止,则跳到函数末尾 + if (!first_run) show_stats(); + // 返回错误代码 return fault; - }