afl-fuzz.c
/* Compute size of DSF map based on DSF_MAX items of MAP_SIZE 4-byte records */
根据MAP_SIZE 4字节记录的DSF_MAX项计算DSF地图的大小
#define DSF_LEN (DSF_MAX * MAP_SIZE)
EXP_ST u32* dsf_map; /* DSF - SHM与附加地图 */
EXP_ST u32 dsf_cumulated[DSF_LEN]; /* DSF - 追踪累积的数值*/
EXP_ST u8 dsf_entry_changed[DSF_LEN]; /* DSF - 追踪运行中的变化 */
EXP_ST int dsf_count = 0; /* DDSF - 注册域名的数量 */
EXP_ST int dsf_len_actual = 0; /* 对DSF长度的动态调整 */
EXP_ST dsf_config dsf_configs[DSF_MAX]; /* DSF - 配置结构数组 */
typedef u32 (*reducer_t)(u32, u32); /* 减速器函数的签名*/
extern reducer_t dsf_reducers[]; /*预定义的还原器函数阵列 */
extern const char* dsf_reducer_names[];/* 预定义的还原器函数名称数组 */
num_dsf_inputs, /*在dsf地图上有东西的输入的数量*/
dsf_cksum; /* DSF - cksum of 未被发现的痕迹 */
/* dsf_map是否有一个非零值的*/
static inline u8 has_dsf_bit() {
for (int i = 0; i < dsf_len_actual; i++){
if (unlikely(dsf_map[i])){
return 1;
}
}
return 0;
}
int hibit(unsigned int n) {
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return n - (n >> 1);
}
/* 特定领域的模糊地图是否有任何变化。如果多次调用此函数,应返回false。 */
static inline u8 has_dsf_changed() {
int ret = 0;
for (int j = 0; j < dsf_count; j++) {
dsf_config* dsf = &dsf_configs[j]; // Config for this domain
reducer_t reduce = dsf_reducers[dsf->reducer]; // Reducer function for this domain
for (int i = dsf->start; i < dsf->end; i++){
u32 cur_val = dsf_map[i];
u32 old_cumulated = dsf_cumulated[i];
u32 new_cumulated = reduce(old_cumulated, cur_val);
if (unlikely(old_cumulated != new_cumulated)) {
dsf_cumulated[i] = new_cumulated;
ret = 1;
dsf_entry_changed[i] = 1;
DEBUG("DSF update at key=0x%06x, val=%u (0x%08x); new aggregate: %u (0x%08x); earlier was: %u (0x%08x)\n", i, cur_val, cur_val, new_cumulated, new_cumulated, old_cumulated, old_cumulated);
}
}
}
return ret;
}
当我们遇到一条新的路径时,我们会调用这个功能,看看这条路径是否径比任何现有的路径更 “有利”。判断 "有利 "的目的是要有一个最小的路径集,它可以触发迄今为止在位图中看到的所有位并专注于对它们进行模糊处理,而忽略了其余的。
这个过程的第一步是为位图中的每个字节维护一个top_rated[]条目列表。如果之前没有竞争者,或者竞争者有更有利的速度x大小的因素,我们就会赢得那个位置。
static void update_bitmap_score(struct queue_entry* q) {
u32 i;
if (dsf_enabled){
for (int j = 0; j < dsf_count; j++) {
dsf_config* dsf = &dsf_configs[j];
for (i = dsf->start; i < dsf->end; i++) {
/* Did the entry in the DSF map change this run? */
if (dsf_entry_changed[i]) {
/* Insert ourselves as the new winner. */
top_rated[i] = q;
score_changed = 1;
DEBUG("This input is the new top rated entry for key 0x%06x\n", i);
dsf_entry_changed[i] = 0;
}
}
}
上面讨论的机制的第二部分是一个例行程序,它翻阅top_rated[]条目,然后依次抓取以前未见过的字节(temp_v)的赢家,并将其标记为被看好的,至少在 直到下一次运行。在所有的模糊处理步骤中,被看好的条目会被给予更多的播放时间。 在所有的模糊处理步骤中,被看好的条目会得到更多的时间。 在dsf_enabled设置中,我们只支持达到最大值的条目。
if (dsf_enabled) {
for (i = 0; i < dsf_len_actual; i++) {
if (top_rated[i]) {
/* if top rated for any i, will be favored */
u8 was_favored_already = top_rated[i]->favored;
top_rated[i]->favored = 1;
/* increments counts only if not also favored for another i */
if (!was_favored_already){
queued_favored++;
if (!top_rated[i]->was_fuzzed) pending_favored++;
}
}
}
}
配置共享内存和virgin_bits。这是在startcup时调用的。
setup_shm(void):
在特定领域模糊的情况下,将DSF 映射在常规位图之后。
afl:
//共享内存函数由shmget、shmat、shmdt、shmctl四个函数组成
//shmget---分配一个system V 共享内存段,容量65536
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
fuzzfactory:
shm_id = shmget(IPC_PRIVATE, MAP_SIZE + (DSF_LEN * sizeof(u32)), IPC_CREAT | IPC_EXCL | 0600);
// setup dsf map if needed
if (dsf_enabled || save_everything) dsf_map = (u32 *) (trace_bits + MAP_SIZE);
将累积的DSF地图设置为提供的初始值
EXP_ST void setup_dsf_cumulated() {
for (int i = 0; i < dsf_count; i++) {
int dsf_start = dsf_configs[i].start;
int dsf_end = dsf_configs[i].end;
u32 dsf_initial = dsf_configs[i].initial;
for (int j = dsf_start; j < dsf_end; j++) {
dsf_cumulated[j] = dsf_initial;
}
}
// memset(dsf_cumulated, 0, dsf_len_actual * sizeof(u32));
}
init_forkserver(char** argv):
/* If we have a four-byte "hello" message from the server, we're all set.
Otherwise, try to figure out what went wrong. */
//如果读到了4bytes的状态信息,说明一切准备就绪,return
if (rlen == 4) {
OKF("All right - fork server is up.");
if (dsf_enabled || save_everything) {
/* If doing domain-specific fuzzing, this value is actually meaningful. */
dsf_count = status;
if (dsf_count < 0 || dsf_count > DSF_MAX) FATAL("%d is too many DSF maps! Max is %d", dsf_count, DSF_MAX);
if (dsf_count == 0) {
SAYF("No domain-specific front-ends to receive.\n");
return;
}
SAYF("Receiving %d domain-specific front-ends..\n", dsf_count);
rlen = read(fsrv_st_fd, dsf_configs, dsf_count * sizeof(dsf_config));
if (rlen != dsf_count * sizeof(dsf_config)) FATAL("Could not read DSF configs");
OKF("%d domain-specific front-end configs received", dsf_count);
for (int j = 0; j < dsf_count; j++) {
int start = dsf_configs[j].start;
int end = dsf_configs[j].end;
int reducer = dsf_configs[j].reducer;
int initial = dsf_configs[j].initial;
SAYF("DSF %d: Start=0x%06x, End=0x%06x, Size=%d, Reducer[%d]=%s, Initial=%d\n", j, start, end, end-start, reducer, dsf_reducer_names[reducer], initial);
dsf_len_actual = end;
}
SAYF("Total DSF map length = %d\n", dsf_len_actual);
}
return;
}
run_target(char** argv, u32 timeout):
执行目标应用程序,监测超时情况。返回状态 信息。被调用的程序将更新trace_bits[]。
if (dsf_enabled || save_everything) {
memset(dsf_map, 0, dsf_len_actual * sizeof(u32));
}
show_stats(void);
else {
q->exec_cksum = cksum;
/* setup the dsf cksum here. Assume it is not variable, or that
variability will be detected in the regular checking */
//在这里设置dsf的cksum。假设它是不可变的,或者可变性将在常规检查中被发现
if (dsf_enabled)
q->dsf_cksum = hash32(dsf_map, dsf_len_actual*sizeof(u32), HASH_CONST);
memcpy(first_trace, trace_bits, MAP_SIZE);
}
Linux cksum命令用于检查文件的CRC是否正确。确保文件从一个系统传输到另一个系统的过程中不被损坏。
perform_dry_run(char** argv)
// 正确填充dsf_cumulated。
if (dsf_enabled) has_dsf_changed();
if (dsf_enabled) {
DEBUG("======== Starting Keys ========\n");
for (int j = 0; j < dsf_count; j++) {
DEBUG("# DSF map %d\n", j);
dsf_config* dsf = &dsf_configs[j];
for (u32 k=dsf->start; k < dsf->end; k++){
// if there is a non-initial score at this index...
if (dsf_cumulated[k] != dsf->initial){
DEBUG("At key 0x%06x, val is %d (hex=0x%08x)\n", k, dsf_cumulated[k], dsf_cumulated[k]);
}
}
}
}
static void save_as_perf_input(void * mem, u32 len) {
num_dsf_inputs++;
if (! dsf_enabled && has_dsf_changed()) {
u8 *fn = alloc_printf("%s/perf_inputs/id_%06llu", out_dir, num_dsf_inputs);
s32 fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0) PFATAL("Unable to create '%s'", fn);
ck_write(fd, mem, len, fn);
close(fd);
ck_free(fn);
}
}
save_if_interesting(char** argv, void* mem, u32 len, u8 fault)
if (fault == crash_mode) {
//只有当地图中出现新的比特时才会保留,为将来的模糊处理添加到队列中,等等。DSF:如果有一个新的最大值,也要保留。
u8 dsf_changed = 0;
if (dsf_enabled) dsf_changed = has_dsf_changed();
if (save_everything) {
if (dsf_changed || has_dsf_bit()) save_as_perf_input(mem, len);
}
if (!(hnb = has_new_bits(virgin_bits)) && (!dsf_enabled || !dsf_changed)) {
if (crash_mode) total_crashes++;
return 0;
}
#ifndef SIMPLE_FILES
//如果有newbits则调用,下面这个拼接处路径fn
fn = alloc_printf("%s/queue/id:%06u,%s%s", out_dir, queued_paths,
describe_op(hnb), (dsf_enabled && dsf_changed) ? ",+dsf" : "" );
#endif /* ^!SIMPLE_FILES */
if (dsf_enabled)
queue_top->dsf_cksum = hash32(dsf_map, dsf_len_actual*sizeof(u32), HASH_CONST);
maybe_update_plot_file
if (save_everything) {
fprintf(plot_file,
"%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu\n",
get_cur_time() / 1000, queue_cycle - 1, current_entry, queued_paths,
pending_not_fuzzed, pending_favored, bitmap_cvg, unique_crashes,
unique_hangs, max_depth, eps, num_dsf_inputs); /* ignore errors */
}
trim_case//对于我们的test case进行修剪
static u32 clean_dsf[DSF_LEN];
/* Note that we dont keep track of crashes or hangs here; maybe TODO? */
if (dsf_enabled)
dsf_cksum = hash32(dsf_map, dsf_len_actual*sizeof(u32), HASH_CONST);
exec_cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
//DSF: 请确保特定领域的 细节也不要改变。
if ((exec_cksum == q->exec_cksum) &&
(!dsf_enabled || (dsf_cksum == q->dsf_cksum))) {
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);
/* 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);
if (dsf_enabled) memcpy(clean_dsf, dsf_map, dsf_len_actual*sizeof(u32));
}
if (dsf_enabled) memcpy(dsf_map, clean_dsf, dsf_len_actual*sizeof(u32));
fuzz_one(char** argv)
u32 orig_dsf_cumulated[DSF_LEN];
memcpy(orig_dsf_cumulated, dsf_cumulated, dsf_len_actual*sizeof(u32));
//如果设置了pending_favored
#else
if (pending_favored || (dsf_enabled && queued_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. */
/* in DSF mode, queue inputs remain favored even if
they were previously fuzzed */
if (((queue_cur->was_fuzzed && !dsf_enabled) || !queue_cur->favored) &&
UR(100) < SKIP_TO_NEW_PROB) return 1;
}
//如果eff_map[stage_cur>>3]为0的话
//EFF_APOS宏也起到了一个将stage_cur>>3的效果
if (!eff_map[EFF_APOS(stage_cur)]) {
u32 exec_cksum;
u32 dsf_cksum;
/* If in dumb mode or if the file is very short, just flag everything
without wasting time on checksums. */
if (!dumb_mode && len >= EFF_MIN_LEN){
exec_cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
if (dsf_enabled)
dsf_cksum = hash32(dsf_map, dsf_len_actual*sizeof(u32), HASH_CONST);
} else {
exec_cksum = ~queue_cur->exec_cksum;
if (dsf_enabled)
dsf_cksum = ~queue_cur->dsf_cksum;
}
if ((exec_cksum != queue_cur->exec_cksum) || (dsf_enabled && (dsf_cksum != queue_cur->dsf_cksum))) {
eff_map[EFF_APOS(stage_cur)] = 1;
eff_cnt++;
}
}
main()
case 'a':
SAYF("Saving everything with extra feedback. May be slower.\n");
save_everything = 1;
break;
case 'p':
SAYF("Domain specific fuzzing...\n");
dsf_enabled = 1;
break;
if (!forksrv_pid) init_forkserver(use_argv); // 我们提前这样做是为了得到dsf_len_actual
if (dsf_enabled || save_everything) setup_dsf_cumulated();
if (dsf_enabled)
top_rated= ck_alloc(dsf_len_actual * sizeof(struct queue_entry *));
else
top_rated = ck_alloc(MAP_SIZE * sizeof(struct queue_entry *));
save_everything, /* save all inputs with something in perf_map */