|
|
|
@ -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;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|